Remove everything prior to Shell 2 migration
This commit is contained in:
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.shell.core.ExitShellRequest;
|
||||
import org.springframework.shell.core.JLineShellComponent;
|
||||
import org.springframework.shell.core.Shell;
|
||||
import org.springframework.shell.support.logging.HandlerUtils;
|
||||
import org.springframework.util.StopWatch;
|
||||
|
||||
/**
|
||||
* Loads a {@link Shell} using Spring IoC container.
|
||||
*
|
||||
* @author Ben Alex (original Roo code)
|
||||
* @author Mark Pollack
|
||||
* @author David Winterfeldt
|
||||
*
|
||||
*/
|
||||
public class Bootstrap {
|
||||
|
||||
private final static String[] CONTEXT_PATH = { "classpath*:/META-INF/spring/spring-shell-plugin.xml" };
|
||||
|
||||
private CommandLine commandLine;
|
||||
|
||||
private GenericApplicationContext ctx;
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
ExitShellRequest exitShellRequest;
|
||||
try {
|
||||
Bootstrap bootstrap = new Bootstrap(args);
|
||||
exitShellRequest = bootstrap.run();
|
||||
}
|
||||
catch (RuntimeException t) {
|
||||
throw t;
|
||||
}
|
||||
finally {
|
||||
HandlerUtils.flushAllHandlers(Logger.getLogger(""));
|
||||
}
|
||||
|
||||
System.exit(exitShellRequest.getExitCode());
|
||||
}
|
||||
|
||||
public Bootstrap() {
|
||||
this(null, CONTEXT_PATH);
|
||||
}
|
||||
|
||||
public Bootstrap(String[] args) throws IOException {
|
||||
this(args, CONTEXT_PATH);
|
||||
}
|
||||
|
||||
public Bootstrap(String[] args, String[] contextPath) {
|
||||
try {
|
||||
commandLine = SimpleShellCommandLineOptions.parseCommandLine(args);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new ShellException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
ctx = new GenericApplicationContext();
|
||||
ctx.registerShutdownHook();
|
||||
configureApplicationContext(ctx);
|
||||
// built-in commands and converters
|
||||
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(ctx);
|
||||
if (commandLine.getDisableInternalCommands()) {
|
||||
scanner.scan("org.springframework.shell.converters", "org.springframework.shell.plugin.support");
|
||||
}
|
||||
else {
|
||||
scanner.scan("org.springframework.shell.commands", "org.springframework.shell.converters",
|
||||
"org.springframework.shell.plugin.support");
|
||||
}
|
||||
// user contributed commands
|
||||
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(ctx);
|
||||
reader.loadBeanDefinitions(contextPath);
|
||||
ctx.refresh();
|
||||
}
|
||||
|
||||
public ApplicationContext getApplicationContext() {
|
||||
return ctx;
|
||||
}
|
||||
|
||||
private void configureApplicationContext(GenericApplicationContext annctx) {
|
||||
createAndRegisterBeanDefinition(annctx, org.springframework.shell.core.JLineShellComponent.class, "shell");
|
||||
annctx.getBeanFactory().registerSingleton("commandLine", commandLine);
|
||||
}
|
||||
|
||||
protected void createAndRegisterBeanDefinition(GenericApplicationContext annctx, Class<?> clazz, String name) {
|
||||
RootBeanDefinition rbd = new RootBeanDefinition();
|
||||
rbd.setBeanClass(clazz);
|
||||
DefaultListableBeanFactory bf = (DefaultListableBeanFactory) annctx.getBeanFactory();
|
||||
if (name != null) {
|
||||
bf.registerBeanDefinition(name, rbd);
|
||||
}
|
||||
else {
|
||||
bf.registerBeanDefinition(clazz.getSimpleName(), rbd);
|
||||
}
|
||||
}
|
||||
|
||||
public ExitShellRequest run() {
|
||||
StopWatch sw = new StopWatch("Spring Shell");
|
||||
sw.start();
|
||||
String[] commandsToExecuteAndThenQuit = commandLine.getShellCommandsToExecute();
|
||||
// The shell is used
|
||||
JLineShellComponent shell = ctx.getBean("shell", JLineShellComponent.class);
|
||||
ExitShellRequest exitShellRequest;
|
||||
|
||||
if (null != commandsToExecuteAndThenQuit) {
|
||||
boolean successful = false;
|
||||
exitShellRequest = ExitShellRequest.FATAL_EXIT;
|
||||
|
||||
for (String cmd : commandsToExecuteAndThenQuit) {
|
||||
successful = shell.executeCommand(cmd).isSuccess();
|
||||
if (!successful)
|
||||
break;
|
||||
}
|
||||
|
||||
// if all commands were successful, set the normal exit status
|
||||
if (successful) {
|
||||
exitShellRequest = ExitShellRequest.NORMAL_EXIT;
|
||||
}
|
||||
}
|
||||
else {
|
||||
shell.start();
|
||||
exitShellRequest = shell.getExitShellRequest();
|
||||
if (exitShellRequest == null) {
|
||||
// shouldn't really happen, but we'll fallback to this anyway
|
||||
exitShellRequest = ExitShellRequest.NORMAL_EXIT;
|
||||
}
|
||||
shell.waitForComplete();
|
||||
}
|
||||
|
||||
ctx.close();
|
||||
sw.stop();
|
||||
if (shell.isDevelopmentMode()) {
|
||||
System.out.println("Total execution time: " + sw.getLastTaskTimeMillis() + " ms");
|
||||
}
|
||||
return exitShellRequest;
|
||||
}
|
||||
|
||||
public JLineShellComponent getJLineShellComponent() {
|
||||
return ctx.getBean("shell", JLineShellComponent.class);
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell;
|
||||
|
||||
|
||||
/**
|
||||
* Encapsulates the list of argument passed to the shell.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
*/
|
||||
public class CommandLine {
|
||||
|
||||
private String[] args;
|
||||
private int historySize;
|
||||
private String[] shellCommandsToExecute;
|
||||
private boolean disableInternalCommands;
|
||||
|
||||
/**
|
||||
* Construct a new CommandLine
|
||||
* @param args an array of strings from main(String[] args)
|
||||
* @param historySize the size of this history buffer
|
||||
* @param shellCommandsToExecute semi-colon delimited list of commands for the shell to execute
|
||||
*/
|
||||
public CommandLine(String[] args, int historySize, String[] shellCommandsToExecute) {
|
||||
this(args,historySize,shellCommandsToExecute, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new CommandLine
|
||||
* @param args an array of strings from main(String[] args)
|
||||
* @param historySize the size of this history buffer
|
||||
* @param shellCommandsToExecute semi-colon delimited list of commands for the shell to execute
|
||||
* @param disableInternalCommands if true, do not load the built-in shell commands
|
||||
*/
|
||||
public CommandLine(String[] args, int historySize, String[] shellCommandsToExecute, boolean disableInternalCommands) {
|
||||
this.args = args;
|
||||
this.historySize = historySize;
|
||||
this.shellCommandsToExecute = shellCommandsToExecute;
|
||||
this.disableInternalCommands = disableInternalCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the command line arguments
|
||||
* @return the command line arguments
|
||||
*/
|
||||
public String[] getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the historySize
|
||||
*/
|
||||
public int getHistorySize() {
|
||||
return historySize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the shellCommandsToExecute
|
||||
*/
|
||||
public String[] getShellCommandsToExecute() {
|
||||
return shellCommandsToExecute;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the disableInternalCommands value
|
||||
*/
|
||||
public boolean getDisableInternalCommands() {
|
||||
return disableInternalCommands;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell;
|
||||
|
||||
|
||||
/**
|
||||
* Shell exception.
|
||||
*
|
||||
* @author David Wintefeldt
|
||||
*/
|
||||
public class ShellException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1123895874463364743L;
|
||||
|
||||
public ShellException() {}
|
||||
|
||||
public ShellException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ShellException(Throwable t) {
|
||||
super(t);
|
||||
}
|
||||
|
||||
public ShellException(String message, Throwable t) {
|
||||
super(message, t);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.springframework.shell.support.logging.HandlerUtils;
|
||||
|
||||
/**
|
||||
* Used to pass in command line options to customize the shell on launch
|
||||
*
|
||||
* @author vnagaraja
|
||||
*/
|
||||
public class SimpleShellCommandLineOptions {
|
||||
|
||||
private static final Logger LOGGER = HandlerUtils.getLogger(SimpleShellCommandLineOptions.class);
|
||||
public static final int DEFAULT_HISTORY_SIZE = 3000;
|
||||
String[] executeThenQuit = null;
|
||||
Map<String, String> extraSystemProperties = new HashMap<String, String>();
|
||||
int historySize = DEFAULT_HISTORY_SIZE;
|
||||
boolean disableCommands;
|
||||
|
||||
public static CommandLine parseCommandLine(String[] args)
|
||||
throws IOException {
|
||||
if (args == null) {
|
||||
args = new String[] {};
|
||||
}
|
||||
SimpleShellCommandLineOptions options = new SimpleShellCommandLineOptions();
|
||||
List<String> commands = new ArrayList<String>();
|
||||
int i = 0;
|
||||
while (i < args.length) {
|
||||
String arg = args[i++];
|
||||
if (arg.equals("--profiles")) {
|
||||
try {
|
||||
String profiles = args[i++];
|
||||
options.extraSystemProperties.put("spring.profiles.active", profiles);
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
LOGGER.warning("No value specified for --profiles option");
|
||||
}
|
||||
} else if (arg.equals("--cmdfile")) {
|
||||
try {
|
||||
File f = new File(args[i++]);
|
||||
commands.addAll(FileUtils.readLines(f));
|
||||
} catch (IOException e) {
|
||||
LOGGER.warning("Could not read lines from command file: " + e.getMessage());
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
LOGGER.warning("No value specified for --cmdfile option");
|
||||
}
|
||||
} else if (arg.equals("--histsize")) {
|
||||
try {
|
||||
String histSizeArg = args[i++];
|
||||
int histSize = Integer.parseInt(histSizeArg);
|
||||
if (histSize <= 0) {
|
||||
LOGGER.warning("histsize option must be > 0, using default value of " + DEFAULT_HISTORY_SIZE);
|
||||
} else {
|
||||
options.historySize = histSize;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
LOGGER.warning("Unable to parse histsize value to an integer ");
|
||||
} catch (ArrayIndexOutOfBoundsException ae) {
|
||||
LOGGER.warning("No value specified for --histsize option");
|
||||
}
|
||||
} else if (arg.equals("--disableInternalCommands")) {
|
||||
options.disableCommands = true;
|
||||
} else if (arg.equals("--help")) {
|
||||
printUsage();
|
||||
System.exit(0);
|
||||
} else {
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (; i < args.length; i++) {
|
||||
if (sb.length() > 0) {
|
||||
sb.append(" ");
|
||||
}
|
||||
sb.append(args[i]);
|
||||
}
|
||||
|
||||
if (sb.length() > 0) {
|
||||
String[] cmdLineCommands = sb.toString().split(";");
|
||||
for (String s : cmdLineCommands) {
|
||||
// add any command line commands after the commands loaded from the file
|
||||
commands.add(s.trim());
|
||||
}
|
||||
}
|
||||
|
||||
if (commands.size() > 0) {
|
||||
options.executeThenQuit = commands.toArray(new String[commands.size()]);
|
||||
}
|
||||
|
||||
for (Map.Entry<String, String> entry : options.extraSystemProperties.entrySet()) {
|
||||
System.setProperty(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
return new CommandLine(args, options.historySize, options.executeThenQuit, options.disableCommands);
|
||||
}
|
||||
|
||||
private static void printUsage() {
|
||||
System.out.println("Usage: --help --histsize [size] --cmdfile [file name] --profiles [comma-separated list of profile names]");
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package org.springframework.shell;
|
||||
|
||||
/**
|
||||
* To be implemented by command result objects that can adapt to the terminal size when they are being rendered.
|
||||
*
|
||||
* <p>An object which does not implement this interface will simply be rendered by invoking its {@link #toString()}
|
||||
* method.</p>
|
||||
*
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
public interface TerminalSizeAware {
|
||||
|
||||
CharSequence render(int terminalWidth);
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.commands;
|
||||
|
||||
import static org.fusesource.jansi.Ansi.ansi;
|
||||
|
||||
import org.fusesource.jansi.AnsiConsole;
|
||||
import org.springframework.shell.core.CommandMarker;
|
||||
import org.springframework.shell.core.annotation.CliCommand;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Commands related to the manipulation of the jline console.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class ConsoleCommands implements CommandMarker {
|
||||
|
||||
@CliCommand(value = { "cls", "clear" }, help = "Clears the console")
|
||||
public void clear() {
|
||||
AnsiConsole.out().print(ansi().eraseScreen().cursor(0, 0));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.commands;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.springframework.shell.core.CommandMarker;
|
||||
import org.springframework.shell.core.annotation.CliCommand;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Commands related to the dates
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class DateCommands implements CommandMarker {
|
||||
|
||||
@CliCommand(value = { "date" }, help = "Displays the local date and time")
|
||||
public String date() {
|
||||
return DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, Locale.getDefault()).format(new Date());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.commands;
|
||||
|
||||
import org.springframework.shell.core.CommandMarker;
|
||||
import org.springframework.shell.core.ExitShellRequest;
|
||||
import org.springframework.shell.core.annotation.CliCommand;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Commands related to exiting the shell
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class ExitCommands implements CommandMarker {
|
||||
|
||||
@CliCommand(value={"exit", "quit"}, help="Exits the shell")
|
||||
public ExitShellRequest quit() {
|
||||
return ExitShellRequest.NORMAL_EXIT;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.commands;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.shell.core.CommandMarker;
|
||||
import org.springframework.shell.core.JLineShellComponent;
|
||||
import org.springframework.shell.core.SimpleParser;
|
||||
import org.springframework.shell.core.annotation.CliCommand;
|
||||
import org.springframework.shell.core.annotation.CliOption;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Provides a listing of commands known to the shell.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Mark Pollack
|
||||
* @author Jarred Li
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class HelpCommands implements CommandMarker, ApplicationContextAware {
|
||||
|
||||
private ApplicationContext ctx;
|
||||
|
||||
@CliCommand(value = "help", help = "List all commands usage")
|
||||
public void obtainHelp(
|
||||
@CliOption(key = { "", "command" }, optionContext = "disable-string-converter availableCommands", help = "Command name to provide help for")
|
||||
String buffer) {
|
||||
JLineShellComponent shell = ctx.getBean("shell", JLineShellComponent.class);
|
||||
SimpleParser parser = shell.getSimpleParser();
|
||||
parser.obtainHelp(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.ctx = applicationContext;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.commands;
|
||||
|
||||
import org.springframework.shell.core.CommandMarker;
|
||||
import org.springframework.shell.core.annotation.CliCommand;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Commands relating to inline comments
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class InlineCommentCommands implements CommandMarker {
|
||||
|
||||
@CliCommand(value = { "//", ";" }, help = "Inline comment markers (start of line only)")
|
||||
public void inlineComment() {}
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.commands;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.springframework.shell.core.CommandMarker;
|
||||
import org.springframework.shell.core.annotation.CliCommand;
|
||||
import org.springframework.shell.core.annotation.CliOption;
|
||||
import org.springframework.shell.support.logging.HandlerUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Command type to allow execution of native OS commands from the Spring Shell.
|
||||
*
|
||||
* @author Stefan Schmidt
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@Component
|
||||
public class OsCommands implements CommandMarker {
|
||||
|
||||
private static final Logger LOGGER = HandlerUtils
|
||||
.getLogger(OsCommands.class);
|
||||
|
||||
private OsOperations osOperations = new OsOperationsImpl();
|
||||
|
||||
@CliCommand(value = "!", help = "Allows execution of operating system (OS) commands")
|
||||
public void command(
|
||||
@CliOption(key = { "", "command" }, mandatory = false, specifiedDefaultValue = "", unspecifiedDefaultValue = "", help = "The command to execute") final String command) {
|
||||
|
||||
System.out.println("command is:" + command);
|
||||
if (command != null && command.length() > 0) {
|
||||
try {
|
||||
osOperations.executeCommand(command);
|
||||
}
|
||||
catch (final IOException e) {
|
||||
LOGGER.severe("Unable to execute command " + command + " ["
|
||||
+ e.getMessage() + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.commands;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Operations type to allow execution of native OS commands from the Spring Roo
|
||||
* shell.
|
||||
*
|
||||
* @author Stefan Schmidt
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public interface OsOperations {
|
||||
|
||||
/**
|
||||
* Attempts the execution of a commands and delegates the output to the
|
||||
* standard logger.
|
||||
*
|
||||
* @param command the command to execute
|
||||
* @throws IOException if an error occurs
|
||||
*/
|
||||
void executeCommand(String command) throws IOException;
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.commands;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.shell.support.logging.HandlerUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Implementation of {@link OsOperations} interface.
|
||||
*
|
||||
* @author Stefan Schmidt
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@Component
|
||||
public class OsOperationsImpl implements OsOperations {
|
||||
private static final Logger LOGGER = HandlerUtils.getLogger(OsOperationsImpl.class);
|
||||
|
||||
public void executeCommand(final String command) throws IOException {
|
||||
final File root = new File(".");
|
||||
final Process p = Runtime.getRuntime().exec(command, null, root);
|
||||
Reader input = new InputStreamReader(p.getInputStream());
|
||||
Reader errors = new InputStreamReader(p.getErrorStream());
|
||||
|
||||
for (String line : IOUtils.readLines(input)) {
|
||||
if (line.startsWith("[ERROR]")) {
|
||||
LOGGER.severe(line);
|
||||
}
|
||||
else if (line.startsWith("[WARNING]")) {
|
||||
LOGGER.warning(line);
|
||||
}
|
||||
else {
|
||||
LOGGER.info(line);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (String line : IOUtils.readLines(errors)) {
|
||||
if (line.startsWith("[ERROR]")) {
|
||||
LOGGER.severe(line);
|
||||
}
|
||||
else if (line.startsWith("[WARNING]")) {
|
||||
LOGGER.warning(line);
|
||||
}
|
||||
else {
|
||||
LOGGER.info(line);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
p.getOutputStream().close();
|
||||
|
||||
|
||||
try {
|
||||
if (p.waitFor() != 0) {
|
||||
LOGGER.warning("The command '" + command + "' did not complete successfully");
|
||||
}
|
||||
} catch (final InterruptedException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.commands;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.shell.core.CommandMarker;
|
||||
import org.springframework.shell.core.JLineShellComponent;
|
||||
import org.springframework.shell.core.annotation.CliCommand;
|
||||
import org.springframework.shell.core.annotation.CliOption;
|
||||
import org.springframework.shell.support.logging.HandlerUtils;
|
||||
import org.springframework.shell.support.util.IOUtils;
|
||||
import org.springframework.shell.support.util.MathUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@Component
|
||||
public class ScriptCommands implements CommandMarker {
|
||||
protected final Logger logger = HandlerUtils.getLogger(getClass());
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Autowired
|
||||
private JLineShellComponent shell;
|
||||
@CliCommand(value = { "script" }, help = "Parses the specified resource file and executes its commands")
|
||||
public void script(
|
||||
@CliOption(key = { "", "file" }, help = "The file to locate and execute", mandatory = true) final File script,
|
||||
@CliOption(key = "lineNumbers", mandatory = false, specifiedDefaultValue = "true", unspecifiedDefaultValue = "false", help = "Display line numbers when executing the script") final boolean lineNumbers) {
|
||||
|
||||
Assert.notNull(script, "Script file to parse is required");
|
||||
double startedNanoseconds = System.nanoTime();
|
||||
final InputStream inputStream = openScript(script);
|
||||
|
||||
BufferedReader in = null;
|
||||
try {
|
||||
in = new BufferedReader(new InputStreamReader(inputStream));
|
||||
String line;
|
||||
int i = 0;
|
||||
while ((line = in.readLine()) != null) {
|
||||
i++;
|
||||
if (lineNumbers) {
|
||||
logger.fine("Line " + i + ": " + line);
|
||||
} else {
|
||||
logger.fine(line);
|
||||
}
|
||||
if (!"".equals(line.trim())) {
|
||||
boolean success = shell.executeScriptLine(line);
|
||||
if (success && ((line.trim().startsWith("q") || line.trim().startsWith("ex")))) {
|
||||
break;
|
||||
} else if (!success) {
|
||||
// Abort script processing, given something went wrong
|
||||
throw new IllegalStateException("Script execution aborted");
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(inputStream, in);
|
||||
double executionDurationInSeconds = (System.nanoTime() - startedNanoseconds) / 1000000000D;
|
||||
logger.fine("Script required " + MathUtils.round(executionDurationInSeconds, 3) + " seconds to execute");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the given script for reading
|
||||
*
|
||||
* @param script the script to read (required)
|
||||
* @return a non-<code>null</code> input stream
|
||||
*/
|
||||
private InputStream openScript(final File script) {
|
||||
try {
|
||||
return new BufferedInputStream(new FileInputStream(script));
|
||||
} catch (final FileNotFoundException fnfe) {
|
||||
// Try to find the script via the classloader
|
||||
final Collection<URL> urls = findResources(script.getName());
|
||||
|
||||
// Handle search failure
|
||||
Assert.notNull(urls, "Unexpected error looking for '" + script.getName() + "'");
|
||||
|
||||
// Handle the search being OK but the file simply not being present
|
||||
Assert.notEmpty(urls, "Script '" + script + "' not found on disk or in classpath");
|
||||
Assert.isTrue(urls.size() == 1, "More than one '" + script + "' was found in the classpath; unable to continue");
|
||||
try {
|
||||
return urls.iterator().next().openStream();
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Collection<URL> findResources(final String path) {
|
||||
try {
|
||||
Resource[] resources = applicationContext.getResources(path);
|
||||
Collection<URL> list = new ArrayList<URL>(resources.length);
|
||||
for (Resource resource : resources) {
|
||||
list.add(resource.getURL());
|
||||
}
|
||||
return list;
|
||||
} catch (IOException ex) {
|
||||
logger.fine("Cannot find path " + path);
|
||||
// return Collections.emptyList();
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.commands;
|
||||
|
||||
import static org.springframework.shell.support.util.OsUtils.LINE_SEPARATOR;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.springframework.shell.core.CommandMarker;
|
||||
import org.springframework.shell.core.annotation.CliCommand;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Commands related to system properties
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class SystemPropertyCommands implements CommandMarker {
|
||||
|
||||
@CliCommand(value = { "system properties" }, help = "Shows the shell's properties")
|
||||
public String props() {
|
||||
final Set<String> data = new TreeSet<String>(); // For repeatability
|
||||
for (final Entry<Object, Object> entry : System.getProperties().entrySet()) {
|
||||
data.add(entry.getKey() + " = " + entry.getValue());
|
||||
}
|
||||
|
||||
return StringUtils.collectionToDelimitedString(data, LINE_SEPARATOR) + LINE_SEPARATOR;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package org.springframework.shell.commands;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.shell.core.CommandMarker;
|
||||
import org.springframework.shell.core.annotation.CliCommand;
|
||||
import org.springframework.shell.plugin.BannerProvider;
|
||||
import org.springframework.shell.plugin.PluginUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Essential built-in shell commands.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
* @author Erwin Vervaet
|
||||
*/
|
||||
@Component
|
||||
public class VersionCommands implements CommandMarker, ApplicationContextAware {
|
||||
|
||||
private ApplicationContext ctx;
|
||||
|
||||
@CliCommand(value = { "version" }, help = "Displays shell version")
|
||||
public String version() {
|
||||
return PluginUtils.getHighestPriorityProvider(ctx, BannerProvider.class).getVersion();
|
||||
}
|
||||
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
this.ctx = applicationContext;
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
package org.springframework.shell.converters;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.shell.core.Completion;
|
||||
import org.springframework.shell.core.Converter;
|
||||
import org.springframework.shell.core.MethodTarget;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* A converter that knows how to use other converters to create arrays of supported types.
|
||||
*
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
@Component
|
||||
public class ArrayConverter implements Converter<Object[]>{
|
||||
|
||||
private Set<Converter<?>> converters;
|
||||
|
||||
@Autowired
|
||||
public void setConverters(Set<Converter<?>> converters) {
|
||||
this.converters = converters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> type, String optionContext) {
|
||||
return findComponentConverter(type, optionContext) != null && !optionContext.contains("disable-array-converter");
|
||||
|
||||
}
|
||||
|
||||
private Converter<?> findComponentConverter(Class<?> targetType, String optionContext) {
|
||||
if (!targetType.isArray()) {
|
||||
return null;
|
||||
}
|
||||
Class<?> componentType = targetType.getComponentType();
|
||||
for (Converter<?> converter : converters) {
|
||||
if (converter.supports(componentType, optionContext)) {
|
||||
return converter;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] convertFromText(String value, Class<?> targetType, String optionContext) {
|
||||
Class<?> componentType = targetType.getComponentType();
|
||||
|
||||
String splittingRegex = inferSplittingRegex(targetType, optionContext);
|
||||
String[] splits = value.split(splittingRegex);
|
||||
Object[] result = (Object[]) Array.newInstance(componentType, splits.length);
|
||||
Converter<?> converter = findComponentConverter(targetType, optionContext);
|
||||
|
||||
for (int i = 0; i < splits.length; i++) {
|
||||
result[i] = converter.convertFromText(splits[i], componentType, optionContext);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a regex used to split the string representation of items.
|
||||
* <p>The default delimiter is a comma, unless we're dealing with Files, in which case
|
||||
* {@link java.io.File.pathSeparator} is used.</p>
|
||||
* <p>Delimiters can be protected by an escape character, which is '\' by default.</p>
|
||||
* <p>Command methods may override bot the delimiter and the escape through the {@code splittingRegex} option context
|
||||
* string.</p>
|
||||
*/
|
||||
private String inferSplittingRegex(Class<?> targetType, String optionContext) {
|
||||
String regex = extract(optionContext, "splittingRegex");
|
||||
if (regex == null) {
|
||||
// Default for files is to use system separator with no way to escape
|
||||
if (File[].class.isAssignableFrom(targetType)) {
|
||||
regex = File.pathSeparator;
|
||||
} else {
|
||||
String delimiter = ",";
|
||||
String escape = "\\";
|
||||
regex = String.format("(?<!\\Q%s\\E)\\Q%s\\E", escape, delimiter);
|
||||
}
|
||||
}
|
||||
return regex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAllPossibleValues(List<Completion> completions, Class<?> targetType, String existingData, String optionContext, MethodTarget target) {
|
||||
Class<?> componentType = targetType.getComponentType();
|
||||
|
||||
String splittingRegex = inferSplittingRegex(targetType, optionContext);
|
||||
String[] splits = existingData.split(splittingRegex);
|
||||
Converter<?> converter = findComponentConverter(targetType, optionContext);
|
||||
|
||||
// Search for completions with the last part only, prefixing the results by everything that was
|
||||
// before the delimiter
|
||||
String last = splits[splits.length - 1];
|
||||
int end = existingData.lastIndexOf(last);
|
||||
String prefix = existingData.substring(0, end);
|
||||
List<Completion> ours = new ArrayList<Completion>();
|
||||
|
||||
// Passing our method target below, as we can't do better. Obviously, method sig will be wrong
|
||||
boolean result = converter.getAllPossibleValues(ours, componentType, last, optionContext, target);
|
||||
for (Completion completion : ours) {
|
||||
completions.add(new Completion(prefix + completion.getValue(), completion.getValue(), null, 0));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private String extract(String optionContext, String key) {
|
||||
String[] splits = optionContext.split(" ");
|
||||
String prefix = key + "=";
|
||||
for (String split : splits) {
|
||||
if (split.startsWith(prefix)) {
|
||||
return split.substring(prefix.length());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.converters;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.shell.core.Completion;
|
||||
import org.springframework.shell.core.Converter;
|
||||
import org.springframework.shell.core.JLineShellComponent;
|
||||
import org.springframework.shell.core.MethodTarget;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Available commands converter.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Eric Bottard
|
||||
* @since 1.0
|
||||
*/
|
||||
@Component
|
||||
public class AvailableCommandsConverter implements Converter<String> {
|
||||
|
||||
@Autowired
|
||||
private JLineShellComponent shell;
|
||||
|
||||
@Override
|
||||
public String convertFromText(final String text, final Class<?> requiredType, final String optionContext) {
|
||||
return text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(final Class<?> requiredType, final String optionContext) {
|
||||
return String.class.isAssignableFrom(requiredType) && optionContext.contains("availableCommands");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAllPossibleValues(final List<Completion> completions, final Class<?> requiredType,
|
||||
final String existingData, final String optionContext, final MethodTarget target) {
|
||||
|
||||
for (String s : shell.getSimpleParser().getEveryCommand()) {
|
||||
completions.add(new Completion(s));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.converters;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.shell.core.Completion;
|
||||
import org.springframework.shell.core.Converter;
|
||||
import org.springframework.shell.core.MethodTarget;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* {@link Converter} for {@link BigDecimal}.
|
||||
*
|
||||
* @author Stefan Schmidt
|
||||
* @since 1.0
|
||||
*/
|
||||
@Component
|
||||
public class BigDecimalConverter implements Converter<BigDecimal> {
|
||||
|
||||
@Override
|
||||
public BigDecimal convertFromText(final String value, final Class<?> requiredType, final String optionContext) {
|
||||
return new BigDecimal(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAllPossibleValues(final List<Completion> completions, final Class<?> requiredType,
|
||||
final String existingData, final String optionContext, final MethodTarget target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(final Class<?> requiredType, final String optionContext) {
|
||||
return BigDecimal.class.isAssignableFrom(requiredType);
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.converters;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.shell.core.Completion;
|
||||
import org.springframework.shell.core.Converter;
|
||||
import org.springframework.shell.core.MethodTarget;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* {@link Converter} for {@link BigInteger}.
|
||||
*
|
||||
* @author Stefan Schmidt
|
||||
* @since 1.0
|
||||
*/
|
||||
@Component
|
||||
public class BigIntegerConverter implements Converter<BigInteger> {
|
||||
|
||||
@Override
|
||||
public BigInteger convertFromText(final String value, final Class<?> requiredType, final String optionContext) {
|
||||
return new BigInteger(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAllPossibleValues(final List<Completion> completions, final Class<?> requiredType,
|
||||
final String existingData, final String optionContext, final MethodTarget target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(final Class<?> requiredType, final String optionContext) {
|
||||
return BigInteger.class.isAssignableFrom(requiredType);
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.converters;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.shell.core.Completion;
|
||||
import org.springframework.shell.core.Converter;
|
||||
import org.springframework.shell.core.MethodTarget;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* {@link Converter} for {@link Boolean}.
|
||||
*
|
||||
* @author Stefan Schmidt
|
||||
* @since 1.0
|
||||
*/
|
||||
@Component
|
||||
public class BooleanConverter implements Converter<Boolean> {
|
||||
|
||||
@Override
|
||||
public Boolean convertFromText(final String value, final Class<?> requiredType, final String optionContext) {
|
||||
if ("true".equalsIgnoreCase(value) || "1".equals(value) || "yes".equalsIgnoreCase(value)) {
|
||||
return true;
|
||||
}
|
||||
else if ("false".equalsIgnoreCase(value) || "0".equals(value) || "no".equalsIgnoreCase(value)) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Cannot convert " + value + " to type Boolean.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAllPossibleValues(final List<Completion> completions, final Class<?> requiredType,
|
||||
final String existingData, final String optionContext, final MethodTarget target) {
|
||||
completions.add(new Completion("true"));
|
||||
completions.add(new Completion("false"));
|
||||
completions.add(new Completion("yes"));
|
||||
completions.add(new Completion("no"));
|
||||
completions.add(new Completion("1"));
|
||||
completions.add(new Completion("0"));
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(final Class<?> requiredType, final String optionContext) {
|
||||
return Boolean.class.isAssignableFrom(requiredType) || boolean.class.isAssignableFrom(requiredType);
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.converters;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.shell.core.Completion;
|
||||
import org.springframework.shell.core.Converter;
|
||||
import org.springframework.shell.core.MethodTarget;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* {@link Converter} for {@link Character}.
|
||||
*
|
||||
* @author Stefan Schmidt
|
||||
* @since 1.0
|
||||
*/
|
||||
@Component
|
||||
public class CharacterConverter implements Converter<Character> {
|
||||
|
||||
@Override
|
||||
public Character convertFromText(final String value, final Class<?> requiredType, final String optionContext) {
|
||||
return value.charAt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAllPossibleValues(final List<Completion> completions, final Class<?> requiredType,
|
||||
final String existingData, final String optionContext, final MethodTarget target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(final Class<?> requiredType, final String optionContext) {
|
||||
return Character.class.isAssignableFrom(requiredType) || char.class.isAssignableFrom(requiredType);
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.converters;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.springframework.shell.core.Completion;
|
||||
import org.springframework.shell.core.Converter;
|
||||
import org.springframework.shell.core.MethodTarget;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* {@link Converter} for {@link Date}.
|
||||
*
|
||||
* @author Stefan Schmidt
|
||||
* @since 1.0
|
||||
*/
|
||||
@Component
|
||||
public class DateConverter implements Converter<Date> {
|
||||
|
||||
// Fields
|
||||
private final DateFormat dateFormat;
|
||||
|
||||
public DateConverter() {
|
||||
this.dateFormat = DateFormat.getDateInstance(DateFormat.DEFAULT, Locale.getDefault());
|
||||
}
|
||||
|
||||
public DateConverter(final DateFormat dateFormat) {
|
||||
this.dateFormat = dateFormat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date convertFromText(final String value, final Class<?> requiredType, final String optionContext) {
|
||||
try {
|
||||
return dateFormat.parse(value);
|
||||
}
|
||||
catch (ParseException e) {
|
||||
throw new IllegalArgumentException("Could not parse date: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAllPossibleValues(final List<Completion> completions, final Class<?> requiredType,
|
||||
final String existingData, final String optionContext, final MethodTarget target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(final Class<?> requiredType, final String optionContext) {
|
||||
return Date.class.isAssignableFrom(requiredType);
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.converters;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.shell.core.Completion;
|
||||
import org.springframework.shell.core.Converter;
|
||||
import org.springframework.shell.core.MethodTarget;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* {@link Converter} for {@link Double}.
|
||||
*
|
||||
* @author Stefan Schmidt
|
||||
* @since 1.0
|
||||
*/
|
||||
@Component
|
||||
public class DoubleConverter implements Converter<Double> {
|
||||
|
||||
@Override
|
||||
public Double convertFromText(final String value, final Class<?> requiredType, final String optionContext) {
|
||||
return new Double(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAllPossibleValues(final List<Completion> completions, final Class<?> requiredType,
|
||||
final String existingData, final String optionContext, final MethodTarget target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(final Class<?> requiredType, final String optionContext) {
|
||||
return Double.class.isAssignableFrom(requiredType) || double.class.isAssignableFrom(requiredType);
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.converters;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.shell.core.Completion;
|
||||
import org.springframework.shell.core.Converter;
|
||||
import org.springframework.shell.core.MethodTarget;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* {@link Converter} for {@link Enum}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Alan Stewart
|
||||
* @since 1.0
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
@Component
|
||||
public class EnumConverter implements Converter<Enum<?>> {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Enum<?> convertFromText(final String value, final Class<?> requiredType, final String optionContext) {
|
||||
if (!Enum.class.isAssignableFrom(requiredType)) {
|
||||
return null;
|
||||
}
|
||||
Class<Enum> enumClass = (Class<Enum>) requiredType;
|
||||
return Enum.valueOf(enumClass, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean getAllPossibleValues(final List<Completion> completions, final Class<?> requiredType,
|
||||
final String existingData, final String optionContext, final MethodTarget target) {
|
||||
if (!Enum.class.isAssignableFrom(requiredType)) {
|
||||
return false;
|
||||
}
|
||||
Class<Enum> enumClass = (Class<Enum>) requiredType;
|
||||
for (Enum<?> enumValue : enumClass.getEnumConstants()) {
|
||||
String candidate = enumValue.name();
|
||||
if ("".equals(existingData) || candidate.startsWith(existingData) || existingData.startsWith(candidate)
|
||||
|| candidate.toUpperCase().startsWith(existingData.toUpperCase())
|
||||
|| existingData.toUpperCase().startsWith(candidate.toUpperCase())) {
|
||||
completions.add(new Completion(candidate));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(final Class<?> requiredType, final String optionContext) {
|
||||
return Enum.class.isAssignableFrom(requiredType);
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.converters;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.shell.core.Completion;
|
||||
import org.springframework.shell.core.Converter;
|
||||
import org.springframework.shell.core.MethodTarget;
|
||||
import org.springframework.shell.support.util.FileUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link Converter} for {@link File}.
|
||||
*
|
||||
* @author Stefan Schmidt
|
||||
* @author Roman Kuzmik
|
||||
* @author Ben Alex
|
||||
* @since 1.0
|
||||
*/
|
||||
public abstract class FileConverter implements Converter<File> {
|
||||
|
||||
private static final String HOME_DIRECTORY_SYMBOL = "~";
|
||||
// Constants
|
||||
private static final String home = System.getProperty("user.home");
|
||||
|
||||
// Fields
|
||||
|
||||
/**
|
||||
* @return the "current working directory" this {@link FileConverter} should use if the user fails to provide
|
||||
* an explicit directory in their input (required)
|
||||
*/
|
||||
protected abstract File getWorkingDirectory();
|
||||
|
||||
public File convertFromText(final String value, final Class<?> requiredType, final String optionContext) {
|
||||
return new File(convertUserInputIntoAFullyQualifiedPath(value));
|
||||
}
|
||||
|
||||
public boolean getAllPossibleValues(final List<Completion> completions, final Class<?> requiredType, final String originalUserInput, final String optionContext, final MethodTarget target) {
|
||||
String adjustedUserInput = convertUserInputIntoAFullyQualifiedPath(originalUserInput);
|
||||
|
||||
String directoryData = adjustedUserInput.substring(0, adjustedUserInput.lastIndexOf(File.separator) + 1);
|
||||
adjustedUserInput = adjustedUserInput.substring(adjustedUserInput.lastIndexOf(File.separator) + 1);
|
||||
|
||||
populate(completions, adjustedUserInput, originalUserInput, directoryData);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void populate(final List<Completion> completions, final String adjustedUserInput, final String originalUserInput, final String directoryData) {
|
||||
File directory = new File(directoryData);
|
||||
|
||||
if (!directory.isDirectory()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (File file : directory.listFiles()) {
|
||||
if (adjustedUserInput == null || adjustedUserInput.length() == 0 ||
|
||||
file.getName().startsWith(adjustedUserInput)) {
|
||||
|
||||
String completion = "";
|
||||
if (directoryData.length() > 0)
|
||||
completion += directoryData;
|
||||
completion += file.getName();
|
||||
|
||||
completion = convertCompletionBackIntoUserInputStyle(originalUserInput, completion);
|
||||
|
||||
if (file.isDirectory()) {
|
||||
completions.add(new Completion(completion + File.separator));
|
||||
} else {
|
||||
completions.add(new Completion(completion));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean supports(final Class<?> requiredType, final String optionContext) {
|
||||
return File.class.isAssignableFrom(requiredType);
|
||||
}
|
||||
|
||||
private String convertCompletionBackIntoUserInputStyle(final String originalUserInput, final String completion) {
|
||||
if (FileUtils.denotesAbsolutePath(originalUserInput)) {
|
||||
// Input was originally as a fully-qualified path, so we just keep the completion in that form
|
||||
return completion;
|
||||
}
|
||||
if (originalUserInput.startsWith(HOME_DIRECTORY_SYMBOL)) {
|
||||
// Input originally started with this symbol, so replace the user's home directory with it again
|
||||
Assert.notNull(home, "Home directory could not be determined from system properties");
|
||||
return HOME_DIRECTORY_SYMBOL + completion.substring(home.length());
|
||||
}
|
||||
// The path was working directory specific, so strip the working directory given the user never typed it
|
||||
return completion.substring(getWorkingDirectoryAsString().length());
|
||||
}
|
||||
|
||||
/**
|
||||
* If the user input starts with a tilde character (~), replace the tilde character with the
|
||||
* user's home directory. If the user input does not start with a tilde, simply return the original
|
||||
* user input without any changes if the input specifies an absolute path, or return an absolute path
|
||||
* based on the working directory if the input specifies a relative path.
|
||||
*
|
||||
* @param userInput the user input, which may commence with a tilde (required)
|
||||
* @return a string that is guaranteed to no longer contain a tilde as the first character (never null)
|
||||
*/
|
||||
private String convertUserInputIntoAFullyQualifiedPath(final String userInput) {
|
||||
if (FileUtils.denotesAbsolutePath(userInput)) {
|
||||
// Input is already in a fully-qualified path form
|
||||
return userInput;
|
||||
}
|
||||
if (userInput.startsWith(HOME_DIRECTORY_SYMBOL)) {
|
||||
// Replace this symbol with the user's actual home directory
|
||||
Assert.notNull(home, "Home directory could not be determined from system properties");
|
||||
if (userInput.length() > 1) {
|
||||
return home + userInput.substring(1);
|
||||
}
|
||||
}
|
||||
// The path is working directory specific, so prepend the working directory
|
||||
String fullPath = getWorkingDirectoryAsString() + userInput;
|
||||
return fullPath;
|
||||
}
|
||||
|
||||
private String getWorkingDirectoryAsString() {
|
||||
try {
|
||||
return getWorkingDirectory().getCanonicalPath() + File.separator;
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.converters;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.shell.core.Completion;
|
||||
import org.springframework.shell.core.Converter;
|
||||
import org.springframework.shell.core.MethodTarget;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* {@link Converter} for {@link Float}.
|
||||
*
|
||||
* @author Stefan Schmidt
|
||||
* @since 1.0
|
||||
*/
|
||||
@Component
|
||||
public class FloatConverter implements Converter<Float> {
|
||||
|
||||
@Override
|
||||
public Float convertFromText(final String value, final Class<?> requiredType, final String optionContext) {
|
||||
return new Float(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAllPossibleValues(final List<Completion> completions, final Class<?> requiredType,
|
||||
final String existingData, final String optionContext, final MethodTarget target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(final Class<?> requiredType, final String optionContext) {
|
||||
return Float.class.isAssignableFrom(requiredType) || float.class.isAssignableFrom(requiredType);
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package org.springframework.shell.converters;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.shell.core.Completion;
|
||||
import org.springframework.shell.core.Converter;
|
||||
import org.springframework.shell.core.MethodTarget;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* {@link Converter} for {@link Integer}.
|
||||
*
|
||||
* @author Stefan Schmidt
|
||||
* @since 1.0
|
||||
*/
|
||||
@Component
|
||||
public class IntegerConverter implements Converter<Integer> {
|
||||
|
||||
@Override
|
||||
public Integer convertFromText(final String value, final Class<?> requiredType, final String optionContext) {
|
||||
return new Integer(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAllPossibleValues(final List<Completion> completions, final Class<?> requiredType,
|
||||
final String existingData, final String optionContext, final MethodTarget target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(final Class<?> requiredType, final String optionContext) {
|
||||
return Integer.class.isAssignableFrom(requiredType) || int.class.isAssignableFrom(requiredType);
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.converters;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.springframework.shell.core.Completion;
|
||||
import org.springframework.shell.core.Converter;
|
||||
import org.springframework.shell.core.MethodTarget;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* {@link Converter} for {@link Locale}. Supports locales with ISO-639 (ie 'en') or a combination of ISO-639 and
|
||||
* ISO-3166 (ie 'en_AU').
|
||||
*
|
||||
* @author Stefan Schmidt
|
||||
* @since 1.1
|
||||
*/
|
||||
@Component
|
||||
public class LocaleConverter implements Converter<Locale> {
|
||||
|
||||
@Override
|
||||
public Locale convertFromText(final String value, final Class<?> requiredType, final String optionContext) {
|
||||
if (value.length() == 2) {
|
||||
// In case only a simpele ISO-639 code is provided we use that code also for the country (ie 'de_DE')
|
||||
return new Locale(value, value.toUpperCase());
|
||||
}
|
||||
else if (value.length() == 5) {
|
||||
String[] split = value.split("_");
|
||||
return new Locale(split[0], split[1]);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAllPossibleValues(final List<Completion> completions, final Class<?> requiredType,
|
||||
final String existingData, final String optionContext, final MethodTarget target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(final Class<?> requiredType, final String optionContext) {
|
||||
return Locale.class.isAssignableFrom(requiredType);
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.converters;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.shell.core.Completion;
|
||||
import org.springframework.shell.core.Converter;
|
||||
import org.springframework.shell.core.MethodTarget;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* {@link Converter} for {@link Long}.
|
||||
*
|
||||
* @author Stefan Schmidt
|
||||
* @since 1.0
|
||||
*/
|
||||
@Component
|
||||
public class LongConverter implements Converter<Long> {
|
||||
|
||||
@Override
|
||||
public Long convertFromText(final String value, final Class<?> requiredType, final String optionContext) {
|
||||
return new Long(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAllPossibleValues(final List<Completion> completions, final Class<?> requiredType,
|
||||
final String existingData, final String optionContext, final MethodTarget target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(final Class<?> requiredType, final String optionContext) {
|
||||
return Long.class.isAssignableFrom(requiredType) || long.class.isAssignableFrom(requiredType);
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.converters;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.shell.core.Completion;
|
||||
import org.springframework.shell.core.Converter;
|
||||
import org.springframework.shell.core.MethodTarget;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* {@link Converter} for {@link Short}.
|
||||
*
|
||||
* @author Stefan Schmidt
|
||||
* @since 1.0
|
||||
*/
|
||||
@Component
|
||||
public class ShortConverter implements Converter<Short> {
|
||||
|
||||
@Override
|
||||
public Short convertFromText(final String value, final Class<?> requiredType, final String optionContext) {
|
||||
return new Short(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAllPossibleValues(final List<Completion> completions, final Class<?> requiredType,
|
||||
final String existingData, final String optionContext, final MethodTarget target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(final Class<?> requiredType, final String optionContext) {
|
||||
return Short.class.isAssignableFrom(requiredType) || short.class.isAssignableFrom(requiredType);
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.converters;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.shell.core.Shell;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class SimpleFileConverter extends FileConverter {
|
||||
@Autowired
|
||||
private Shell shell;
|
||||
|
||||
@Override
|
||||
protected File getWorkingDirectory() {
|
||||
return shell.getHome();
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.converters;
|
||||
|
||||
import org.springframework.shell.core.Converter;
|
||||
|
||||
/**
|
||||
* Interface for adding and removing classes that provide static fields which should
|
||||
* be made available via a {@link Converter}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @since 1.0
|
||||
*/
|
||||
public interface StaticFieldConverter extends Converter<Object> {
|
||||
|
||||
void add(Class<?> clazz);
|
||||
|
||||
void remove(Class<?> clazz);
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.converters;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.shell.core.Completion;
|
||||
import org.springframework.shell.core.Converter;
|
||||
import org.springframework.shell.core.MethodTarget;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A simple {@link Converter} for those classes which provide public static fields to represent possible textual values.
|
||||
*
|
||||
* @author Stefan Schmidt
|
||||
* @author Ben Alex
|
||||
* @since 1.0
|
||||
*/
|
||||
@Component
|
||||
public class StaticFieldConverterImpl implements StaticFieldConverter {
|
||||
|
||||
// Fields
|
||||
private final Map<Class<?>, Map<String, Field>> fields = new HashMap<Class<?>, Map<String, Field>>();
|
||||
|
||||
@Override
|
||||
public void add(final Class<?> clazz) {
|
||||
Assert.notNull(clazz, "A class to provide conversion services is required");
|
||||
Assert.isNull(fields.get(clazz), "Class '" + clazz + "' is already registered for completion services");
|
||||
Map<String, Field> ffields = new HashMap<String, Field>();
|
||||
for (Field field : clazz.getFields()) {
|
||||
int modifier = field.getModifiers();
|
||||
if (Modifier.isStatic(modifier) && Modifier.isPublic(modifier)) {
|
||||
ffields.put(field.getName(), field);
|
||||
}
|
||||
}
|
||||
Assert.notEmpty(ffields, "Zero public static fields accessible in '" + clazz + "'");
|
||||
fields.put(clazz, ffields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(final Class<?> clazz) {
|
||||
Assert.notNull(clazz, "A class that was providing conversion services is required");
|
||||
fields.remove(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object convertFromText(final String value, final Class<?> requiredType, final String optionContext) {
|
||||
if (!StringUtils.hasText(value)) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Field> ffields = fields.get(requiredType);
|
||||
if (ffields == null) {
|
||||
return null;
|
||||
}
|
||||
Field f = ffields.get(value);
|
||||
if (f == null) {
|
||||
// Fallback to case insensitive search
|
||||
for (Field candidate : ffields.values()) {
|
||||
if (candidate.getName().equalsIgnoreCase(value)) {
|
||||
f = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (f == null) {
|
||||
// Still not found, despite a case-insensitive search
|
||||
return null;
|
||||
}
|
||||
}
|
||||
try {
|
||||
return f.get(null);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Unable to acquire field '" + value + "' from '" + requiredType.getName()
|
||||
+ "'", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAllPossibleValues(final List<Completion> completions, final Class<?> requiredType,
|
||||
final String existingData, final String optionContext, final MethodTarget target) {
|
||||
Map<String, Field> ffields = fields.get(requiredType);
|
||||
if (ffields == null) {
|
||||
return true;
|
||||
}
|
||||
for (String field : ffields.keySet()) {
|
||||
completions.add(new Completion(field));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(final Class<?> requiredType, final String optionContext) {
|
||||
return fields.get(requiredType) != null;
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.converters;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.shell.core.Completion;
|
||||
import org.springframework.shell.core.Converter;
|
||||
import org.springframework.shell.core.MethodTarget;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* {@link Converter} for {@link String}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @since 1.0
|
||||
*/
|
||||
@Component
|
||||
public class StringConverter implements Converter<String> {
|
||||
|
||||
@Override
|
||||
public String convertFromText(final String value, final Class<?> requiredType, final String optionContext) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAllPossibleValues(final List<Completion> completions, final Class<?> requiredType,
|
||||
final String existingData, final String optionContext, final MethodTarget target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(final Class<?> requiredType, final String optionContext) {
|
||||
return String.class.isAssignableFrom(requiredType)
|
||||
&& (optionContext == null || !optionContext.contains("disable-string-converter"));
|
||||
}
|
||||
}
|
||||
@@ -1,314 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.core;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import jline.TerminalFactory;
|
||||
|
||||
import org.springframework.shell.TerminalSizeAware;
|
||||
import org.springframework.shell.core.annotation.CliCommand;
|
||||
import org.springframework.shell.event.AbstractShellStatusPublisher;
|
||||
import org.springframework.shell.event.ParseResult;
|
||||
import org.springframework.shell.event.ShellStatus;
|
||||
import org.springframework.shell.event.ShellStatus.Status;
|
||||
import org.springframework.shell.support.logging.HandlerUtils;
|
||||
import org.springframework.shell.support.util.VersionUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Provides a base {@link Shell} implementation.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Gunnar Hillert
|
||||
*/
|
||||
public abstract class AbstractShell extends AbstractShellStatusPublisher implements Shell {
|
||||
|
||||
// Constants
|
||||
private static final String MY_SLOT = AbstractShell.class.getName();
|
||||
|
||||
//TODO Abstract out to make configurable.
|
||||
protected static final String ROO_PROMPT = "spring> ";
|
||||
|
||||
// Public static fields; don't rename, make final, or make non-public, as
|
||||
// they are part of the public API, e.g. are changed by STS.
|
||||
public static String completionKeys = "TAB";
|
||||
public static String shellPrompt = ROO_PROMPT;
|
||||
|
||||
// Instance fields
|
||||
protected final Logger logger = HandlerUtils.getLogger(getClass());
|
||||
|
||||
protected final Logger exceptionLogger = Logger.getLogger(getClass().getName() + ".exceptions");
|
||||
|
||||
protected boolean inBlockComment;
|
||||
protected ExitShellRequest exitShellRequest;
|
||||
|
||||
protected abstract String getHomeAsString();
|
||||
|
||||
protected abstract ExecutionStrategy getExecutionStrategy();
|
||||
|
||||
protected abstract Parser getParser();
|
||||
|
||||
|
||||
/**
|
||||
* Execute the single line from a script.
|
||||
* <p>
|
||||
* This method can be overridden by sub-classes to pre-process script lines.
|
||||
*/
|
||||
public boolean executeScriptLine(final String line) {
|
||||
return executeCommand(line).isSuccess();
|
||||
}
|
||||
|
||||
public CommandResult executeCommand(String line) {
|
||||
// Another command was attempted
|
||||
setShellStatus(ShellStatus.Status.PARSING);
|
||||
|
||||
final ExecutionStrategy executionStrategy = getExecutionStrategy();
|
||||
boolean flashedMessage = false;
|
||||
while (executionStrategy == null || !executionStrategy.isReadyForCommands()) {
|
||||
// Wait
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException ignore) {}
|
||||
if (!flashedMessage) {
|
||||
flash(Level.INFO, "Please wait - still loading", MY_SLOT);
|
||||
flashedMessage = true;
|
||||
}
|
||||
}
|
||||
if (flashedMessage) {
|
||||
flash(Level.INFO, "", MY_SLOT);
|
||||
}
|
||||
|
||||
ParseResult parseResult = null;
|
||||
try {
|
||||
// We support simple block comments; ie a single pair per line
|
||||
if (!inBlockComment && line.contains("/*") && line.contains("*/")) {
|
||||
blockCommentBegin();
|
||||
String lhs = line.substring(0, line.lastIndexOf("/*"));
|
||||
if (line.contains("*/")) {
|
||||
line = lhs + line.substring(line.lastIndexOf("*/") + 2);
|
||||
blockCommentFinish();
|
||||
} else {
|
||||
line = lhs;
|
||||
}
|
||||
}
|
||||
if (inBlockComment) {
|
||||
if (!line.contains("*/")) {
|
||||
return new CommandResult(true);
|
||||
}
|
||||
blockCommentFinish();
|
||||
line = line.substring(line.lastIndexOf("*/") + 2);
|
||||
}
|
||||
// We also support inline comments (but only at start of line, otherwise valid
|
||||
// command options like http://www.helloworld.com will fail as per ROO-517)
|
||||
if (!inBlockComment && (line.trim().startsWith("//") || line.trim().startsWith("#"))) { // # support in ROO-1116
|
||||
line = "";
|
||||
}
|
||||
// Convert any TAB characters to whitespace (ROO-527)
|
||||
line = line.replace('\t', ' ');
|
||||
if ("".equals(line.trim())) {
|
||||
setShellStatus(Status.EXECUTION_SUCCESS);
|
||||
return new CommandResult(true);
|
||||
}
|
||||
parseResult = getParser().parse(line);
|
||||
if (parseResult == null) {
|
||||
return new CommandResult(false);
|
||||
}
|
||||
|
||||
setShellStatus(Status.EXECUTING);
|
||||
Object result = executionStrategy.execute(parseResult);
|
||||
setShellStatus(Status.EXECUTION_RESULT_PROCESSING);
|
||||
if (result != null) {
|
||||
if (result instanceof ExitShellRequest) {
|
||||
exitShellRequest = (ExitShellRequest) result;
|
||||
// Give ProcessManager a chance to close down its threads before the overall OSGi framework is terminated (ROO-1938)
|
||||
executionStrategy.terminate();
|
||||
} else {
|
||||
handleExecutionResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
logCommandIfRequired(line, true);
|
||||
setShellStatus(Status.EXECUTION_SUCCESS, line, parseResult);
|
||||
return new CommandResult(true, result, null);
|
||||
} catch (RuntimeException e) {
|
||||
setShellStatus(Status.EXECUTION_FAILED, line, parseResult);
|
||||
exceptionLogger.log(Level.WARNING, e.getMessage(), e);
|
||||
// We rely on execution strategy to log it
|
||||
try {
|
||||
logCommandIfRequired(line, false);
|
||||
} catch (Exception ignored) {}
|
||||
return new CommandResult(false, null, e);
|
||||
} finally {
|
||||
setShellStatus(Status.USER_INPUT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows a subclass to log the execution of a well-formed command. This is invoked after a command
|
||||
* has completed, and indicates whether the command returned normally or returned an exception. Note
|
||||
* that attempted commands that are not well-formed (eg they are missing a mandatory argument) will
|
||||
* never be presented to this method, as the command execution is never actually attempted in those
|
||||
* cases. This method is only invoked if an attempt is made to execute a particular command.
|
||||
*
|
||||
* <p>
|
||||
* Implementations should consider specially handling the "script" commands, and also
|
||||
* indicating whether a command was successful or not. Implementations that wish to behave
|
||||
* consistently with other {@link AbstractShell} subclasses are encouraged to simply override
|
||||
* {@link #logCommandToOutput(String)} instead, and only override this method if you actually
|
||||
* need to fine-tune the output logic.
|
||||
*
|
||||
* @param line the parsed line (any comments have been removed; never null)
|
||||
* @param successful if the command was successful or not
|
||||
*/
|
||||
protected void logCommandIfRequired(final String line, final boolean successful) {
|
||||
if (line.startsWith("script")) {
|
||||
logCommandToOutput((successful ? "// " : "// [failed] ") + line);
|
||||
} else {
|
||||
logCommandToOutput((successful ? "" : "// [failed] ") + line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows a subclass to actually write the resulting logged command to some form of output. This
|
||||
* frees subclasses from needing to implement the logic within {@link #logCommandIfRequired(String, boolean)}.
|
||||
*
|
||||
* <p>
|
||||
* Implementations should invoke {@link #getExitShellRequest()} to monitor any attempts to exit the shell and
|
||||
* release resources such as output log files.
|
||||
*
|
||||
* @param processedLine the line that should be appended to some type of output (excluding the \n character)
|
||||
*/
|
||||
protected void logCommandToOutput(final String processedLine) {}
|
||||
|
||||
/**
|
||||
* Base implementation of the {@link Shell#setPromptPath(String)} method, designed for simple shell
|
||||
* implementations. Advanced implementations (eg those that support ANSI codes etc) will likely want
|
||||
* to override this method and set the {@link #shellPrompt} variable directly.
|
||||
*
|
||||
* @param path to set (can be null or empty; must NOT be formatted in any special way eg ANSI codes)
|
||||
*/
|
||||
public void setPromptPath(final String path) {
|
||||
if (path == null || "".equals(path)) {
|
||||
shellPrompt = ROO_PROMPT;
|
||||
} else {
|
||||
shellPrompt = path + " " + ROO_PROMPT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of {@link Shell#setPromptPath(String, boolean))} method to satisfy STS compatibility.
|
||||
*
|
||||
* @param path to set (can be null or empty)
|
||||
* @param overrideStyle
|
||||
*/
|
||||
public void setPromptPath(String path, boolean overrideStyle) {
|
||||
setPromptPath(path);
|
||||
}
|
||||
|
||||
public ExitShellRequest getExitShellRequest() {
|
||||
return exitShellRequest;
|
||||
}
|
||||
|
||||
@CliCommand(value = { "/*" }, help = "Start of block comment")
|
||||
public void blockCommentBegin() {
|
||||
Assert.isTrue(!inBlockComment, "Cannot open a new block comment when one already active");
|
||||
inBlockComment = true;
|
||||
}
|
||||
|
||||
@CliCommand(value = { "*/" }, help = "End of block comment")
|
||||
public void blockCommentFinish() {
|
||||
Assert.isTrue(inBlockComment, "Cannot close a block comment when it has not been opened");
|
||||
inBlockComment = false;
|
||||
}
|
||||
|
||||
public String versionInfo(){
|
||||
return VersionUtils.versionInfo();
|
||||
}
|
||||
|
||||
public String getShellPrompt() {
|
||||
return shellPrompt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the home directory for the current shell instance.
|
||||
*
|
||||
* <p>
|
||||
* Note: calls the {@link #getHomeAsString()} method to allow subclasses to provide the home directory location as
|
||||
* string using different environment-specific strategies.
|
||||
*
|
||||
* <p>
|
||||
* If the path indicated by {@link #getHomeAsString()} exists and refers to a directory, that directory
|
||||
* is returned.
|
||||
*
|
||||
* <p>
|
||||
* If the path indicated by {@link #getHomeAsString()} exists and refers to a file, an exception is thrown.
|
||||
*
|
||||
* <p>
|
||||
* If the path indicated by {@link #getHomeAsString()} does not exist, it will be created as a directory.
|
||||
* If this fails, an exception will be thrown.
|
||||
*
|
||||
* @return the home directory for the current shell instance (which is guaranteed to exist and be a directory)
|
||||
*/
|
||||
public File getHome() {
|
||||
String rooHome = getHomeAsString();
|
||||
File f = new File(rooHome);
|
||||
Assert.isTrue(!f.exists() || (f.exists() && f.isDirectory()), "Path '" + f.getAbsolutePath() + "' must be a directory, or it must not exist");
|
||||
if (!f.exists()) {
|
||||
f.mkdirs();
|
||||
}
|
||||
Assert.isTrue(f.exists() && f.isDirectory(), "Path '" + f.getAbsolutePath() + "' is not a directory; please specify roo.home system property correctly");
|
||||
return f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple implementation of {@link #flash(Level, String, String)} that simply displays the message via the logger. It is
|
||||
* strongly recommended shell implementations override this method with a more effective approach.
|
||||
*/
|
||||
public void flash(final Level level, final String message, final String slot) {
|
||||
Assert.notNull(level, "Level is required for a flash message");
|
||||
Assert.notNull(message, "Message is required for a flash message");
|
||||
Assert.hasText(slot, "Slot name must be specified for a flash message");
|
||||
if (!("".equals(message))) {
|
||||
logger.log(level, message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the result of execution of a command. Given <i>result</i> is
|
||||
* expected to be not <code>null</code>. If <i>result</i> is a
|
||||
* {@link java.lang.Iterable} object, it will be iterated through to print
|
||||
* the output of <i>toString</i>. For other type of objects, simply output
|
||||
* of <i>toString</i> is shown. Subclasses can alter this implementation
|
||||
* to handle the <i>result</i> differently.
|
||||
*
|
||||
* @param result not <code>null</code> result of execution of a command.
|
||||
*/
|
||||
protected void handleExecutionResult(Object result) {
|
||||
if (result instanceof Iterable<?>) {
|
||||
for (Object o : (Iterable<?>) result) {
|
||||
handleExecutionResult(o);
|
||||
}
|
||||
} else if (result instanceof TerminalSizeAware) {
|
||||
int width = TerminalFactory.get().getWidth();
|
||||
logger.info(((TerminalSizeAware) result).render(width).toString());
|
||||
} else {
|
||||
logger.info(result.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.core;
|
||||
|
||||
/**
|
||||
* Marker interface indicating a provider of one or more shell commands.
|
||||
*/
|
||||
public interface CommandMarker {}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.core;
|
||||
|
||||
public class CommandResult {
|
||||
|
||||
private boolean success;
|
||||
|
||||
private Object result;
|
||||
|
||||
private Throwable exception;
|
||||
|
||||
public CommandResult(boolean success) {
|
||||
this.success = success;
|
||||
}
|
||||
public CommandResult(boolean success, Object result, Throwable exception) {
|
||||
super();
|
||||
this.success = success;
|
||||
this.result = result;
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public Object getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public Throwable getException() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CommandResult [success=" + success + ", result=" + result
|
||||
+ ", exception=" + exception + "]";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.core;
|
||||
|
||||
import org.springframework.shell.support.util.AnsiEscapeCode;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
public class Completion {
|
||||
|
||||
// Fields
|
||||
private final int order;
|
||||
private final String formattedValue;
|
||||
private final String heading;
|
||||
private final String value;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
public Completion(final String value) {
|
||||
this(value, value, null, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param value
|
||||
* @param formattedValue
|
||||
* @param heading
|
||||
* @param order
|
||||
*/
|
||||
public Completion(final String value, final String formattedValue, String heading, final int order) {
|
||||
this.formattedValue = formattedValue;
|
||||
this.order = order;
|
||||
this.value = value;
|
||||
if (StringUtils.hasText(heading)) {
|
||||
heading = AnsiEscapeCode.decorate(heading, AnsiEscapeCode.UNDERSCORE, AnsiEscapeCode.FG_GREEN);
|
||||
}
|
||||
this.heading = heading;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getFormattedValue() {
|
||||
return formattedValue;
|
||||
}
|
||||
|
||||
public String getHeading() {
|
||||
return heading;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return order + ". " + heading + " - " + value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Completion that = (Completion) o;
|
||||
if (formattedValue != null ? !formattedValue.equals(that.formattedValue) : that.formattedValue != null) {
|
||||
return false;
|
||||
}
|
||||
if (heading != null ? !heading.equals(that.heading) : that.heading != null) {
|
||||
return false;
|
||||
}
|
||||
if (value != null ? !value.equals(that.value) : that.value != null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = value != null ? value.hashCode() : 0;
|
||||
result = 31 * result + (formattedValue != null ? formattedValue.hashCode() : 0);
|
||||
result = 31 * result + (heading != null ? heading.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.core;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.shell.core.annotation.CliCommand;
|
||||
import org.springframework.shell.core.annotation.CliOption;
|
||||
|
||||
/**
|
||||
* Converts between Strings (as displayed by and entered via the shell) and Java objects
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @param <T> the type being converted to/from
|
||||
*/
|
||||
public interface Converter<T> {
|
||||
|
||||
/**
|
||||
* The prefix for the option context property that indicates how many successive tab completion requests have
|
||||
* occurred.
|
||||
*/
|
||||
public static final String TAB_COMPLETION_COUNT_PREFIX = "tab-completion-count-";
|
||||
|
||||
/**
|
||||
* Indicates whether this converter supports the given type in the given option context
|
||||
*
|
||||
* @param type the type being checked
|
||||
* @param optionContext a non-<code>null</code> string that customises the behaviour of this converter for a given
|
||||
* {@link CliOption} of a given {@link CliCommand}; the contents will have special meaning to this converter (e.g.
|
||||
* be a comma-separated list of keywords known to this converter)
|
||||
* @return see above
|
||||
*/
|
||||
boolean supports(Class<?> type, String optionContext);
|
||||
|
||||
/**
|
||||
* Converts from the given String value to type T
|
||||
*
|
||||
* @param value the value to convert
|
||||
* @param targetType the type being converted to; can't be <code>null</code>
|
||||
* @param optionContext a non-<code>null</code> string that customises the behaviour of this converter for a given
|
||||
* {@link CliOption} of a given {@link CliCommand}; the contents will have special meaning to this converter (e.g.
|
||||
* be a comma-separated list of keywords known to this converter)
|
||||
* @return see above
|
||||
* @throws RuntimeException if the given value could not be converted
|
||||
*/
|
||||
T convertFromText(String value, Class<?> targetType, String optionContext);
|
||||
|
||||
/**
|
||||
* Populates the given list with the possible completions
|
||||
*
|
||||
* @param completions the list to populate; can't be <code>null</code>
|
||||
* @param targetType the type of parameter for which a string is being entered
|
||||
* @param existingData what the user has typed so far
|
||||
* @param optionContext a non-<code>null</code> string that customises the behaviour of this converter for a given
|
||||
* {@link CliOption} of a given {@link CliCommand}; the contents will have special meaning to this converter (e.g.
|
||||
* be a comma-separated list of keywords known to this converter)
|
||||
* @param target
|
||||
* @return <code>true</code> if all the added completions are complete values, or <code>false</code> if the user can
|
||||
* press TAB to add further information to some or all of them
|
||||
*/
|
||||
boolean getAllPossibleValues(List<Completion> completions, Class<?> targetType, String existingData,
|
||||
String optionContext, MethodTarget target);
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.core;
|
||||
|
||||
import org.springframework.shell.event.ParseResult;
|
||||
|
||||
/**
|
||||
* Extension interface allowing command provider to be called
|
||||
* in a generic fashion just before, and right after, executing a command.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface ExecutionProcessor extends CommandMarker {
|
||||
|
||||
/**
|
||||
* Method called before invoking the target command (described by {@link ParseResult}).
|
||||
* Additionally, for advanced cases, the parse result itself effectively changing the invocation
|
||||
* calling site.
|
||||
*
|
||||
* @param invocationContext target command context
|
||||
* @return the invocation target
|
||||
*/
|
||||
ParseResult beforeInvocation(ParseResult invocationContext);
|
||||
|
||||
/**
|
||||
* Method called after successfully invoking the target command (described by {@link ParseResult}).
|
||||
*
|
||||
* @param invocationContext target command context
|
||||
* @param result the invocation result
|
||||
*/
|
||||
void afterReturningInvocation(ParseResult invocationContext, Object result);
|
||||
|
||||
/**
|
||||
* Method called after invoking the target command (described by {@link ParseResult}) had thrown an exception .
|
||||
*
|
||||
* @param invocationContext target command context
|
||||
* @param thrown the thrown object
|
||||
*/
|
||||
void afterThrowingInvocation(ParseResult invocationContext, Throwable thrown);
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.core;
|
||||
|
||||
import org.springframework.shell.event.ParseResult;
|
||||
|
||||
/**
|
||||
* Strategy interface to permit the controlled execution of methods.
|
||||
*
|
||||
* <p>
|
||||
* This interface is used to enable a {@link Shell} to execute methods in a consistent, system-wide
|
||||
* manner. A typical use case is to ensure user interface commands are not executed concurrently
|
||||
* when other background threads are performing certain operations.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @since 1.0
|
||||
*
|
||||
*/
|
||||
public interface ExecutionStrategy {
|
||||
|
||||
/**
|
||||
* Executes the method indicated by the {@link ParseResult}.
|
||||
*
|
||||
* @param parseResult that should be executed (never presented as null)
|
||||
* @return an object which will be rendered by the {@link Shell} implementation (may return null)
|
||||
* @throws RuntimeException which is handled by the {@link Shell} implementation
|
||||
*/
|
||||
Object execute(ParseResult parseResult) throws RuntimeException;
|
||||
|
||||
/**
|
||||
* Indicates commands are able to be presented. This generally means all important
|
||||
* system startup activities have completed.
|
||||
*
|
||||
* @return whether commands can be presented for processing at this time
|
||||
*/
|
||||
boolean isReadyForCommands();
|
||||
|
||||
/**
|
||||
* Indicates the execution runtime should be terminated. This allows it to cleanup before returning
|
||||
* control flow to the caller. Necessary for clean shutdowns.
|
||||
*/
|
||||
void terminate();
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.core;
|
||||
|
||||
/**
|
||||
* An immutable representation of a request to exit the shell.
|
||||
*
|
||||
* <p>
|
||||
* Implementations of the shell are free to handle these requests in whatever
|
||||
* way they wish. Callers should not expect an exit request to be completed.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public class ExitShellRequest {
|
||||
|
||||
// Constants
|
||||
public static final ExitShellRequest NORMAL_EXIT = new ExitShellRequest(0);
|
||||
public static final ExitShellRequest FATAL_EXIT = new ExitShellRequest(1);
|
||||
public static final ExitShellRequest JVM_TERMINATED_EXIT = new ExitShellRequest(99); // Ensure 99 is maintained in o.s.r.bootstrap.Main as it's the default for a null roo.exit code
|
||||
|
||||
// Fields
|
||||
private final int exitCode;
|
||||
|
||||
private ExitShellRequest(final int exitCode) {
|
||||
this.exitCode = exitCode;
|
||||
}
|
||||
|
||||
public int getExitCode() {
|
||||
return exitCode;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.core;
|
||||
|
||||
import jline.UnsupportedTerminal;
|
||||
|
||||
/**
|
||||
* Terminal used for debugging inside an IDE. See the development instructions.
|
||||
*/
|
||||
public class IdeTerminal extends UnsupportedTerminal {
|
||||
|
||||
public boolean isANSISupported() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,223 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.core;
|
||||
|
||||
import static org.fusesource.jansi.Ansi.ansi;
|
||||
import static org.fusesource.jansi.Ansi.Color.GREEN;
|
||||
import static org.fusesource.jansi.Ansi.Color.MAGENTA;
|
||||
import static org.fusesource.jansi.Ansi.Color.RED;
|
||||
import static org.springframework.shell.support.util.OsUtils.LINE_SEPARATOR;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
|
||||
import jline.console.ConsoleReader;
|
||||
|
||||
import org.fusesource.jansi.Ansi;
|
||||
import org.fusesource.jansi.Ansi.Attribute;
|
||||
import org.springframework.shell.support.util.IOUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* JDK logging {@link Handler} that emits log messages to a JLine {@link ConsoleReader}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @since 1.0
|
||||
*/
|
||||
public class JLineLogHandler extends Handler {
|
||||
|
||||
// Fields
|
||||
private ConsoleReader reader;
|
||||
|
||||
private ShellPromptAccessor shellPromptAccessor;
|
||||
|
||||
private static ThreadLocal<Boolean> redrawProhibit = new ThreadLocal<Boolean>();
|
||||
|
||||
private static String lastMessage;
|
||||
|
||||
private static boolean includeThreadName = false;
|
||||
|
||||
private boolean ansiSupported;
|
||||
|
||||
private String userInterfaceThreadName;
|
||||
|
||||
private static boolean suppressDuplicateMessages = true;
|
||||
|
||||
public JLineLogHandler(final ConsoleReader reader, final ShellPromptAccessor shellPromptAccessor) {
|
||||
Assert.notNull(reader, "Console reader required");
|
||||
Assert.notNull(shellPromptAccessor, "Shell prompt accessor required");
|
||||
this.reader = reader;
|
||||
this.shellPromptAccessor = shellPromptAccessor;
|
||||
this.userInterfaceThreadName = Thread.currentThread().getName();
|
||||
this.ansiSupported = reader.getTerminal().isAnsiSupported();
|
||||
|
||||
setFormatter(new Formatter() {
|
||||
@Override
|
||||
public String format(final LogRecord record) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
if (record.getMessage() != null) {
|
||||
sb.append(record.getMessage()).append(LINE_SEPARATOR);
|
||||
}
|
||||
if (record.getThrown() != null) {
|
||||
PrintWriter pw = null;
|
||||
try {
|
||||
StringWriter sw = new StringWriter();
|
||||
pw = new PrintWriter(sw);
|
||||
record.getThrown().printStackTrace(pw);
|
||||
sb.append(sw.toString());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
}
|
||||
finally {
|
||||
IOUtils.closeQuietly(pw);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws SecurityException {
|
||||
}
|
||||
|
||||
public static void prohibitRedraw() {
|
||||
redrawProhibit.set(true);
|
||||
}
|
||||
|
||||
public static void cancelRedrawProhibition() {
|
||||
redrawProhibit.remove();
|
||||
}
|
||||
|
||||
public static void setIncludeThreadName(final boolean include) {
|
||||
includeThreadName = include;
|
||||
}
|
||||
|
||||
public static void resetMessageTracking() {
|
||||
lastMessage = null; // see ROO-251
|
||||
}
|
||||
|
||||
public static boolean isSuppressDuplicateMessages() {
|
||||
return suppressDuplicateMessages;
|
||||
}
|
||||
|
||||
public static void setSuppressDuplicateMessages(final boolean suppressDuplicateMessages) {
|
||||
JLineLogHandler.suppressDuplicateMessages = suppressDuplicateMessages;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publish(final LogRecord record) {
|
||||
try {
|
||||
// Avoid repeating the same message that displayed immediately before the current message (ROO-30, ROO-1873)
|
||||
String toDisplay = toDisplay(record);
|
||||
if (toDisplay.equals(lastMessage) && suppressDuplicateMessages) {
|
||||
return;
|
||||
}
|
||||
lastMessage = toDisplay;
|
||||
|
||||
StringBuilder buffer = reader.getCursorBuffer().copy().buffer;
|
||||
int cursor = reader.getCursorBuffer().cursor;
|
||||
if (reader.getCursorBuffer().length() > 0) {
|
||||
// The user has semi-typed something, so put a new line in so the debug message is separated
|
||||
reader.println();
|
||||
|
||||
// We need to cancel whatever they typed (it's reset later on), so the line appears empty
|
||||
reader.getCursorBuffer().clear();
|
||||
}
|
||||
|
||||
// This ensures nothing is ever displayed when redrawing the line
|
||||
reader.setPrompt("");
|
||||
reader.redrawLine();
|
||||
// Now restore the line formatting settings back to their original
|
||||
reader.setPrompt(shellPromptAccessor.getShellPrompt());
|
||||
|
||||
reader.getCursorBuffer().write(buffer.toString());
|
||||
reader.getCursorBuffer().cursor = cursor;
|
||||
|
||||
reader.print(toDisplay);
|
||||
|
||||
Boolean prohibitingRedraw = redrawProhibit.get();
|
||||
if (prohibitingRedraw == null) {
|
||||
reader.redrawLine();
|
||||
}
|
||||
|
||||
reader.flush();
|
||||
}
|
||||
catch (Exception e) {
|
||||
reportError("Could not publish log message", e, Level.SEVERE.intValue());
|
||||
}
|
||||
}
|
||||
|
||||
private String toDisplay(final LogRecord event) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
String threadName;
|
||||
String eventString;
|
||||
if (includeThreadName && !userInterfaceThreadName.equals(Thread.currentThread().getName())
|
||||
&& !"".equals(Thread.currentThread().getName())) {
|
||||
threadName = "[" + Thread.currentThread().getName() + "]";
|
||||
|
||||
// Build an event string that will indent nicely given the left hand side now contains a thread name
|
||||
StringBuilder lineSeparatorAndIndentingString = new StringBuilder();
|
||||
for (int i = 0; i <= threadName.length(); i++) {
|
||||
lineSeparatorAndIndentingString.append(" ");
|
||||
}
|
||||
|
||||
eventString = " "
|
||||
+ getFormatter().format(event).replace(LINE_SEPARATOR,
|
||||
LINE_SEPARATOR + lineSeparatorAndIndentingString.toString());
|
||||
if (eventString.endsWith(lineSeparatorAndIndentingString.toString())) {
|
||||
eventString = eventString.substring(0, eventString.length() - lineSeparatorAndIndentingString.length());
|
||||
}
|
||||
}
|
||||
else {
|
||||
threadName = "";
|
||||
eventString = getFormatter().format(event);
|
||||
}
|
||||
|
||||
if (ansiSupported) {
|
||||
Ansi ansi = ansi(sb);
|
||||
if (event.getLevel().intValue() >= Level.SEVERE.intValue()) {
|
||||
ansi.a(Attribute.NEGATIVE_ON).a(threadName).a(Attribute.NEGATIVE_OFF).fg(RED).a(eventString).reset();
|
||||
}
|
||||
else if (event.getLevel().intValue() >= Level.WARNING.intValue()) {
|
||||
ansi.a(Attribute.NEGATIVE_ON).a(threadName).a(Attribute.NEGATIVE_OFF).fg(MAGENTA).a(eventString)
|
||||
.reset();
|
||||
}
|
||||
else if (event.getLevel().intValue() >= Level.INFO.intValue()) {
|
||||
ansi.a(Attribute.NEGATIVE_ON).a(threadName).a(Attribute.NEGATIVE_OFF).fg(GREEN).a(eventString).reset();
|
||||
}
|
||||
else {
|
||||
ansi.a(Attribute.NEGATIVE_ON).a(threadName).a(Attribute.NEGATIVE_OFF).a(eventString);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
sb.append(threadName).append(eventString);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,704 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.core;
|
||||
|
||||
import static org.fusesource.jansi.Ansi.ansi;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import jline.WindowsTerminal;
|
||||
import jline.console.ConsoleReader;
|
||||
import jline.console.UserInterruptException;
|
||||
import jline.console.history.History;
|
||||
import jline.console.history.MemoryHistory;
|
||||
|
||||
import org.apache.commons.io.input.ReversedLinesFileReader;
|
||||
import org.fusesource.jansi.Ansi;
|
||||
import org.fusesource.jansi.Ansi.Attribute;
|
||||
import org.fusesource.jansi.Ansi.Color;
|
||||
import org.fusesource.jansi.Ansi.Erase;
|
||||
import org.fusesource.jansi.AnsiConsole;
|
||||
import org.springframework.shell.event.ShellStatus;
|
||||
import org.springframework.shell.event.ShellStatus.Status;
|
||||
import org.springframework.shell.event.ShellStatusListener;
|
||||
import org.springframework.shell.support.util.IOUtils;
|
||||
import org.springframework.shell.support.util.OsUtils;
|
||||
import org.springframework.shell.support.util.VersionUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Uses the feature-rich <a href="http://sourceforge.net/projects/jline/">JLine</a> library to provide an interactive
|
||||
* shell.
|
||||
*
|
||||
* <p>
|
||||
* Due to Windows' lack of color ANSI services out-of-the-box, this implementation automatically detects the classpath
|
||||
* presence of <a href="http://jansi.fusesource.org/">Jansi</a> and uses it if present. This library is not necessary
|
||||
* for *nix machines, which support colour ANSI without any special effort. This implementation has been written to use
|
||||
* reflection in order to avoid hard dependencies on Jansi.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Jarred Li
|
||||
* @author Glenn Renfro
|
||||
* @since 1.0
|
||||
*/
|
||||
public abstract class JLineShell extends AbstractShell implements Shell, Runnable {
|
||||
|
||||
// Constants
|
||||
private static final String ANSI_CONSOLE_CLASSNAME = "org.fusesource.jansi.AnsiConsole";
|
||||
|
||||
private static final boolean JANSI_AVAILABLE = ClassUtils.isPresent(ANSI_CONSOLE_CLASSNAME,
|
||||
JLineShell.class.getClassLoader());
|
||||
|
||||
private static final char ESCAPE = 27;
|
||||
|
||||
private static final String BEL = "\007";
|
||||
|
||||
// Fields
|
||||
protected volatile ConsoleReader reader;
|
||||
|
||||
private boolean developmentMode = false;
|
||||
|
||||
private FileWriter fileLog;
|
||||
|
||||
private final DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
protected ShellStatusListener statusListener; // ROO-836
|
||||
|
||||
/** key: slot name, value: flashInfo instance */
|
||||
private final Map<String, FlashInfo> flashInfoMap = new HashMap<String, FlashInfo>();
|
||||
|
||||
/** key: row number, value: eraseLineFromPosition */
|
||||
private final Map<Integer, Integer> rowErasureMap = new HashMap<Integer, Integer>();
|
||||
|
||||
private boolean shutdownHookFired = false; // ROO-1599
|
||||
|
||||
private int historySize;
|
||||
|
||||
public void run() {
|
||||
reader = createConsoleReader();
|
||||
|
||||
setPromptPath(null);
|
||||
|
||||
JLineLogHandler handler = new JLineLogHandler(reader, this);
|
||||
JLineLogHandler.prohibitRedraw(); // Affects this thread only
|
||||
Logger mainLogger = Logger.getLogger("");
|
||||
removeHandlers(mainLogger);
|
||||
mainLogger.addHandler(handler);
|
||||
|
||||
reader.addCompleter(new ParserCompleter(getParser()));
|
||||
|
||||
reader.setBellEnabled(true);
|
||||
if (Boolean.getBoolean("jline.nobell")) {
|
||||
reader.setBellEnabled(false);
|
||||
}
|
||||
|
||||
// reader.setDebug(new PrintWriter(new FileWriter("writer.debug", true)));
|
||||
|
||||
openFileLogIfPossible();
|
||||
History history = this.reader.getHistory();
|
||||
if (history instanceof MemoryHistory) {
|
||||
((MemoryHistory) history).setMaxSize(getHistorySize());
|
||||
}
|
||||
// Try to build previous command history from the project's log
|
||||
String[] filteredLogEntries = filterLogEntry();
|
||||
for (String logEntry : filteredLogEntries) {
|
||||
reader.getHistory().add(logEntry);
|
||||
}
|
||||
|
||||
flashMessageRenderer();
|
||||
flash(Level.FINE, this.getProductName() + " " + this.getVersion(), Shell.WINDOW_TITLE_SLOT);
|
||||
printBannerAndWelcome();
|
||||
|
||||
String startupNotifications = getStartupNotifications();
|
||||
if (StringUtils.hasText(startupNotifications)) {
|
||||
logger.info(startupNotifications);
|
||||
}
|
||||
|
||||
setShellStatus(Status.STARTED);
|
||||
|
||||
try {
|
||||
// Monitor CTRL+C initiated shutdowns (ROO-1599)
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
|
||||
public void run() {
|
||||
shutdownHookFired = true;
|
||||
}
|
||||
}, getProductName() + " JLine Shutdown Hook"));
|
||||
}
|
||||
catch (Throwable t) {
|
||||
}
|
||||
|
||||
// Handle any "execute-then-quit" operation
|
||||
|
||||
String rooArgs = System.getProperty("roo.args");
|
||||
if (rooArgs != null && !"".equals(rooArgs)) {
|
||||
setShellStatus(Status.USER_INPUT);
|
||||
boolean success = executeCommand(rooArgs).isSuccess();
|
||||
if (exitShellRequest == null) {
|
||||
// The command itself did not specify an exit shell code, so we'll fall back to something sensible here
|
||||
executeCommand("quit"); // ROO-839
|
||||
exitShellRequest = success ? ExitShellRequest.NORMAL_EXIT : ExitShellRequest.FATAL_EXIT;
|
||||
}
|
||||
setShellStatus(Status.SHUTTING_DOWN);
|
||||
}
|
||||
else {
|
||||
// Normal RPEL processing
|
||||
promptLoop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* read history commands from history log. the history size if determined by --histsize options.
|
||||
*
|
||||
* @return history commands
|
||||
*/
|
||||
private String[] filterLogEntry() {
|
||||
ArrayList<String> entries = new ArrayList<String>();
|
||||
ReversedLinesFileReader reversedReader = null;
|
||||
try {
|
||||
reversedReader = new ReversedLinesFileReader(new File(getHistoryFileName()), 4096, Charset.forName("UTF-8"));
|
||||
int size = 0;
|
||||
String line = null;
|
||||
while ((line = reversedReader.readLine()) != null) {
|
||||
if (!line.startsWith("//")) {
|
||||
size++;
|
||||
if (size > historySize) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
entries.add(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger.warning("read history file failed. Reason:" + e.getMessage());
|
||||
}
|
||||
finally {
|
||||
closeReversedReader(reversedReader);
|
||||
}
|
||||
Collections.reverse(entries);
|
||||
return entries.toArray(new String[0]);
|
||||
}
|
||||
|
||||
private void closeReversedReader(ReversedLinesFileReader reversedReader) {
|
||||
if (reversedReader != null) {
|
||||
try {
|
||||
reversedReader.close();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
logger.warning("Cloud not close ReversedLinesFileReader: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new jline ConsoleReader. On Windows if jansi is available, uses createAnsiWindowsReader(). Otherwise,
|
||||
* always creates a default ConsoleReader. Sub-classes of this class can plug in their version of ConsoleReader by
|
||||
* overriding this method, if required.
|
||||
*
|
||||
* @return a jline ConsoleReader instance
|
||||
*/
|
||||
protected ConsoleReader createConsoleReader() {
|
||||
ConsoleReader consoleReader = null;
|
||||
try {
|
||||
if (isJansiAvailable()) {
|
||||
try {
|
||||
consoleReader = createAnsiWindowsReader();
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Try again using default ConsoleReader constructor
|
||||
logger.warning("Can't initialize jansi AnsiConsole, falling back to default: " + e);
|
||||
}
|
||||
}
|
||||
if (consoleReader == null) {
|
||||
consoleReader = new ConsoleReader();
|
||||
}
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
throw new IllegalStateException("Cannot start console class", ioe);
|
||||
}
|
||||
consoleReader.setExpandEvents(false);
|
||||
consoleReader.setHandleUserInterrupt(true);
|
||||
return consoleReader;
|
||||
}
|
||||
|
||||
private boolean isJansiAvailable() {
|
||||
return JANSI_AVAILABLE && OsUtils.isWindows() && System.getProperty("jline.terminal") == null;
|
||||
}
|
||||
|
||||
public void printBannerAndWelcome() {
|
||||
}
|
||||
|
||||
public String getStartupNotifications() {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void removeHandlers(final Logger l) {
|
||||
Handler[] handlers = l.getHandlers();
|
||||
if (handlers != null && handlers.length > 0) {
|
||||
for (Handler h : handlers) {
|
||||
l.removeHandler(h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPromptPath(final String path) {
|
||||
setPromptPath(path, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPromptPath(final String path, final boolean overrideStyle) {
|
||||
if (reader.getTerminal().isAnsiSupported()) {
|
||||
// ANSIBuffer ansi = JLineLogHandler.getANSIBuffer();
|
||||
Ansi ansi = ansi();
|
||||
if (path == null || "".equals(path)) {
|
||||
shellPrompt = ansi.fg(Color.YELLOW).a(getPromptText()).reset().toString();
|
||||
}
|
||||
else {
|
||||
if (overrideStyle) {
|
||||
ansi.a(path);
|
||||
}
|
||||
else {
|
||||
ansi.fg(Color.CYAN).a(path).reset();
|
||||
}
|
||||
shellPrompt = ansi.fg(Color.YELLOW).a(" " + getPromptText()).toString();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// The superclass will do for this non-ANSI terminal
|
||||
super.setPromptPath(path);
|
||||
}
|
||||
|
||||
// The shellPrompt is now correct; let's ensure it now gets used
|
||||
reader.setPrompt(AbstractShell.shellPrompt);
|
||||
}
|
||||
|
||||
protected ConsoleReader createAnsiWindowsReader() throws Exception {
|
||||
// Get decorated OutputStream that parses ANSI-codes
|
||||
final PrintStream ansiOut = (PrintStream) ClassUtils
|
||||
.forName(ANSI_CONSOLE_CLASSNAME, JLineShell.class.getClassLoader()).getMethod("out").invoke(null);
|
||||
WindowsTerminal ansiTerminal = new WindowsTerminal() {
|
||||
@Override
|
||||
public synchronized boolean isAnsiSupported() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
ansiTerminal.init();
|
||||
// Make sure to reset the original shell's colors on shutdown by closing the stream
|
||||
statusListener = new ShellStatusListener() {
|
||||
public void onShellStatusChange(final ShellStatus oldStatus, final ShellStatus newStatus) {
|
||||
if (newStatus.getStatus().equals(Status.SHUTTING_DOWN)) {
|
||||
ansiOut.close();
|
||||
}
|
||||
}
|
||||
};
|
||||
addShellStatusListener(statusListener);
|
||||
|
||||
// return new ConsoleReader(new FileInputStream(FileDescriptor.in), new PrintWriter(new OutputStreamWriter(
|
||||
// ansiOut,
|
||||
// // Default to Cp850 encoding for Windows console output (ROO-439)
|
||||
// System.getProperty("jline.WindowsTerminal.output.encoding", "Cp850"))), null, ansiTerminal);
|
||||
|
||||
OutputStream out = AnsiConsole.wrapOutputStream(ansiOut);
|
||||
return new ConsoleReader(new FileInputStream(FileDescriptor.in), out, ansiTerminal);
|
||||
}
|
||||
|
||||
private void flashMessageRenderer() {
|
||||
if (!reader.getTerminal().isAnsiSupported()) {
|
||||
return;
|
||||
}
|
||||
// Setup a thread to ensure flash messages are displayed and cleared correctly
|
||||
Thread t = new Thread(new Runnable() {
|
||||
public void run() {
|
||||
while (!shellStatus.getStatus().equals(Status.SHUTTING_DOWN) && !shutdownHookFired) {
|
||||
synchronized (flashInfoMap) {
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
Set<String> toRemove = new HashSet<String>();
|
||||
for (String slot : flashInfoMap.keySet()) {
|
||||
FlashInfo flashInfo = flashInfoMap.get(slot);
|
||||
|
||||
if (flashInfo.flashMessageUntil < now) {
|
||||
// Message has expired, so clear it
|
||||
toRemove.add(slot);
|
||||
doAnsiFlash(flashInfo.rowNumber, Level.ALL, "");
|
||||
}
|
||||
else {
|
||||
// The expiration time for this message has not been reached, so preserve it
|
||||
doAnsiFlash(flashInfo.rowNumber, flashInfo.flashLevel, flashInfo.flashMessage);
|
||||
}
|
||||
}
|
||||
for (String slot : toRemove) {
|
||||
flashInfoMap.remove(slot);
|
||||
}
|
||||
}
|
||||
try {
|
||||
Thread.sleep(200);
|
||||
}
|
||||
catch (InterruptedException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}, getProductName() + " JLine Flash Message Manager");
|
||||
t.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flash(final Level level, final String message, final String slot) {
|
||||
Assert.notNull(level, "Level is required for a flash message");
|
||||
Assert.notNull(message, "Message is required for a flash message");
|
||||
Assert.hasText(slot, "Slot name must be specified for a flash message");
|
||||
|
||||
if (Shell.WINDOW_TITLE_SLOT.equals(slot)) {
|
||||
if (reader != null && reader.getTerminal().isAnsiSupported()) {
|
||||
// We can probably update the window title, as requested
|
||||
if (!StringUtils.hasText(message)) {
|
||||
System.out.println("No text");
|
||||
}
|
||||
|
||||
Ansi ansi = ansi();
|
||||
ansi.a(ESCAPE + "]0;").a(message).a(BEL);
|
||||
try {
|
||||
reader.print(ansi.toString());
|
||||
reader.flush();
|
||||
}
|
||||
catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
if ((reader != null && !reader.getTerminal().isAnsiSupported())) {
|
||||
super.flash(level, message, slot);
|
||||
return;
|
||||
}
|
||||
synchronized (flashInfoMap) {
|
||||
FlashInfo flashInfo = flashInfoMap.get(slot);
|
||||
|
||||
if ("".equals(message)) {
|
||||
// Request to clear the message, but give the user some time to read it first
|
||||
if (flashInfo == null) {
|
||||
// We didn't have a record of displaying it in the first place, so just quit
|
||||
return;
|
||||
}
|
||||
flashInfo.flashMessageUntil = System.currentTimeMillis() + 1500;
|
||||
}
|
||||
else {
|
||||
// Display this message displayed until further notice
|
||||
if (flashInfo == null) {
|
||||
// Find a row for this new slot; we basically take the first line number we discover
|
||||
flashInfo = new FlashInfo();
|
||||
flashInfo.rowNumber = Integer.MAX_VALUE;
|
||||
outer: for (int i = 1; i < Integer.MAX_VALUE; i++) {
|
||||
for (FlashInfo existingFlashInfo : flashInfoMap.values()) {
|
||||
if (existingFlashInfo.rowNumber == i) {
|
||||
// Veto, let's try the new candidate row number
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
// If we got to here, nobody owns this row number, so use it
|
||||
flashInfo.rowNumber = i;
|
||||
break outer;
|
||||
}
|
||||
|
||||
// Store it
|
||||
flashInfoMap.put(slot, flashInfo);
|
||||
}
|
||||
// Populate the instance with the latest data
|
||||
flashInfo.flashMessageUntil = Long.MAX_VALUE;
|
||||
flashInfo.flashLevel = level;
|
||||
flashInfo.flashMessage = message;
|
||||
|
||||
// Display right now
|
||||
doAnsiFlash(flashInfo.rowNumber, flashInfo.flashLevel, flashInfo.flashMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Externally synchronized via the two calling methods having a mutex on flashInfoMap
|
||||
private void doAnsiFlash(final int row, final Level level, final String message) {
|
||||
Ansi ansi = ansi();
|
||||
if (isAppleTerminal()) {
|
||||
ansi.a(ESCAPE + "7");
|
||||
}
|
||||
else {
|
||||
ansi.saveCursorPosition();
|
||||
}
|
||||
|
||||
// Figure out the longest line we're presently displaying (or were) and erase the line from that position
|
||||
int mostFurtherLeftColNumber = Integer.MAX_VALUE;
|
||||
for (Integer candidate : rowErasureMap.values()) {
|
||||
if (candidate < mostFurtherLeftColNumber) {
|
||||
mostFurtherLeftColNumber = candidate;
|
||||
}
|
||||
}
|
||||
|
||||
if (mostFurtherLeftColNumber == Integer.MAX_VALUE) {
|
||||
// There is nothing to erase
|
||||
}
|
||||
else {
|
||||
ansi.cursor(row, mostFurtherLeftColNumber);
|
||||
ansi.eraseLine(Erase.FORWARD); // Clear what was present on the line
|
||||
}
|
||||
|
||||
if (("".equals(message))) {
|
||||
// They want the line blank; we've already achieved this if needed via the erasing above
|
||||
// Just need to record we no longer care about this line the next time doAnsiFlash is invoked
|
||||
rowErasureMap.remove(row);
|
||||
}
|
||||
else {
|
||||
if (shutdownHookFired) {
|
||||
return; // ROO-1599
|
||||
}
|
||||
// They want some message displayed
|
||||
int startFrom = reader.getTerminal().getWidth() - message.length() + 1;
|
||||
if (startFrom < 1) {
|
||||
startFrom = 1;
|
||||
}
|
||||
ansi.cursor(row, startFrom);
|
||||
ansi.a(Attribute.NEGATIVE_ON).a(message).a(Attribute.NEGATIVE_OFF);
|
||||
// Record we want to erase from this positioning next time (so we clean up after ourselves)
|
||||
rowErasureMap.put(row, startFrom);
|
||||
}
|
||||
if (isAppleTerminal()) {
|
||||
ansi.a(ESCAPE + "8");
|
||||
}
|
||||
else {
|
||||
ansi.restorCursorPosition();
|
||||
}
|
||||
|
||||
try {
|
||||
reader.print(ansi.toString());
|
||||
reader.flush();
|
||||
}
|
||||
catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Awaits user input, executes the command and displays the prompt to the user.
|
||||
*/
|
||||
public void promptLoop() {
|
||||
setShellStatus(Status.USER_INPUT);
|
||||
String line = null;
|
||||
String prompt = getPromptText();
|
||||
|
||||
try {
|
||||
while (exitShellRequest == null) {
|
||||
try {
|
||||
line = reader.readLine();
|
||||
}
|
||||
catch (UserInterruptException e) {
|
||||
if (e.getPartialLine().length() == 0) {
|
||||
exitShellRequest = ExitShellRequest.FATAL_EXIT;
|
||||
}
|
||||
}
|
||||
JLineLogHandler.resetMessageTracking();
|
||||
setShellStatus(Status.USER_INPUT);
|
||||
|
||||
if (StringUtils.hasText(line)) {
|
||||
executeCommand(line);
|
||||
}
|
||||
//update the prompt after the command has been executed in case an application event listener in the
|
||||
//command changes state in the prompt provider.
|
||||
prompt = generatePromptUpdate(prompt);
|
||||
|
||||
}
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
throw new IllegalStateException("Shell line reading failure", ioe);
|
||||
}
|
||||
setShellStatus(Status.SHUTTING_DOWN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the latest prompt and if the latest prompt is different than the existing prompt,
|
||||
* the shellPrompt is updated.
|
||||
* @param existingPrompt The prompt that is recognized as the current prompt.
|
||||
* @return The prompt that the shellPrompt displays.
|
||||
*/
|
||||
public String generatePromptUpdate(String existingPrompt) {
|
||||
String newPrompt = getPromptText();
|
||||
if (!ObjectUtils.nullSafeEquals(existingPrompt, newPrompt)) {
|
||||
setPromptPath(null);
|
||||
}
|
||||
return newPrompt;
|
||||
}
|
||||
|
||||
public void setDevelopmentMode(final boolean developmentMode) {
|
||||
JLineLogHandler.setIncludeThreadName(developmentMode);
|
||||
JLineLogHandler.setSuppressDuplicateMessages(!developmentMode); // We want to see duplicate messages during
|
||||
// development time (ROO-1873)
|
||||
this.developmentMode = developmentMode;
|
||||
}
|
||||
|
||||
public boolean isDevelopmentMode() {
|
||||
return this.developmentMode;
|
||||
}
|
||||
|
||||
private void openFileLogIfPossible() {
|
||||
try {
|
||||
fileLog = new FileWriter(getHistoryFileName(), true);
|
||||
// First write, so let's record the date and time of the first user command
|
||||
fileLog.write("// " + getProductName() + " " + versionInfo() + " log opened at " + df.format(new Date())
|
||||
+ "\n");
|
||||
fileLog.flush();
|
||||
}
|
||||
catch (IOException ignoreIt) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void logCommandToOutput(final String processedLine) {
|
||||
if (fileLog == null) {
|
||||
openFileLogIfPossible();
|
||||
if (fileLog == null) {
|
||||
// Still failing, so give up
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
fileLog.write(processedLine + "\n"); // Unix line endings only from Roo
|
||||
fileLog.flush(); // So tail -f will show it's working
|
||||
if (getExitShellRequest() != null) {
|
||||
// Shutting down, so close our file (we can always reopen it later if needed)
|
||||
fileLog.write("// " + getProductName() + " " + versionInfo() + " log closed at "
|
||||
+ df.format(new Date()) + "\n");
|
||||
IOUtils.closeQuietly(fileLog);
|
||||
fileLog = null;
|
||||
}
|
||||
}
|
||||
catch (IOException ignoreIt) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the "roo.home" from the system property, falling back to the current working directory if missing.
|
||||
*
|
||||
* @return the 'roo.home' system property
|
||||
*/
|
||||
@Override
|
||||
protected String getHomeAsString() {
|
||||
String rooHome = System.getProperty("roo.home");
|
||||
if (rooHome == null) {
|
||||
try {
|
||||
rooHome = new File(".").getCanonicalPath();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
return rooHome;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be called by a subclass before deactivating the shell.
|
||||
*/
|
||||
protected void closeShell() {
|
||||
// Notify we're closing down (normally our status is already shutting_down, but if it was a CTRL+C via the
|
||||
// o.s.r.bootstrap.Main hook)
|
||||
setShellStatus(Status.SHUTTING_DOWN);
|
||||
if (statusListener != null) {
|
||||
removeShellStatusListener(statusListener);
|
||||
}
|
||||
}
|
||||
|
||||
private static class FlashInfo {
|
||||
String flashMessage;
|
||||
|
||||
long flashMessageUntil;
|
||||
|
||||
Level flashLevel;
|
||||
|
||||
int rowNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* get history file name from provider. The provider has highest order
|
||||
* <link>org.springframework.core.Ordered.getOder</link> will win.
|
||||
*
|
||||
* @return history file name
|
||||
*/
|
||||
abstract protected String getHistoryFileName();
|
||||
|
||||
/**
|
||||
* get prompt text from provider. The provider has highest order
|
||||
* <link>org.springframework.core.Ordered.getOder</link> will win.
|
||||
*
|
||||
* @return prompt text
|
||||
*/
|
||||
abstract protected String getPromptText();
|
||||
|
||||
/**
|
||||
* get product name
|
||||
*
|
||||
* @return Product Name
|
||||
*/
|
||||
abstract protected String getProductName();
|
||||
|
||||
/**
|
||||
* get version information
|
||||
*
|
||||
* @return Version
|
||||
*/
|
||||
protected String getVersion() {
|
||||
return VersionUtils.versionInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the historySize
|
||||
*/
|
||||
public int getHistorySize() {
|
||||
return historySize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param historySize the historySize to set
|
||||
*/
|
||||
public void setHistorySize(int historySize) {
|
||||
this.historySize = historySize;
|
||||
}
|
||||
|
||||
private static boolean isAppleTerminal() {
|
||||
final String terminalName = System.getenv("TERM_PROGRAM");
|
||||
return ("Apple_Terminal".equalsIgnoreCase(terminalName) || Boolean.getBoolean("is.apple.terminal"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,240 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.core;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.shell.CommandLine;
|
||||
import org.springframework.shell.plugin.BannerProvider;
|
||||
import org.springframework.shell.plugin.HistoryFileNameProvider;
|
||||
import org.springframework.shell.plugin.PluginUtils;
|
||||
import org.springframework.shell.plugin.PromptProvider;
|
||||
|
||||
/**
|
||||
* Launcher for {@link JLineShell}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @since 1.1
|
||||
*/
|
||||
public class JLineShellComponent extends JLineShell implements SmartLifecycle, ApplicationContextAware, InitializingBean {
|
||||
|
||||
@Autowired
|
||||
private CommandLine commandLine;
|
||||
|
||||
private volatile boolean running = false;
|
||||
private Thread shellThread;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
private boolean printBanner = true;
|
||||
|
||||
private String historyFileName;
|
||||
private String promptText;
|
||||
private String productName;
|
||||
private String banner;
|
||||
private String version;
|
||||
private String welcomeMessage;
|
||||
|
||||
private ExecutionStrategy executionStrategy = new SimpleExecutionStrategy();
|
||||
private SimpleParser parser = new SimpleParser();
|
||||
|
||||
public SimpleParser getSimpleParser() {
|
||||
return parser;
|
||||
}
|
||||
|
||||
public boolean isAutoStartup() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void stop(Runnable callback) {
|
||||
stop();
|
||||
callback.run();
|
||||
}
|
||||
|
||||
public int getPhase() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
//customizePlug must run before start thread to take plugin's configuration into effect
|
||||
customizePlugin();
|
||||
shellThread = new Thread(this, "Spring Shell");
|
||||
shellThread.start();
|
||||
running = true;
|
||||
}
|
||||
|
||||
|
||||
public void stop() {
|
||||
if (running) {
|
||||
closeShell();
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
Map<String, CommandMarker> commands = BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, CommandMarker.class);
|
||||
for (CommandMarker command : commands.values()) {
|
||||
getSimpleParser().add(command);
|
||||
}
|
||||
|
||||
Map<String, Converter> converters = BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, Converter.class);
|
||||
for (Converter<?> converter : converters.values()) {
|
||||
getSimpleParser().add(converter);
|
||||
}
|
||||
|
||||
setHistorySize(commandLine.getHistorySize());
|
||||
if (commandLine.getShellCommandsToExecute() != null) {
|
||||
setPrintBanner(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wait the shell command to complete by typing "quit" or "exit"
|
||||
*
|
||||
*/
|
||||
public void waitForComplete() {
|
||||
try {
|
||||
shellThread.join();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ExecutionStrategy getExecutionStrategy() {
|
||||
return executionStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Parser getParser() {
|
||||
return parser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStartupNotifications() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
public void customizePlugin() {
|
||||
this.historyFileName = getHistoryFileName();
|
||||
this.promptText = getPromptText();
|
||||
String[] banner = getBannerText();
|
||||
this.banner = banner[0];
|
||||
this.welcomeMessage = banner[1];
|
||||
this.version = banner[2];
|
||||
this.productName = banner[3];
|
||||
}
|
||||
|
||||
/**
|
||||
* get history file name from provider. The provider has highest order
|
||||
* <link>org.springframework.core.Ordered.getOder</link> will win.
|
||||
*
|
||||
* @return history file name
|
||||
*/
|
||||
protected String getHistoryFileName() {
|
||||
HistoryFileNameProvider historyFileNameProvider = PluginUtils.getHighestPriorityProvider(this.applicationContext,HistoryFileNameProvider.class);
|
||||
String providerHistoryFileName = historyFileNameProvider.getHistoryFileName();
|
||||
if (providerHistoryFileName != null) {
|
||||
return providerHistoryFileName;
|
||||
} else {
|
||||
return historyFileName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get prompt text from provider. The provider has highest order
|
||||
* <link>org.springframework.core.Ordered.getOder</link> will win.
|
||||
*
|
||||
* @return prompt text
|
||||
*/
|
||||
protected String getPromptText() {
|
||||
PromptProvider promptProvider = PluginUtils.getHighestPriorityProvider(this.applicationContext,PromptProvider.class);
|
||||
String providerPromptText = promptProvider.getPrompt();
|
||||
if (providerPromptText != null) {
|
||||
return providerPromptText;
|
||||
} else {
|
||||
return promptText;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Banner and Welcome Message from provider. The provider has highest order
|
||||
* <link>org.springframework.core.Ordered.getOder</link> will win.
|
||||
* @return BannerText[0]: Banner
|
||||
* BannerText[1]: Welcome Message
|
||||
* BannerText[2]: Version
|
||||
* BannerText[3]: Product Name
|
||||
*/
|
||||
private String[] getBannerText() {
|
||||
String[] bannerText = new String[4];
|
||||
BannerProvider provider = PluginUtils.getHighestPriorityProvider(this.applicationContext,BannerProvider.class);
|
||||
bannerText[0] = provider.getBanner();
|
||||
bannerText[1] = provider.getWelcomeMessage();
|
||||
bannerText[2] = provider.getVersion();
|
||||
bannerText[3] = provider.getProviderName();
|
||||
return bannerText;
|
||||
}
|
||||
|
||||
|
||||
public void printBannerAndWelcome() {
|
||||
if (printBanner) {
|
||||
logger.info(this.banner);
|
||||
logger.info(getWelcomeMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get the welcome message at start.
|
||||
*
|
||||
* @return welcome message
|
||||
*/
|
||||
public String getWelcomeMessage() {
|
||||
return this.welcomeMessage;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param printBanner the printBanner to set
|
||||
*/
|
||||
public void setPrintBanner(boolean printBanner) {
|
||||
this.printBanner = printBanner;
|
||||
}
|
||||
|
||||
protected String getProductName() {
|
||||
return productName;
|
||||
}
|
||||
|
||||
protected String getVersion() {
|
||||
return version;
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.core;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A method that can be executed via a shell command.
|
||||
* <p>
|
||||
* Immutable since 1.2.0.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public class MethodTarget {
|
||||
|
||||
// Fields
|
||||
private final Method method;
|
||||
|
||||
private final Object target;
|
||||
|
||||
private final String remainingBuffer;
|
||||
|
||||
private final String key;
|
||||
|
||||
/**
|
||||
* Constructor for a <code>null remainingBuffer</code> and <code>key</code>
|
||||
*
|
||||
* @param method the method to invoke (required)
|
||||
* @param target the object on which the method is to be invoked (required)
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public MethodTarget(final Method method, final Object target) {
|
||||
this(method, target, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor that allows all fields to be set
|
||||
*
|
||||
* @param method the method to invoke (required)
|
||||
* @param target the object on which the method is to be invoked (required)
|
||||
* @param remainingBuffer can be blank
|
||||
* @param key can be blank
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public MethodTarget(final Method method, final Object target, final String remainingBuffer, final String key) {
|
||||
Assert.notNull(method, "Method is required");
|
||||
Assert.notNull(target, "Target is required");
|
||||
this.key = StringUtils.trimWhitespace(key);
|
||||
this.method = method;
|
||||
this.remainingBuffer = remainingBuffer;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (other == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof MethodTarget)) {
|
||||
return false;
|
||||
}
|
||||
final MethodTarget otherMethodTarget = (MethodTarget) other;
|
||||
return this.method.equals(otherMethodTarget.getMethod()) && this.target.equals(otherMethodTarget.getTarget());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return ObjectUtils.nullSafeHashCode(method) + ObjectUtils.nullSafeHashCode(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
final ToStringCreator tsc = new ToStringCreator(this);
|
||||
tsc.append("target", target);
|
||||
tsc.append("method", method);
|
||||
tsc.append("remainingBuffer", remainingBuffer);
|
||||
tsc.append("key", key);
|
||||
return tsc.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public String getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a non-<code>null</code> method
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public Method getMethod() {
|
||||
return this.method;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public String getRemainingBuffer() {
|
||||
return this.remainingBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a non-<code>null</code> Object
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public Object getTarget() {
|
||||
return this.target;
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.core;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.shell.event.ParseResult;
|
||||
|
||||
/**
|
||||
* Interface for {@link SimpleParser}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Alan Stewart
|
||||
* @since 1.0
|
||||
*/
|
||||
public interface Parser {
|
||||
|
||||
ParseResult parse(String buffer);
|
||||
|
||||
/**
|
||||
* Populates a list of completion candidates. This method is required for backward compatibility for STS versions up to 2.8.0.
|
||||
*
|
||||
* @param buffer
|
||||
* @param cursor
|
||||
* @param candidates
|
||||
* @return
|
||||
*/
|
||||
int complete(String buffer, int cursor, List<String> candidates);
|
||||
|
||||
/**
|
||||
* Populates a list of completion candidates.
|
||||
*
|
||||
* @param buffer
|
||||
* @param cursor
|
||||
* @param candidates
|
||||
* @return
|
||||
*/
|
||||
int completeAdvanced(String buffer, int cursor, List<Completion> candidates);
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jline.console.completer.Completer;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An implementation of JLine's {@link Completer} interface that delegates to a {@link Parser}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @since 1.0
|
||||
*/
|
||||
public class ParserCompleter implements Completer {
|
||||
|
||||
// Fields
|
||||
private final Parser parser;
|
||||
|
||||
public ParserCompleter(final Parser parser) {
|
||||
Assert.notNull(parser, "Parser required");
|
||||
this.parser = parser;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public int complete(final String buffer, final int cursor, final List candidates) {
|
||||
int result;
|
||||
try {
|
||||
JLineLogHandler.cancelRedrawProhibition();
|
||||
List<Completion> completions = new ArrayList<Completion>();
|
||||
result = parser.completeAdvanced(buffer, cursor, completions);
|
||||
for (Completion completion : completions) {
|
||||
candidates.add(completion.getValue());
|
||||
}
|
||||
}
|
||||
finally {
|
||||
JLineLogHandler.prohibitRedraw();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.core;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.springframework.shell.event.ShellStatusProvider;
|
||||
|
||||
/**
|
||||
* Specifies the contract for an interactive shell.
|
||||
*
|
||||
* <p>
|
||||
* Any interactive shell class which implements these methods can be launched by the roo-bootstrap mechanism.
|
||||
*
|
||||
* <p>
|
||||
* It is envisaged implementations will be provided for JLine initially, with possible implementations for
|
||||
* Eclipse in the future.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @since 1.0
|
||||
*/
|
||||
public interface Shell extends ShellStatusProvider, ShellPromptAccessor {
|
||||
|
||||
/**
|
||||
* The slot name to use with {@link #flash(Level, String, String)} if a caller wishes to modify the window title.
|
||||
* This may not be supported by all operating system shells. It is provided on a best-effort basis only.
|
||||
*/
|
||||
String WINDOW_TITLE_SLOT = "WINDOW_TITLE_SLOT";
|
||||
|
||||
/**
|
||||
* Presents a console prompt and allows the user to interact with the shell. The shell should not return
|
||||
* to the caller until the user has finished their session (by way of a "quit" or similar command).
|
||||
*/
|
||||
void promptLoop();
|
||||
|
||||
/**
|
||||
* @return null if no exit was requested, otherwise the last exit code indicated to the shell to use
|
||||
*/
|
||||
ExitShellRequest getExitShellRequest();
|
||||
|
||||
/**
|
||||
* Runs the specified command. Control will return to the caller after the command is run.
|
||||
*
|
||||
* @param line to execute (required)
|
||||
* @return true if the command was successful, false if there was an exception
|
||||
*/
|
||||
CommandResult executeCommand(String line);
|
||||
|
||||
/**
|
||||
* Indicates the shell should switch into a lower-level development mode. The exact meaning varies by
|
||||
* shell implementation.
|
||||
*
|
||||
* @param developmentMode true if development mode should be enabled, false otherwise
|
||||
*/
|
||||
void setDevelopmentMode(boolean developmentMode);
|
||||
|
||||
/**
|
||||
* Displays a progress notification to the user. This notification will ideally be displayed in a
|
||||
* consistent screen location by the shell implementation.
|
||||
*
|
||||
* <p>
|
||||
* An implementation may allow multiple messages to be displayed concurrently. So an implementation can
|
||||
* determine when a flash message replaces a previous flash message, callers should allocate a unique
|
||||
* "slot" name for their messages. It is suggested the class name of the caller be used. This way a
|
||||
* slot will be updated without conflicting with flash message sequences from other slots.
|
||||
*
|
||||
* <p>
|
||||
* Passing an empty string in as the "message" indicates the slot should be cleared.
|
||||
*
|
||||
* <p>
|
||||
* An implementation need not necessarily use the level or slot concepts. They are expected to be
|
||||
* used in most cases, though.
|
||||
*
|
||||
* @param level the importance of the message (cannot be null)
|
||||
* @param message to display (cannot be null, but may be empty)
|
||||
* @param slot the identification slot for the message (cannot be null or empty)
|
||||
*/
|
||||
void flash(Level level, String message, String slot);
|
||||
|
||||
boolean isDevelopmentMode();
|
||||
|
||||
/**
|
||||
* Changes the "path" displayed in the shell prompt. An implementation will ensure this path is
|
||||
* included on the screen, taking care to merge it with the product name and handle any special
|
||||
* formatting requirements (such as ANSI, if supported by the implementation).
|
||||
*
|
||||
* @param path to set (can be null or empty; must NOT be formatted in any special way eg ANSI codes)
|
||||
*/
|
||||
void setPromptPath(String path);
|
||||
|
||||
void setPromptPath(String path, boolean overrideStyle);
|
||||
|
||||
/**
|
||||
* Returns the home directory of the current running shell instance
|
||||
*
|
||||
* @return the home directory of the current shell instance
|
||||
*/
|
||||
File getHome();
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.core;
|
||||
|
||||
|
||||
/**
|
||||
* Obtains the prompt used by a {@link Shell}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @since 1.0
|
||||
*/
|
||||
public interface ShellPromptAccessor {
|
||||
|
||||
/**
|
||||
* @return the shell prompt (never null; the result may include special characters such as ANSI
|
||||
* escape codes if the implementation is using them)
|
||||
*/
|
||||
String getShellPrompt();
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.core;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.springframework.shell.event.ParseResult;
|
||||
import org.springframework.shell.support.logging.HandlerUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Simple execution strategy for invoking a target method.
|
||||
* Supports pre/post processing to allow {@link CommandMarker}s for aop-like behavior (
|
||||
* typically used for controlling stateful objects).
|
||||
*
|
||||
* @author Mark Pollack
|
||||
* @author Costin Leau
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class SimpleExecutionStrategy implements ExecutionStrategy {
|
||||
|
||||
private static final Logger logger = HandlerUtils.getLogger(SimpleExecutionStrategy.class);
|
||||
|
||||
private final Class<?> mutex = SimpleExecutionStrategy.class;
|
||||
|
||||
public Object execute(ParseResult parseResult) throws RuntimeException {
|
||||
Assert.notNull(parseResult, "Parse result required");
|
||||
synchronized (mutex) {
|
||||
Assert.isTrue(isReadyForCommands(), "SimpleExecutionStrategy not yet ready for commands");
|
||||
Object target = parseResult.getInstance();
|
||||
if (target instanceof ExecutionProcessor) {
|
||||
ExecutionProcessor processor = ((ExecutionProcessor) target);
|
||||
parseResult = processor.beforeInvocation(parseResult);
|
||||
try {
|
||||
Object result = invoke(parseResult);
|
||||
processor.afterReturningInvocation(parseResult, result);
|
||||
return result;
|
||||
} catch (Throwable th) {
|
||||
processor.afterThrowingInvocation(parseResult, th);
|
||||
return handleThrowable(th);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return invoke(parseResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Object invoke(ParseResult parseResult) {
|
||||
try {
|
||||
Method method = parseResult.getMethod();
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
return ReflectionUtils.invokeMethod(method, parseResult.getInstance(), parseResult.getArguments());
|
||||
} catch (Throwable th) {
|
||||
logger.severe("Command failed " + th);
|
||||
return handleThrowable(th);
|
||||
}
|
||||
}
|
||||
|
||||
private Object handleThrowable(Throwable th) {
|
||||
if (th instanceof Error) {
|
||||
throw ((Error) th);
|
||||
}
|
||||
if (th instanceof RuntimeException) {
|
||||
throw ((RuntimeException) th);
|
||||
}
|
||||
throw new RuntimeException(th);
|
||||
}
|
||||
|
||||
public boolean isReadyForCommands() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void terminate() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,355 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.shell.core;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Converts a particular buffer into a tokenized structure.
|
||||
*
|
||||
* <p>
|
||||
* Properly treats double quotes (") as option delimiters.
|
||||
*
|
||||
* <p>
|
||||
* Expects option names to be preceded by a double dash. We call this an "option marker".
|
||||
*
|
||||
* <p>
|
||||
* Treats spaces as the default option tokenizer.
|
||||
*
|
||||
* <p>
|
||||
* Any token without an option marker is considered the default. The default is returned in the Map as an element with
|
||||
* an empty string key (""). There can only be a single default.
|
||||
*
|
||||
* @author Eric Bottard
|
||||
* @since 1.1
|
||||
*/
|
||||
public class Tokenizer {
|
||||
|
||||
private static final char ESCAPE_CHAR = '\\';
|
||||
|
||||
private final char[] buffer;
|
||||
|
||||
private int pos = 0;
|
||||
|
||||
private final Map<String, String> result = new LinkedHashMap<String, String>();
|
||||
|
||||
/** Useful when trying to do auto complete. */
|
||||
private boolean allowUnbalancedLastQuotedValue;
|
||||
|
||||
/**
|
||||
* Used to indicate that the last value was indeed half enclosed in quotes. Useful so that parser can re-add it.
|
||||
*/
|
||||
private boolean openingQuotesHaveNotBeenClosed;
|
||||
|
||||
private char lastValueDelimiter;
|
||||
|
||||
private int lastValueStartOffset = -1;
|
||||
|
||||
public Tokenizer(String text) {
|
||||
this(text, false);
|
||||
}
|
||||
|
||||
public Tokenizer(String text, boolean allowUnbalancedLastQuotedValue) {
|
||||
this.buffer = text.toCharArray();
|
||||
this.allowUnbalancedLastQuotedValue = allowUnbalancedLastQuotedValue;
|
||||
tokenize();
|
||||
}
|
||||
|
||||
private void eatWhiteSpace() {
|
||||
while (lookAhead(' ')) {
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
public void tokenize() {
|
||||
while (pos < buffer.length) {
|
||||
eatWhiteSpace();
|
||||
if (pos < buffer.length) {
|
||||
eatKeyValuePair();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, String> getTokens() {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the remaining buffer matches the given String (return false if there is not enough input).
|
||||
*/
|
||||
private boolean lookAhead(char... toMatch) {
|
||||
for (int i = 0; i < toMatch.length; i++) {
|
||||
if (pos + i >= buffer.length || buffer[pos + i] != toMatch[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume either {@code --key[=value]} or just {@code value}, eating extra spaces.
|
||||
*/
|
||||
private void eatKeyValuePair() {
|
||||
if (lookAhead('-', '-')) {
|
||||
pos += 2;
|
||||
eatKeyEqualsValue();
|
||||
}
|
||||
else {
|
||||
int offsetInCaseOfFailure = pos;
|
||||
String value = eatValue(true);
|
||||
store("", value, offsetInCaseOfFailure);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a command key/value pair, reporting a failure if a mapping with the same key is already present.
|
||||
* @param key the command key
|
||||
* @param value the command value
|
||||
* @param failureOffset the buffer offset at which the mapping we're trying to store was tokenized
|
||||
*/
|
||||
private void store(String key, String value, int failureOffset) {
|
||||
String alreadyThere = result.put(key, value);
|
||||
if (alreadyThere != null) {
|
||||
if ("".equals(key)) {
|
||||
String explanation = String.format(
|
||||
"You cannot specify '%s' as another value for the default ('') option in a single command.%n"
|
||||
+ "You already provided '%s' earlier.%n"
|
||||
+ "Did you forget to add quotes around the value of another option?", value,
|
||||
alreadyThere);
|
||||
throw new TokenizingException(failureOffset, buffer, explanation);
|
||||
}
|
||||
else {
|
||||
String explanation = String.format(
|
||||
"You cannot specify '%s' as another value for the '--%s' option in a single command.%n"
|
||||
+ "You already provided '%s' earlier.", value, key, alreadyThere);
|
||||
throw new TokenizingException(failureOffset, buffer, explanation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Eat a value that may be enclosed in some delimiters.
|
||||
* @param emptyKey if true, we're currently reading the value for the empty key
|
||||
*/
|
||||
private String eatValue(boolean emptyKey) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
char endDelimiter = ' ';
|
||||
if (buffer[pos] == '"' || buffer[pos] == '\'') {
|
||||
endDelimiter = buffer[pos];
|
||||
pos++;
|
||||
}
|
||||
// So that it can be retrieved later (if this is actually the last value)
|
||||
lastValueDelimiter = endDelimiter;
|
||||
lastValueStartOffset = pos;
|
||||
while (pos < buffer.length && buffer[pos] != endDelimiter) {
|
||||
if (buffer[pos] == ESCAPE_CHAR) {
|
||||
sb.append(processCharacterEscapeCodes(endDelimiter));
|
||||
continue;
|
||||
}
|
||||
if (lookAhead(ESCAPE_CHAR, endDelimiter)) {
|
||||
sb.append(endDelimiter);
|
||||
pos += 2;
|
||||
continue;
|
||||
}
|
||||
sb.append(buffer[pos]);
|
||||
pos++;
|
||||
}
|
||||
// If we're grabbing the key-less value, allow additional chunks, as long as
|
||||
// 1) we don't hit '--'
|
||||
// 2) we were not using a quote delimited value
|
||||
if (emptyKey && endDelimiter == ' ') {
|
||||
while (!lookAhead('-', '-') && pos < buffer.length) {
|
||||
sb.append(buffer[pos++]);
|
||||
}
|
||||
// Trim to the right
|
||||
while (Character.isWhitespace(sb.charAt(sb.length() - 1))) {
|
||||
sb.setLength(sb.length() - 1);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
// When here, we either ran out of input, or encountered our delim, or both
|
||||
// Fail, unless we allow an unfinished quoted string to be reported
|
||||
if (endDelimiter != ' ' && // we're using quotes
|
||||
pos == buffer.length && // we ran of input
|
||||
(buffer[pos - 1] != endDelimiter || // quotes are not properly closed
|
||||
sb.length() == 0)) { // BUT it's ok if consumed nothing (pos-1 is *opening* quote then)
|
||||
if (allowUnbalancedLastQuotedValue) {
|
||||
openingQuotesHaveNotBeenClosed = true;
|
||||
return sb.toString();
|
||||
}
|
||||
else {
|
||||
throw new TokenizingException(pos, buffer, "Cannot have an unbalanced number of quotation marks");
|
||||
}
|
||||
}
|
||||
// Eat our delim
|
||||
pos++;
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* When the escape character is encountered, consume and return the escaped sequence. Note that depending on which
|
||||
* end delimiter is currently in use, not all combinations need to be escaped
|
||||
* @param endDelimiter the current endDelimiter
|
||||
*/
|
||||
private char processCharacterEscapeCodes(char endDelimiter) {
|
||||
pos++;
|
||||
if (pos >= buffer.length) {
|
||||
throw new TokenizingException(buffer.length, buffer, "Ran out of input in escape sequence");
|
||||
}
|
||||
switch (buffer[pos]) {
|
||||
case ESCAPE_CHAR:
|
||||
pos++; // consume the second escape char
|
||||
return ESCAPE_CHAR;
|
||||
case 't':
|
||||
pos++;
|
||||
return '\t';
|
||||
case 'r':
|
||||
pos++;
|
||||
return '\r';
|
||||
case 'n':
|
||||
pos++;
|
||||
return '\n';
|
||||
case 'f':
|
||||
pos++;
|
||||
return '\f';
|
||||
case 'u':
|
||||
if (pos + 5 > buffer.length) {
|
||||
throw new TokenizingException(buffer.length, buffer, "Ran out of input in unicode escape sequence");
|
||||
}
|
||||
String hex = new String(buffer, pos + 1, 4);
|
||||
try {
|
||||
char code = (char) Integer.parseInt(hex, 16);
|
||||
pos += 5;
|
||||
return code;
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
throw new TokenizingException(pos - 1, buffer, "Illegal unicode escape sequence: " + ESCAPE_CHAR + "u"
|
||||
+ hex);
|
||||
}
|
||||
|
||||
default:
|
||||
if (buffer[pos] == endDelimiter) {
|
||||
pos++;
|
||||
return endDelimiter;
|
||||
}
|
||||
else {
|
||||
// Not an actual escape. Do not increment pos,
|
||||
// and return the \ we consumed at the very beginning
|
||||
return ESCAPE_CHAR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the offset at which the last value seen started (NOT including any delimiter).
|
||||
*/
|
||||
public int getLastValueStartOffset() {
|
||||
Assert.isTrue(lastValueStartOffset >= 0, "lastValueStartOffset has not been set yet");
|
||||
return lastValueStartOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the delimiter (space or quotes) that was (or is being) used for the last value.
|
||||
*/
|
||||
public char getLastValueDelimiter() {
|
||||
Assert.isTrue(lastValueDelimiter != 0, "lastValueDelimiter has not been set yet");
|
||||
return lastValueDelimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the last value was meant to be enclosed in quotes, but the closing quote has not been typed yet.
|
||||
*/
|
||||
public boolean openingQuotesHaveNotBeenClosed() {
|
||||
return openingQuotesHaveNotBeenClosed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if we know for sure that the last value has been typed in full.
|
||||
*/
|
||||
public boolean lastValueIsComplete() {
|
||||
// If using quotes as delim and they're not closed, we know for sure.
|
||||
// Moreover if we're using space as the delimiter, we can't know.
|
||||
return !openingQuotesHaveNotBeenClosed && lastValueDelimiter != ' ';
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply delimiter escaping to the given string, using the actual delimiter that was used for the last value.
|
||||
*/
|
||||
public String escape(String value) {
|
||||
String result = value.replace("" + lastValueDelimiter, "" + ESCAPE_CHAR + lastValueDelimiter);
|
||||
result = result.replace("\r", "\\r");
|
||||
result = result.replace("\n", "\\n");
|
||||
result = result.replace("\t", "\\t");
|
||||
result = result.replace("\f", "\\f");
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume a full {@code --key value} pair *unless*
|
||||
* <ul>
|
||||
* <li>we're at the very end,</li>
|
||||
* <li>or the next token starts with {@code --}</li>
|
||||
* </ul>
|
||||
* in which case allow for just {@code --key}, using "" for the value.
|
||||
*/
|
||||
private void eatKeyEqualsValue() {
|
||||
// We already consumed '--'
|
||||
int offsetInCaseOfFailure = pos - 2;
|
||||
String key = eatKey();
|
||||
eatWhiteSpace();
|
||||
String value;
|
||||
// We're at the very end or it's a valueless option
|
||||
// Make last* fields consistent
|
||||
if (pos >= buffer.length || lookAhead('-', '-')) {
|
||||
lastValueDelimiter = ' ';
|
||||
lastValueStartOffset = pos;
|
||||
value = "";
|
||||
}
|
||||
else {
|
||||
value = eatValue(false);
|
||||
}
|
||||
// Don't store the ""="" that would result from having a pending " --" at the end
|
||||
if (key.equals("") && value.equals("")) {
|
||||
return;
|
||||
}
|
||||
store(key, value, offsetInCaseOfFailure);
|
||||
}
|
||||
|
||||
private String eatKey() {
|
||||
int start = pos;
|
||||
while (pos < buffer.length && buffer[pos] != ' ') {
|
||||
pos++;
|
||||
}
|
||||
return new String(buffer, start, pos - start);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder result = new StringBuilder().append(buffer).append('\n');
|
||||
for (int i = 0; i < lastValueStartOffset; i++) {
|
||||
result.append(' ');
|
||||
}
|
||||
result.append('^');
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.shell.core;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class TokenizingException extends RuntimeException {
|
||||
|
||||
private final int offendingOffset;
|
||||
|
||||
private final char[] buffer;
|
||||
|
||||
private final String reason;
|
||||
|
||||
public TokenizingException(int offendingOffset, char[] buffer, String reason) {
|
||||
super();
|
||||
this.offendingOffset = offendingOffset;
|
||||
this.buffer = buffer;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public int getOffendingOffset() {
|
||||
return offendingOffset;
|
||||
}
|
||||
|
||||
public String getBuffer() {
|
||||
return new String(buffer);
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.core.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
/**
|
||||
* Annotates a method that can indicate whether a particular command is presently
|
||||
* available or not.
|
||||
*
|
||||
* <p>
|
||||
* This annotation must only be applied to a public no-argument method that returns primitive boolean.
|
||||
* The method should be inexpensive to evaluate, as this method can be called very
|
||||
* frequently. If expensive operations are necessary to compute command availability,
|
||||
* it is suggested the method return a boolean field that is maintained using the observer
|
||||
* pattern.
|
||||
*
|
||||
* <p>
|
||||
* It is possible that a particular availability method might be able to represent the
|
||||
* availability status of multiple commands. As such, an availability indicator annotation
|
||||
* will indicate the commands that it applies to. If a specific command has multiple
|
||||
* aliases (ie by using an array for {@link CliCommand#value()}), only one of the commands
|
||||
* need to be specified in the {@link CliAvailabilityIndicator} annotation.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @since 1.0
|
||||
*/
|
||||
@Inherited
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface CliAvailabilityIndicator {
|
||||
|
||||
/**
|
||||
* @return the name of the command or commands that this availability indicator represents
|
||||
*/
|
||||
String[] value();
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.core.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
*
|
||||
* Annotates a method that provides a command to the shell.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @since 1.0
|
||||
*
|
||||
*/
|
||||
@Inherited
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface CliCommand {
|
||||
|
||||
/**
|
||||
* @return one or more strings which must serve as the start of a particular command in order to match this method
|
||||
* (these must be unique within the entire application; if not unique, behaviour is not specified)
|
||||
*/
|
||||
String[] value();
|
||||
|
||||
/**
|
||||
* @return a help message for this command (the default is a blank String, which means there is no help)
|
||||
*/
|
||||
String help() default "";
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.core.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.shell.core.Converter;
|
||||
|
||||
/**
|
||||
* Annotates the arguments of a command methods, allowing it to declare the argument value as mandatory or optional with a default value.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @since 1.0
|
||||
*
|
||||
*/
|
||||
@Inherited
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
public @interface CliOption {
|
||||
|
||||
/**
|
||||
* @return if true, the user cannot specify this option and it is provided by the shell infrastructure
|
||||
* (defaults to false)
|
||||
*/
|
||||
boolean systemProvided() default false;
|
||||
|
||||
/**
|
||||
* @return the name of the option, which must be unique within this {@link CliCommand} (an empty String may
|
||||
* be given, which would denote this option is the default for the command)
|
||||
*/
|
||||
String[] key();
|
||||
|
||||
/**
|
||||
* @return true if this option must be specified one way or the other by the user (defaults to false)
|
||||
*/
|
||||
boolean mandatory() default false;
|
||||
|
||||
/**
|
||||
* @return the default value to use if this option is unspecified by the user (defaults to __NULL__, which causes null to
|
||||
* be presented to any non-primitive parameter)
|
||||
*/
|
||||
String unspecifiedDefaultValue() default "__NULL__";
|
||||
|
||||
/**
|
||||
* @return the default value to use if this option is included by the user, but they didn't specify an
|
||||
* actual value (most commonly used for flags; defaults to __NULL__, which causes null to
|
||||
* be presented to any non-primitive parameter)
|
||||
*/
|
||||
String specifiedDefaultValue() default "__NULL__";
|
||||
|
||||
/**
|
||||
* Returns a string providing context-specific information (e.g. a comma-delimited
|
||||
* set of keywords) to the {@link Converter} that handles the annotated parameter's type.
|
||||
* <p>
|
||||
* For example, if a method parameter "thing" of type "Thing" is annotated as
|
||||
* follows:
|
||||
* <pre>@CliOption(..., optionContext = "foo,bar", ...) Thing thing</pre>
|
||||
* ... then the {@link Converter} that converts the text entered by the user
|
||||
* into an instance of Thing will be passed "foo,bar" as the value of the
|
||||
* <code>optionContext</code> parameter in its public methods. This allows
|
||||
* the behaviour of that Converter to be individually customised for each
|
||||
* {@link CliOption} of each {@link CliCommand}.
|
||||
*
|
||||
* @return a non-<code>null</code> string (can be empty)
|
||||
*/
|
||||
String optionContext() default "";
|
||||
|
||||
/**
|
||||
* @return a help message for this option (the default is a blank String, which means there is no help)
|
||||
*/
|
||||
String help() default "";
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.event;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
import org.springframework.shell.event.ShellStatus.Status;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Provides a convenience superclass for those shells wishing to publish status messages.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @since 1.0
|
||||
*/
|
||||
public abstract class AbstractShellStatusPublisher implements ShellStatusProvider {
|
||||
|
||||
// Fields
|
||||
protected Set<ShellStatusListener> shellStatusListeners = new CopyOnWriteArraySet<ShellStatusListener>();
|
||||
protected ShellStatus shellStatus = new ShellStatus(Status.STARTING);
|
||||
|
||||
public final void addShellStatusListener(final ShellStatusListener shellStatusListener) {
|
||||
Assert.notNull(shellStatusListener, "Status listener required");
|
||||
synchronized (shellStatus) {
|
||||
shellStatusListeners.add(shellStatusListener);
|
||||
}
|
||||
}
|
||||
|
||||
public final void removeShellStatusListener(final ShellStatusListener shellStatusListener) {
|
||||
Assert.notNull(shellStatusListener, "Status listener required");
|
||||
synchronized (shellStatus) {
|
||||
shellStatusListeners.remove(shellStatusListener);
|
||||
}
|
||||
}
|
||||
|
||||
public final ShellStatus getShellStatus() {
|
||||
synchronized (shellStatus) {
|
||||
return shellStatus;
|
||||
}
|
||||
}
|
||||
|
||||
protected void setShellStatus(final Status shellStatus) {
|
||||
setShellStatus(shellStatus, null, null);
|
||||
}
|
||||
|
||||
protected void setShellStatus(final Status shellStatus, final String msg, final ParseResult parseResult) {
|
||||
Assert.notNull(shellStatus, "Shell status required");
|
||||
|
||||
synchronized (this.shellStatus) {
|
||||
ShellStatus st;
|
||||
if (msg == null || msg.length() == 0) {
|
||||
st = new ShellStatus(shellStatus);
|
||||
} else {
|
||||
st = new ShellStatus(shellStatus, msg, parseResult);
|
||||
}
|
||||
|
||||
if (this.shellStatus.equals(st)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (ShellStatusListener listener : shellStatusListeners) {
|
||||
listener.onShellStatusChange(this.shellStatus, st);
|
||||
}
|
||||
this.shellStatus = st;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.event;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.shell.core.Converter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Immutable representation of the outcome of parsing a given shell line.
|
||||
*
|
||||
* <p>
|
||||
* Note that contained objects (the instance and the arguments) may be mutable, as the shell infrastructure
|
||||
* has no way of restricting which methods can be the target of CLI commands and nor the arguments
|
||||
* they will accept via the {@link Converter} infrastructure.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @since 1.0
|
||||
*/
|
||||
public class ParseResult {
|
||||
|
||||
// Fields
|
||||
private final Method method;
|
||||
private final Object instance;
|
||||
private final Object[] arguments; // May be null if no arguments needed
|
||||
|
||||
public ParseResult(final Method method, final Object instance, final Object[] arguments) {
|
||||
Assert.notNull(method, "Method required");
|
||||
Assert.notNull(instance, "Instance required");
|
||||
int length = arguments == null ? 0 : arguments.length;
|
||||
Assert.isTrue(method.getParameterTypes().length == length, "Required " + method.getParameterTypes().length + " arguments, but received " + length);
|
||||
this.method = method;
|
||||
this.instance = instance;
|
||||
this.arguments = arguments;
|
||||
}
|
||||
|
||||
public Method getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public Object getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public Object[] getArguments() {
|
||||
return arguments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + Arrays.hashCode(arguments);
|
||||
result = prime * result + ((instance == null) ? 0 : instance.hashCode());
|
||||
result = prime * result + ((method == null) ? 0 : method.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
ParseResult other = (ParseResult) obj;
|
||||
if (!Arrays.equals(arguments, other.arguments))
|
||||
return false;
|
||||
if (instance == null) {
|
||||
if (other.instance != null)
|
||||
return false;
|
||||
} else if (!instance.equals(other.instance))
|
||||
return false;
|
||||
if (method == null) {
|
||||
if (other.method != null)
|
||||
return false;
|
||||
} else if (!method.equals(other.method))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
ToStringCreator tsc = new ToStringCreator(this);
|
||||
tsc.append("method", method);
|
||||
tsc.append("instance", instance);
|
||||
tsc.append("arguments", StringUtils.arrayToCommaDelimitedString(arguments));
|
||||
return tsc.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.event;
|
||||
|
||||
|
||||
/**
|
||||
* Represents the different states that a shell can legally be in.
|
||||
*
|
||||
* <p>
|
||||
* There is no "shut down" state because the shell would have been terminated by
|
||||
* that stage and potentially garbage collected. There is no guarantee that a
|
||||
* shell implementation will necessarily publish every state.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Stefan Schmidt
|
||||
* @since 1.0
|
||||
*/
|
||||
public class ShellStatus {
|
||||
|
||||
// Fields
|
||||
private final Status status;
|
||||
private String message = "";
|
||||
private ParseResult parseResult;
|
||||
|
||||
public enum Status {
|
||||
STARTING,
|
||||
STARTED,
|
||||
USER_INPUT,
|
||||
PARSING,
|
||||
EXECUTING,
|
||||
EXECUTION_RESULT_PROCESSING,
|
||||
EXECUTION_SUCCESS,
|
||||
EXECUTION_FAILED,
|
||||
SHUTTING_DOWN
|
||||
}
|
||||
|
||||
ShellStatus(final Status status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
ShellStatus(final Status status, final String msg, final ParseResult parseResult) {
|
||||
this.status = status;
|
||||
this.message = msg;
|
||||
this.parseResult = parseResult;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public final ParseResult getParseResult() {
|
||||
return parseResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((message == null) ? 0 : message.hashCode());
|
||||
result = prime * result
|
||||
+ ((parseResult == null) ? 0 : parseResult.hashCode());
|
||||
result = prime * result + ((status == null) ? 0 : status.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
ShellStatus other = (ShellStatus) obj;
|
||||
if (message == null) {
|
||||
if (other.message != null)
|
||||
return false;
|
||||
} else if (!message.equals(other.message))
|
||||
return false;
|
||||
if (parseResult == null) {
|
||||
if (other.parseResult != null)
|
||||
return false;
|
||||
} else if (!parseResult.equals(other.parseResult))
|
||||
return false;
|
||||
if (status == null) {
|
||||
if (other.status != null)
|
||||
return false;
|
||||
} else if (!status.equals(other.status))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.event;
|
||||
|
||||
/**
|
||||
* Implemented by classes that wish to be notified of shell status changes.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @since 1.0
|
||||
*/
|
||||
public interface ShellStatusListener {
|
||||
|
||||
/**
|
||||
* Invoked by the shell to report a new status.
|
||||
*
|
||||
* @param oldStatus the old status
|
||||
* @param newStatus the new status
|
||||
*/
|
||||
void onShellStatusChange(ShellStatus oldStatus, ShellStatus newStatus);
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.event;
|
||||
|
||||
/**
|
||||
* Implemented by shells that support the publication of shell status changes.
|
||||
*
|
||||
* <p>
|
||||
* Implementations are not required to provide any guarantees with respect to the order
|
||||
* in which notifications are delivered to listeners.
|
||||
*
|
||||
* <p>
|
||||
* Implementations must permit modification of the listener list, even while delivering
|
||||
* event notifications to listeners. However, listeners do not receive any guarantee that
|
||||
* their addition or removal from the listener list will be effective or not for any event
|
||||
* notification that is currently proceeding.
|
||||
*
|
||||
* <p>
|
||||
* Implementations must ensure that status notifications are only delivered when an actual
|
||||
* change has taken place.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @since 1.0
|
||||
*/
|
||||
public interface ShellStatusProvider {
|
||||
|
||||
/**
|
||||
* Registers a new status listener.
|
||||
*
|
||||
* @param shellStatusListener to register (cannot be null)
|
||||
*/
|
||||
void addShellStatusListener(ShellStatusListener shellStatusListener);
|
||||
|
||||
/**
|
||||
* Removes an existing status listener.
|
||||
*
|
||||
* <p>
|
||||
* If the presented status listener is not found, the method returns without exception.
|
||||
*
|
||||
* @param shellStatusListener to remove (cannot be null)
|
||||
*/
|
||||
void removeShellStatusListener(ShellStatusListener shellStatusListener);
|
||||
|
||||
/**
|
||||
* Returns the current shell status.
|
||||
*
|
||||
* @return the current status (never null)
|
||||
*/
|
||||
ShellStatus getShellStatus();
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.plugin;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Banner provider. Plugins should implement this interface to replace the version banner.
|
||||
* Use the @Order annotation to specify the priority of the banner to be display, higher
|
||||
* values can be interpreted as lower priority
|
||||
*
|
||||
* @author Jarred Li
|
||||
* @since 1.0
|
||||
*
|
||||
*/
|
||||
public interface BannerProvider extends NamedProvider {
|
||||
|
||||
/**
|
||||
* Returns the banner.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getBanner();
|
||||
|
||||
/**
|
||||
* Returns the associated version.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getVersion();
|
||||
|
||||
/**
|
||||
* Returns the welcome message.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getWelcomeMessage();
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.plugin;
|
||||
|
||||
|
||||
/**
|
||||
* History file name provider.
|
||||
* Plugin should implement this interface to customize history file.
|
||||
* <code>getOrder</code> indicate the priority, higher values can be interpreted as lower priority
|
||||
*
|
||||
* @author Jarred Li
|
||||
* @since 1.0
|
||||
*
|
||||
*/
|
||||
public interface HistoryFileNameProvider extends NamedProvider {
|
||||
|
||||
/**
|
||||
* get history file name
|
||||
*
|
||||
* @return history file name
|
||||
*/
|
||||
String getHistoryFileName();
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.plugin;
|
||||
|
||||
/**
|
||||
* Returns the name of the provider. Providers customize features of the shell such as the banner and command line prompt.
|
||||
*
|
||||
*
|
||||
* @author Jarred Li
|
||||
* @author Mark Pollack
|
||||
* @see BannerProvider
|
||||
* @see PromptProvider
|
||||
* @see HistoryFileNameProvider
|
||||
*/
|
||||
public interface NamedProvider {
|
||||
|
||||
/**
|
||||
* Return the name of the provider.
|
||||
*/
|
||||
String getProviderName();
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.plugin;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
|
||||
/**
|
||||
* Utilities dealing with shell plugins.
|
||||
*
|
||||
* @author Erwin Vervaet
|
||||
*/
|
||||
public final class PluginUtils {
|
||||
|
||||
private PluginUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the highest priority {@link PluginProvider} of specified type defined in
|
||||
* given application context.
|
||||
*
|
||||
* @since 1.0.1
|
||||
*/
|
||||
public static <T extends NamedProvider> T getHighestPriorityProvider(ApplicationContext applicationContext, Class<T> t) {
|
||||
Map<String, T> providers = BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, t);
|
||||
List<T> sortedProviders = new ArrayList<T>(providers.values());
|
||||
Collections.sort(sortedProviders, new AnnotationAwareOrderComparator());
|
||||
T highestPriorityProvider = sortedProviders.get(0);
|
||||
return highestPriorityProvider;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.plugin;
|
||||
|
||||
|
||||
/**
|
||||
* Shell prompt provider.
|
||||
* Plugins should implement this interface to customize prompt.
|
||||
* <code>getOrder</code> indicate the priority, higher values can be interpreted as lower priority
|
||||
*
|
||||
* @author Jarred Li
|
||||
*
|
||||
*/
|
||||
public interface PromptProvider extends NamedProvider {
|
||||
|
||||
/**
|
||||
* Returns the prompt text.
|
||||
*
|
||||
* @return prompt
|
||||
*/
|
||||
String getPrompt();
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.plugin.support;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.shell.plugin.BannerProvider;
|
||||
import org.springframework.shell.support.util.FileUtils;
|
||||
import org.springframework.shell.support.util.OsUtils;
|
||||
import org.springframework.shell.support.util.VersionUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Default Banner provider.
|
||||
*
|
||||
* @author Jarred Li
|
||||
* @author Costin Leau
|
||||
*/
|
||||
@Component
|
||||
@Order(Ordered.LOWEST_PRECEDENCE)
|
||||
public class DefaultBannerProvider implements BannerProvider {
|
||||
|
||||
public String getBanner() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(FileUtils.readBanner(DefaultBannerProvider.class, "banner.txt"));
|
||||
sb.append(getVersion()).append(OsUtils.LINE_SEPARATOR);
|
||||
sb.append(OsUtils.LINE_SEPARATOR);
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
public String getVersion() {
|
||||
return VersionUtils.versionInfo();
|
||||
}
|
||||
|
||||
public String getWelcomeMessage() {
|
||||
return "Welcome to " + getProviderName() + ".";
|
||||
}
|
||||
|
||||
public String getProviderName() {
|
||||
return "Spring Shell";
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.plugin.support;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.shell.plugin.HistoryFileNameProvider;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Default history file provider. Default file is {@link org.springframework.shell.Constant.HISTORY_FILE_NAME}
|
||||
*
|
||||
* @author Jarred Li
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
@Order(Ordered.LOWEST_PRECEDENCE)
|
||||
public class DefaultHistoryFileNameProvider implements HistoryFileNameProvider {
|
||||
|
||||
public String getHistoryFileName() {
|
||||
return "spring-shell.log";
|
||||
}
|
||||
|
||||
public String getProviderName() {
|
||||
return "default history provider";
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.plugin.support;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.shell.plugin.PromptProvider;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Default prompt provider. The prompt text is {@link org.springframework.shell.Constant.COMMAND_LINE_PROMPT}
|
||||
*
|
||||
* @author Jarred Li
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
@Order(Ordered.LOWEST_PRECEDENCE)
|
||||
public class DefaultPromptProvider implements PromptProvider {
|
||||
|
||||
public String getPrompt() {
|
||||
return "spring-shell>";
|
||||
}
|
||||
|
||||
public String getProviderName() {
|
||||
return "default prompt provider";
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.support.logging;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Defers the publication of JDK {@link LogRecord} instances until a target {@link Handler} is registered.
|
||||
*
|
||||
* <p>
|
||||
* This class is useful if a target {@link Handler} cannot be instantiated before {@link LogRecord} instances are being
|
||||
* published. This may be the case if the target {@link Handler} requires the establishment of complex publication
|
||||
* infrastructure such as a GUI, message queue, IoC container and the establishment of that infrastructure may produce
|
||||
* log messages that should ultimately be delivered to the target {@link Handler}.
|
||||
*
|
||||
* <p>
|
||||
* In recognition that sometimes the target {@link Handler} may never be registered (perhaps due to failures configuring
|
||||
* its supporting infrastructure), this class supports a fallback mode. When in fallback mode, a fallback {@link Handler}
|
||||
* will receive all previous and future {@link LogRecord} instances. Fallback mode is automatically triggered if a
|
||||
* {@link LogRecord} is published at the fallback {@link Level}. Fallback mode is also triggered if the {@link #flush()}
|
||||
* or {@link #close()} method is involved and the target {@link Handler} has never been registered.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @since 1.0
|
||||
*/
|
||||
public class DeferredLogHandler extends Handler {
|
||||
|
||||
// Fields
|
||||
private final List<LogRecord> logRecords = Collections.synchronizedList(new ArrayList<LogRecord>());
|
||||
private final Handler fallbackHandler;
|
||||
private final Level fallbackPushLevel;
|
||||
private boolean fallbackMode = false;
|
||||
private Handler targetHandler;
|
||||
|
||||
/**
|
||||
* Creates an instance that will publish all recorded {@link LogRecord} instances to the specified fallback
|
||||
* {@link Handler} if an event of the specified {@link Level} is received.
|
||||
*
|
||||
* @param fallbackHandler to publish events to (mandatory)
|
||||
* @param fallbackPushLevel the level which will trigger an event publication (mandatory)
|
||||
*/
|
||||
public DeferredLogHandler(final Handler fallbackHandler, final Level fallbackPushLevel) {
|
||||
Assert.notNull(fallbackHandler, "Fallback handler required");
|
||||
Assert.notNull(fallbackPushLevel, "Fallback push level required");
|
||||
this.fallbackHandler = fallbackHandler;
|
||||
this.fallbackPushLevel = fallbackPushLevel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws SecurityException {
|
||||
if (targetHandler == null) {
|
||||
fallbackMode = true;
|
||||
}
|
||||
if (fallbackMode) {
|
||||
publishLogRecordsTo(fallbackHandler);
|
||||
fallbackHandler.close();
|
||||
return;
|
||||
}
|
||||
targetHandler.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
if (targetHandler == null) {
|
||||
fallbackMode = true;
|
||||
}
|
||||
if (fallbackMode) {
|
||||
publishLogRecordsTo(fallbackHandler);
|
||||
fallbackHandler.flush();
|
||||
return;
|
||||
}
|
||||
targetHandler.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the log record internally.
|
||||
*/
|
||||
@Override
|
||||
public void publish(final LogRecord record) {
|
||||
if (!isLoggable(record)) {
|
||||
return;
|
||||
}
|
||||
if (fallbackMode) {
|
||||
fallbackHandler.publish(record);
|
||||
return;
|
||||
}
|
||||
if (targetHandler != null) {
|
||||
targetHandler.publish(record);
|
||||
return;
|
||||
}
|
||||
synchronized (logRecords) {
|
||||
logRecords.add(record);
|
||||
}
|
||||
if (!fallbackMode && record.getLevel().intValue() >= fallbackPushLevel.intValue()) {
|
||||
fallbackMode = true;
|
||||
publishLogRecordsTo(fallbackHandler);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the target {@link Handler}, or null if there is no target {@link Handler} defined so far
|
||||
*/
|
||||
public Handler getTargetHandler() {
|
||||
return targetHandler;
|
||||
}
|
||||
|
||||
public void setTargetHandler(final Handler targetHandler) {
|
||||
Assert.notNull(targetHandler, "Must specify a target handler");
|
||||
this.targetHandler = targetHandler;
|
||||
if (!fallbackMode) {
|
||||
publishLogRecordsTo(this.targetHandler);
|
||||
}
|
||||
}
|
||||
|
||||
private void publishLogRecordsTo(final Handler destination) {
|
||||
synchronized (logRecords) {
|
||||
for (LogRecord record : logRecords) {
|
||||
destination.publish(record);
|
||||
}
|
||||
logRecords.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.support.logging;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.ConsoleHandler;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.springframework.shell.support.util.OsUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Utility methods for dealing with {@link Handler} objects.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @since 1.0
|
||||
*
|
||||
*/
|
||||
public abstract class HandlerUtils {
|
||||
|
||||
/**
|
||||
* Obtains a {@link Logger} that guarantees to set the {@link Level}
|
||||
* to {@link Level#FINE} if it is part of org.springframework.roo.
|
||||
* Unfortunately this is needed due to a regression in JDK 1.6.0_18
|
||||
* as per issue ROO-539.
|
||||
*
|
||||
* @param clazz to retrieve the logger for (required)
|
||||
* @return the logger, which will at least of {@link Level#FINE} if no level was specified
|
||||
*/
|
||||
public static Logger getLogger(final Class<?> clazz) {
|
||||
Assert.notNull(clazz, "Class required");
|
||||
String name = clazz.getName();
|
||||
Logger logger = Logger.getLogger(name);
|
||||
if (logger.getLevel() == null && name.startsWith("org.springframework.shell")) {
|
||||
logger.setLevel(Level.FINE);
|
||||
}
|
||||
return logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces each {@link Handler} defined against the presented {@link Logger} with {@link DeferredLogHandler}.
|
||||
*
|
||||
* <p>
|
||||
* This is useful for ensuring any {@link Handler} defaults defined by the user are preserved and treated as the
|
||||
* {@link DeferredLogHandler} "fallback" {@link Handler} if the indicated severity {@link Level} is encountered.
|
||||
*
|
||||
* <p>
|
||||
* This method will create a {@link ConsoleHandler} if the presented {@link Logger} has no current {@link Handler}.
|
||||
*
|
||||
* @param logger to introspect and replace the {@link Handler}s for (required)
|
||||
* @param fallbackSeverity to trigger fallback mode (required)
|
||||
* @return the number of {@link DeferredLogHandler}s now registered against the {@link Logger} (guaranteed to be 1 or above)
|
||||
*/
|
||||
public static int wrapWithDeferredLogHandler(final Logger logger, final Level fallbackSeverity) {
|
||||
Assert.notNull(logger, "Logger is required");
|
||||
Assert.notNull(fallbackSeverity, "Fallback severity is required");
|
||||
|
||||
List<DeferredLogHandler> newHandlers = new ArrayList<DeferredLogHandler>();
|
||||
|
||||
// Create DeferredLogHandlers for each Handler in presented Logger
|
||||
Handler[] handlers = logger.getHandlers();
|
||||
if (handlers != null && handlers.length > 0) {
|
||||
for (Handler h : handlers) {
|
||||
logger.removeHandler(h);
|
||||
newHandlers.add(new DeferredLogHandler(h, fallbackSeverity));
|
||||
}
|
||||
}
|
||||
|
||||
// Create a default DeferredLogHandler if no Handler was defined in the presented Logger
|
||||
if (newHandlers.isEmpty()) {
|
||||
ConsoleHandler consoleHandler = new ConsoleHandler();
|
||||
consoleHandler.setFormatter(new Formatter() {
|
||||
@Override
|
||||
public String format(final LogRecord record) {
|
||||
return record.getMessage() + OsUtils.LINE_SEPARATOR;
|
||||
}
|
||||
});
|
||||
newHandlers.add(new DeferredLogHandler(consoleHandler, fallbackSeverity));
|
||||
}
|
||||
|
||||
// Add the new DeferredLogHandlers to the presented Logger
|
||||
for (DeferredLogHandler h : newHandlers) {
|
||||
logger.addHandler(h);
|
||||
}
|
||||
|
||||
return newHandlers.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the presented target {@link Handler} against any {@link DeferredLogHandler} encountered in the presented
|
||||
* {@link Logger}.
|
||||
*
|
||||
* <p>
|
||||
* Generally this method is used on {@link Logger} instances that have previously been presented to the
|
||||
* {@link #wrapWithDeferredLogHandler(Logger, Level)} method.
|
||||
*
|
||||
* <p>
|
||||
* The method will return a count of how many {@link DeferredLogHandler} instances it detected. Note that no
|
||||
* attempt is made to distinguish between instances already possessing the intended target {@link Handler}
|
||||
* or those already possessing any target {@link Handler} at all. This method always overwrites the target
|
||||
* {@link Handler} and the returned count represents how many overwrites took place.
|
||||
*
|
||||
* @param logger to introspect for {@link DeferredLogHandler} instances (required)
|
||||
* @param target to set as the target {@link Handler}
|
||||
* @return number of {@link DeferredLogHandler} instances detected and updated (may be 0 if none found)
|
||||
*/
|
||||
public static int registerTargetHandler(final Logger logger, final Handler target) {
|
||||
Assert.notNull(logger, "Logger is required");
|
||||
Assert.notNull(target, "Target handler is required");
|
||||
|
||||
int replaced = 0;
|
||||
Handler[] handlers = logger.getHandlers();
|
||||
if (handlers != null && handlers.length > 0) {
|
||||
for (Handler h : handlers) {
|
||||
if (h instanceof DeferredLogHandler) {
|
||||
replaced++;
|
||||
DeferredLogHandler defLogger = (DeferredLogHandler) h;
|
||||
defLogger.setTargetHandler(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return replaced;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces all {@link Handler} instances registered in the presented {@link Logger} to be flushed.
|
||||
*
|
||||
* @param logger to flush (required)
|
||||
* @return the number of {@link Handler}s flushed (may be 0 or above)
|
||||
*/
|
||||
public static int flushAllHandlers(final Logger logger) {
|
||||
Assert.notNull(logger, "Logger is required");
|
||||
|
||||
int flushed = 0;
|
||||
Handler[] handlers = logger.getHandlers();
|
||||
if (handlers != null && handlers.length > 0) {
|
||||
for (Handler h : handlers) {
|
||||
flushed++;
|
||||
h.flush();
|
||||
}
|
||||
}
|
||||
|
||||
return flushed;
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.support.logging;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.springframework.shell.support.util.IOUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Wraps an {@link OutputStream} and automatically passes each line to the {@link Logger}
|
||||
* when {@link OutputStream#flush()} or {@link OutputStream#close()} is called.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @since 1.1
|
||||
*/
|
||||
public class LoggingOutputStream extends OutputStream {
|
||||
|
||||
// Constants
|
||||
protected static final Logger LOGGER = HandlerUtils.getLogger(LoggingOutputStream.class);
|
||||
|
||||
// Fields
|
||||
private final Level level;
|
||||
private String sourceClassName = LoggingOutputStream.class.getName();
|
||||
private int count;
|
||||
private ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param level the level at which to log (required)
|
||||
*/
|
||||
public LoggingOutputStream(final Level level) {
|
||||
Assert.notNull(level, "A logging level is required");
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(final int b) throws IOException {
|
||||
baos.write(b);
|
||||
count++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
if (count > 0) {
|
||||
String msg = new String(baos.toByteArray());
|
||||
LogRecord record = new LogRecord(level, msg);
|
||||
record.setSourceClassName(sourceClassName);
|
||||
try {
|
||||
LOGGER.log(record);
|
||||
} finally {
|
||||
count = 0;
|
||||
IOUtils.closeQuietly(baos);
|
||||
baos = new ByteArrayOutputStream();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
flush();
|
||||
}
|
||||
|
||||
public String getSourceClassName() {
|
||||
return sourceClassName;
|
||||
}
|
||||
|
||||
public void setSourceClassName(final String sourceClassName) {
|
||||
this.sourceClassName = sourceClassName;
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.support.logging;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.springframework.shell.support.util.IOUtils;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
/**
|
||||
* Retrieves text files from the classloader and displays them on-screen.
|
||||
*
|
||||
* <p>
|
||||
* Respects normal Roo conventions such as all resources should appear under the same
|
||||
* package as the bundle itself etc.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @since 1.1.1
|
||||
*/
|
||||
public abstract class MessageDisplayUtils {
|
||||
|
||||
// Constants
|
||||
private static Logger LOGGER = HandlerUtils.getLogger(MessageDisplayUtils.class);
|
||||
|
||||
/**
|
||||
* Displays the requested file via the LOGGER API.
|
||||
*
|
||||
* <p>
|
||||
* Each file must available from the classloader of the "owner". It must also be in the same
|
||||
* package as the class of the "owner". So if the owner is com.foo.Bar, and the file is called
|
||||
* "hello.txt", the file must appear in the same bundle as com.foo.Bar and be available from
|
||||
* the resource path "/com/foo/Hello.txt".
|
||||
*
|
||||
* @param fileName the simple filename (required)
|
||||
* @param owner the class which owns the file (required)
|
||||
* @param important if true, it will display with a higher importance color where possible
|
||||
*/
|
||||
public static void displayFile(final String fileName, final Class<?> owner, final boolean important) {
|
||||
Level level = important ? Level.SEVERE : Level.FINE;
|
||||
String owningPackage = owner.getPackage().getName().replace('.', '/');
|
||||
String fullResourceName = "/" + owningPackage + "/" + fileName;
|
||||
InputStream inputStream = owner.getClassLoader().getResourceAsStream(fullResourceName);
|
||||
if (inputStream == null) {
|
||||
throw new IllegalStateException("Could not locate '" + fileName + "'");
|
||||
}
|
||||
try {
|
||||
String message = FileCopyUtils.copyToString(new InputStreamReader(new BufferedInputStream(inputStream)));
|
||||
LOGGER.log(level, message);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(inputStream);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link #displayFile(String, Class, boolean)} except it passes false as the
|
||||
* final argument.
|
||||
*
|
||||
* @param fileName the simple filename (required)
|
||||
* @param owner the class which owns the file (required)
|
||||
*/
|
||||
public static void displayFile(final String fileName, final Class<?> owner) {
|
||||
displayFile(fileName, owner, false);
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
/*
|
||||
* Copyright 2009-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.shell.support.table;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* Provide a basic concept of a table structure containing a map of column
|
||||
* headers and a collection of rows. Used to render text-based tables (console
|
||||
* output).
|
||||
*
|
||||
* @see TableRenderer
|
||||
*
|
||||
* @author Gunnar Hillert
|
||||
* @deprecated In favor of {@link org.springframework.shell.table.TableBuilder}
|
||||
*/
|
||||
public class Table {
|
||||
|
||||
private final Map<Integer, TableHeader> headers = new TreeMap<Integer, TableHeader>();
|
||||
|
||||
private volatile List<TableRow> rows = new ArrayList<TableRow>(0);
|
||||
|
||||
public List<TableRow> getRows() {
|
||||
return rows;
|
||||
}
|
||||
|
||||
public Map<Integer, TableHeader> getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
public Table addHeader(Integer columnIndex, TableHeader tableHeader) {
|
||||
this.headers.put(columnIndex, tableHeader);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new empty row to the table.
|
||||
*
|
||||
* @return the newly created row, which can be then be populated
|
||||
*/
|
||||
public TableRow newRow() {
|
||||
TableRow row = new TableRow();
|
||||
rows.add(row);
|
||||
return row;
|
||||
}
|
||||
|
||||
public Table addRow(String... values) {
|
||||
|
||||
final TableRow row = new TableRow();
|
||||
|
||||
int column = 1;
|
||||
|
||||
for (String value : values) {
|
||||
row.addValue(column, value);
|
||||
column++;
|
||||
}
|
||||
|
||||
rows.add(row);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public void calculateColumnWidths() {
|
||||
for (java.util.Map.Entry<Integer, TableHeader> headerEntry : headers
|
||||
.entrySet()) {
|
||||
final Integer headerEntryKey = headerEntry.getKey();
|
||||
for (TableRow tableRow : rows) {
|
||||
headerEntry.getValue().updateWidth(
|
||||
tableRow.getValue(headerEntryKey).length());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return TableRenderer.renderTextTable(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
calculateColumnWidths();
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((headers == null) ? 0 : headers.hashCode());
|
||||
result = prime * result + ((rows == null) ? 0 : rows.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
|
||||
Table other = (Table) obj;
|
||||
this.calculateColumnWidths();
|
||||
other.calculateColumnWidths();
|
||||
if (headers == null) {
|
||||
if (other.headers != null)
|
||||
return false;
|
||||
} else if (!headers.equals(other.headers))
|
||||
return false;
|
||||
if (rows == null) {
|
||||
if (other.rows != null)
|
||||
return false;
|
||||
} else if (!rows.equals(other.rows))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
/*
|
||||
* Copyright 2009-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.shell.support.table;
|
||||
|
||||
/**
|
||||
* Defines table column headers used by {@link Table}.
|
||||
*
|
||||
* @see TableRenderer
|
||||
*
|
||||
* @author Gunnar Hillert
|
||||
* @deprecated In favor of {@link org.springframework.shell.table.TableBuilder}
|
||||
*
|
||||
*/
|
||||
public class TableHeader {
|
||||
|
||||
private int maxWidth = -1;
|
||||
|
||||
private int width = 0;
|
||||
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* Constructor that initializes the table header with the provided header
|
||||
* name and the with of the table header.
|
||||
*
|
||||
* @param name
|
||||
* @param width
|
||||
*/
|
||||
public TableHeader(String name, int width) {
|
||||
|
||||
super();
|
||||
this.width = width;
|
||||
this.name = name;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor that initializes the table header with the provided header
|
||||
* name. The with of the table header is calculated and assigned based on
|
||||
* the provided header name.
|
||||
*
|
||||
* @param name
|
||||
*/
|
||||
public TableHeader(String name) {
|
||||
super();
|
||||
this.name = name;
|
||||
|
||||
if (name == null) {
|
||||
this.width = 0;
|
||||
} else {
|
||||
this.width = name.length();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public void setWidth(int width) {
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updated the width for this particular column, but only if the value of
|
||||
* the passed-in width is higher than the value of the pre-existing width.
|
||||
*
|
||||
* @param width
|
||||
*/
|
||||
public void updateWidth(int width) {
|
||||
if (this.width < width) {
|
||||
if (this.maxWidth > 0 && this.maxWidth < width) {
|
||||
this.width = this.maxWidth;
|
||||
} else {
|
||||
this.width = width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getMaxWidth() {
|
||||
return maxWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defaults to -1 indicating to ignore the property.
|
||||
*
|
||||
* @param maxWidth
|
||||
* If negative or zero this property will be ignored.
|
||||
*/
|
||||
public void setMaxWidth(int maxWidth) {
|
||||
this.maxWidth = maxWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + maxWidth;
|
||||
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||
result = prime * result + width;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
TableHeader other = (TableHeader) obj;
|
||||
if (maxWidth != other.maxWidth)
|
||||
return false;
|
||||
if (name == null) {
|
||||
if (other.name != null)
|
||||
return false;
|
||||
} else if (!name.equals(other.name))
|
||||
return false;
|
||||
if (width != other.width)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,274 +0,0 @@
|
||||
/*
|
||||
* Copyright 2009-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.shell.support.table;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.springframework.shell.support.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Contains utility methods for rendering data to a formatted console output.
|
||||
* E.g. it provides helper methods for rendering ASCII-based data tables.
|
||||
*
|
||||
* @author Gunnar Hillert
|
||||
* @author Thomas Risberg
|
||||
* @deprecated In favor of {@link org.springframework.shell.table.TableBuilder}
|
||||
*
|
||||
*/
|
||||
public final class TableRenderer {
|
||||
|
||||
public static final String HORIZONTAL_LINE = "-------------------------------------------------------------------------------\n";
|
||||
|
||||
public static final int COLUMN_1 = 1;
|
||||
|
||||
public static final int COLUMN_2 = 2;
|
||||
|
||||
public static final int COLUMN_3 = 3;
|
||||
|
||||
public static final int COLUMN_4 = 4;
|
||||
|
||||
public static final int COLUMN_5 = 5;
|
||||
|
||||
public static final int COLUMN_6 = 6;
|
||||
|
||||
/**
|
||||
* Prevent instantiation.
|
||||
*
|
||||
*/
|
||||
private TableRenderer() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a textual representation of the list of provided Map data
|
||||
*
|
||||
* @param columns
|
||||
* List of Maps
|
||||
* @return The rendered table representation as String
|
||||
*
|
||||
*/
|
||||
public static String renderMapDataAsTable(List<Map<String, Object>> data,
|
||||
List<String> columns) {
|
||||
|
||||
Table table = new Table();
|
||||
|
||||
int col = 0;
|
||||
for (String colName : columns) {
|
||||
col++;
|
||||
table.getHeaders().put(col, new TableHeader(colName));
|
||||
if (col >= 6) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (Map<String, Object> dataRow : data) {
|
||||
|
||||
TableRow tableRow = new TableRow();
|
||||
|
||||
for (int i = 0; i < col; i++) {
|
||||
String value = dataRow.get(columns.get(i)).toString();
|
||||
table.getHeaders().get(i + 1).updateWidth(value.length());
|
||||
tableRow.addValue(i + 1, value);
|
||||
}
|
||||
|
||||
table.getRows().add(tableRow);
|
||||
}
|
||||
|
||||
return renderTextTable(table);
|
||||
}
|
||||
|
||||
public static String renderParameterInfoDataAsTable(
|
||||
Map<String, String> parameters, boolean withHeader,
|
||||
int lastColumnMaxWidth) {
|
||||
final Table table = new Table();
|
||||
|
||||
table.getHeaders().put(COLUMN_1, new TableHeader("Parameter"));
|
||||
|
||||
final TableHeader tableHeader2 = new TableHeader(
|
||||
"Value (Configured or Default)");
|
||||
tableHeader2.setMaxWidth(lastColumnMaxWidth);
|
||||
table.getHeaders().put(COLUMN_2, tableHeader2);
|
||||
|
||||
for (Entry<String, String> entry : parameters.entrySet()) {
|
||||
|
||||
final TableRow tableRow = new TableRow();
|
||||
|
||||
table.getHeaders().get(COLUMN_1)
|
||||
.updateWidth(entry.getKey().length());
|
||||
tableRow.addValue(COLUMN_1, entry.getKey());
|
||||
|
||||
int width = entry.getValue() != null ? entry.getValue().length()
|
||||
: 0;
|
||||
|
||||
table.getHeaders().get(COLUMN_2).updateWidth(width);
|
||||
tableRow.addValue(COLUMN_2, entry.getValue());
|
||||
|
||||
table.getRows().add(tableRow);
|
||||
}
|
||||
|
||||
return renderTextTable(table, withHeader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a textual representation of provided parameter map.
|
||||
*
|
||||
* @param parameters
|
||||
* Map of parameters (key, value)
|
||||
* @return The rendered table representation as String
|
||||
*
|
||||
*/
|
||||
public static String renderParameterInfoDataAsTable(
|
||||
Map<String, String> parameters) {
|
||||
return renderParameterInfoDataAsTable(parameters, true, -1);
|
||||
}
|
||||
|
||||
public static String renderTextTable(Table table) {
|
||||
return renderTextTable(table, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a textual representation of the provided {@link Table}
|
||||
*
|
||||
* @param table
|
||||
* Table data {@link Table}
|
||||
* @return The rendered table representation as String
|
||||
*/
|
||||
public static String renderTextTable(Table table, boolean withHeader) {
|
||||
|
||||
table.calculateColumnWidths();
|
||||
|
||||
final String padding = " ";
|
||||
final String headerBorder = getHeaderBorder(table.getHeaders());
|
||||
final StringBuilder textTable = new StringBuilder();
|
||||
|
||||
if (withHeader) {
|
||||
final StringBuilder headerline = new StringBuilder();
|
||||
for (TableHeader header : table.getHeaders().values()) {
|
||||
|
||||
if (header.getName().length() > header.getWidth()) {
|
||||
Iterable<String> chunks = split(header.getName(), header.getWidth());
|
||||
int length = headerline.length();
|
||||
boolean first = true;
|
||||
for (String chunk : chunks) {
|
||||
final String lineToAppend;
|
||||
if (first) {
|
||||
lineToAppend = padding
|
||||
+ StringUtils.padRight(chunk,
|
||||
header.getWidth());
|
||||
} else {
|
||||
lineToAppend = StringUtils.padLeft("", length)
|
||||
+ padding
|
||||
+ StringUtils.padRight(chunk,
|
||||
header.getWidth());
|
||||
}
|
||||
first = false;
|
||||
headerline.append(lineToAppend);
|
||||
headerline.append("\n");
|
||||
}
|
||||
headerline.deleteCharAt(headerline.lastIndexOf("\n"));
|
||||
} else {
|
||||
String lineToAppend = padding
|
||||
+ StringUtils.padRight(header.getName(),
|
||||
header.getWidth());
|
||||
headerline.append(lineToAppend);
|
||||
}
|
||||
}
|
||||
textTable.append(org.springframework.util.StringUtils
|
||||
.trimTrailingWhitespace(headerline.toString()));
|
||||
textTable.append("\n");
|
||||
}
|
||||
|
||||
textTable.append(headerBorder);
|
||||
|
||||
for (TableRow row : table.getRows()) {
|
||||
StringBuilder rowLine = new StringBuilder();
|
||||
for (Entry<Integer, TableHeader> entry : table.getHeaders()
|
||||
.entrySet()) {
|
||||
String value = row.getValue(entry.getKey());
|
||||
if (value.length() > entry.getValue().getWidth()) {
|
||||
Iterable<String> chunks = split(value, entry.getValue().getWidth());
|
||||
int length = rowLine.length();
|
||||
boolean first = true;
|
||||
for (String chunk : chunks) {
|
||||
final String lineToAppend;
|
||||
if (first) {
|
||||
lineToAppend = padding
|
||||
+ StringUtils.padRight(chunk, entry
|
||||
.getValue().getWidth());
|
||||
} else {
|
||||
lineToAppend = StringUtils.padLeft("", length)
|
||||
+ padding
|
||||
+ StringUtils.padRight(chunk, entry
|
||||
.getValue().getWidth());
|
||||
}
|
||||
first = false;
|
||||
rowLine.append(lineToAppend);
|
||||
rowLine.append("\n");
|
||||
}
|
||||
rowLine.deleteCharAt(rowLine.lastIndexOf("\n"));
|
||||
} else {
|
||||
String lineToAppend = padding
|
||||
+ StringUtils.padRight(value, entry.getValue()
|
||||
.getWidth());
|
||||
rowLine.append(lineToAppend);
|
||||
}
|
||||
}
|
||||
textTable.append(org.springframework.util.StringUtils
|
||||
.trimTrailingWhitespace(rowLine.toString()));
|
||||
textTable.append("\n");
|
||||
}
|
||||
|
||||
if (!withHeader) {
|
||||
textTable.append(headerBorder);
|
||||
}
|
||||
|
||||
return textTable.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the Table header border, based on the map of provided headers.
|
||||
*
|
||||
* @param headers
|
||||
* Map of headers containing meta information e.g. name+width of
|
||||
* header
|
||||
* @return Returns the rendered header border as String
|
||||
*/
|
||||
public static String getHeaderBorder(Map<Integer, TableHeader> headers) {
|
||||
|
||||
final StringBuilder headerBorder = new StringBuilder();
|
||||
|
||||
for (TableHeader header : headers.values()) {
|
||||
headerBorder.append(StringUtils.padRight(" ",
|
||||
header.getWidth() + 2, '-'));
|
||||
}
|
||||
headerBorder.append("\n");
|
||||
|
||||
return headerBorder.toString();
|
||||
}
|
||||
|
||||
private static List<String> split(String in, int length) {
|
||||
List<String> result = new ArrayList<String>(in.length() / length);
|
||||
for (int i = 0; i < in.length(); i += length) {
|
||||
result.add(in.substring(i, Math.min(in.length(), i + length)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright 2009-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.shell.support.table;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Holds the table rows used by {@link Table}.
|
||||
*
|
||||
* @see TableRenderer
|
||||
*
|
||||
* @author Gunnar Hillert
|
||||
* @author Ilayaperumal Gopinathan
|
||||
* @deprecated In favor of {@link org.springframework.shell.table.TableBuilder}
|
||||
*
|
||||
*/
|
||||
public class TableRow {
|
||||
|
||||
/** Holds the data for the column */
|
||||
private Map<Integer, String> data = new HashMap<Integer, String>();
|
||||
|
||||
public void setData(Map<Integer, String> data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a value from this row.
|
||||
*
|
||||
* @param key
|
||||
* Column for which to return the value for
|
||||
* @return Value of the specified column within this row
|
||||
*
|
||||
*/
|
||||
public String getValue(Integer key) {
|
||||
return data.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a value to the to the specified column within this row.
|
||||
*
|
||||
* @param column
|
||||
* @param value
|
||||
*/
|
||||
public TableRow addValue(Integer column, String value) {
|
||||
this.data.put(column, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((data == null) ? 0 : data.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
TableRow other = (TableRow) obj;
|
||||
if (data == null) {
|
||||
if (other.data != null)
|
||||
return false;
|
||||
} else if (!data.equals(other.data))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.support.util;
|
||||
|
||||
/**
|
||||
* ANSI escape codes supported by JLine
|
||||
*
|
||||
* @author Andrew Swan
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public enum AnsiEscapeCode {
|
||||
|
||||
// These int literals are non-public constants in ANSIBuffer.ANSICodes
|
||||
BLINK(5),
|
||||
BOLD(1),
|
||||
CONCEALED(8),
|
||||
FG_BLACK(30),
|
||||
FG_BLUE(34),
|
||||
FG_CYAN(36),
|
||||
FG_GREEN(32),
|
||||
FG_MAGENTA(35),
|
||||
FG_RED(31),
|
||||
FG_YELLOW(33),
|
||||
FG_WHITE(37),
|
||||
OFF(0),
|
||||
REVERSE(7),
|
||||
UNDERSCORE(4);
|
||||
|
||||
// Constant for the escape character
|
||||
private static final boolean ANSI_SUPPORTED = Boolean.getBoolean("roo.console.ansi");
|
||||
private static final char ESC = 27;
|
||||
|
||||
/**
|
||||
* Decorates the given text with the given escape codes (turning them off
|
||||
* afterwards)
|
||||
*
|
||||
* @param text the text to decorate; can be <code>null</code>
|
||||
* @param codes
|
||||
* @return <code>null</code> if <code>null</code> is passed
|
||||
*/
|
||||
public static String decorate(final String text, final AnsiEscapeCode... codes) {
|
||||
if (text == null || "".equals(text)) {
|
||||
return text;
|
||||
}
|
||||
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
if (ANSI_SUPPORTED) {
|
||||
for (final AnsiEscapeCode code : codes) {
|
||||
sb.append(code.code);
|
||||
}
|
||||
}
|
||||
sb.append(text);
|
||||
if (codes != null && codes.length > 0 && ANSI_SUPPORTED) {
|
||||
sb.append(OFF.code);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
// Fields
|
||||
final String code;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param code the numeric ANSI escape code
|
||||
*/
|
||||
private AnsiEscapeCode(final int code) {
|
||||
// Copied from the method ANSIBuffer.ANSICodes#attrib(int)
|
||||
this.code = ESC + "[" + code + "m";
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.support.util;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Methods for working with exceptions.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @since 1.0
|
||||
*/
|
||||
public abstract class ExceptionUtils {
|
||||
|
||||
/**
|
||||
* Obtains the root cause of an exception, if available.
|
||||
*
|
||||
* @param ex to extract the root cause from (required)
|
||||
* @return the root cause, or original exception is unavailable (guaranteed to never be null)
|
||||
*/
|
||||
public final static Throwable extractRootCause(final Throwable ex) {
|
||||
Assert.notNull(ex, "An exception is required");
|
||||
Throwable root = ex;
|
||||
if (ex.getCause() != null) {
|
||||
root = ex.getCause();
|
||||
}
|
||||
return root;
|
||||
}
|
||||
}
|
||||
@@ -1,406 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.support.util;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
||||
/**
|
||||
* Utilities for handling {@link File} instances.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @since 1.0
|
||||
*/
|
||||
public final class FileUtils {
|
||||
|
||||
// Constants
|
||||
private static final String BACKSLASH = "\\";
|
||||
private static final String ESCAPED_BACKSLASH = "\\\\";
|
||||
|
||||
/**
|
||||
* The relative file path to the current directory. Should be valid on all
|
||||
* platforms that Roo supports.
|
||||
*/
|
||||
public static final String CURRENT_DIRECTORY = ".";
|
||||
|
||||
private static final String WINDOWS_DRIVE_PREFIX = "^[A-Za-z]:";
|
||||
|
||||
// Doesn't check for backslash after the colon, since Java has no issues with paths like c:/Windows
|
||||
private static final Pattern WINDOWS_DRIVE_PATH = Pattern.compile(WINDOWS_DRIVE_PREFIX + ".*");
|
||||
|
||||
private static final PathMatcher PATH_MATCHER;
|
||||
|
||||
static {
|
||||
PATH_MATCHER = new AntPathMatcher();
|
||||
((AntPathMatcher) PATH_MATCHER).setPathSeparator(File.separator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the specified {@link File}.
|
||||
*
|
||||
* <p>
|
||||
* If the {@link File} refers to a directory, any contents of that directory (including other directories)
|
||||
* are also deleted.
|
||||
*
|
||||
* <p>
|
||||
* If the {@link File} does not already exist, this method immediately returns true.
|
||||
*
|
||||
* @param file to delete (required; the file may or may not exist)
|
||||
* @return true if the file is fully deleted, or false if there was a failure when deleting
|
||||
*/
|
||||
public static boolean deleteRecursively(final File file) {
|
||||
Assert.notNull(file, "File to delete required");
|
||||
if (!file.exists()) {
|
||||
return true;
|
||||
}
|
||||
if (file.isDirectory()) {
|
||||
for (File f : file.listFiles()) {
|
||||
if (!deleteRecursively(f)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
file.delete();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the specified source directory to the destination.
|
||||
*
|
||||
* <p>
|
||||
* Both the source must exist. If the destination does not already exist, it will be created. If the destination
|
||||
* does exist, it must be a directory (not a file).
|
||||
*
|
||||
* @param source the already-existing source directory (required)
|
||||
* @param destination the destination directory (required)
|
||||
* @param deleteDestinationOnExit indicates whether to mark any created destinations for deletion on exit
|
||||
* @return true if the copy was successful
|
||||
*/
|
||||
public static boolean copyRecursively(final File source, final File destination, final boolean deleteDestinationOnExit) {
|
||||
Assert.notNull(source, "Source directory required");
|
||||
Assert.notNull(destination, "Destination directory required");
|
||||
Assert.isTrue(source.exists(), "Source directory '" + source + "' must exist");
|
||||
Assert.isTrue(source.isDirectory(), "Source directory '" + source + "' must be a directory");
|
||||
if (destination.exists()) {
|
||||
Assert.isTrue(destination.isDirectory(), "Destination directory '" + destination + "' must be a directory");
|
||||
}
|
||||
else {
|
||||
destination.mkdirs();
|
||||
if (deleteDestinationOnExit) {
|
||||
destination.deleteOnExit();
|
||||
}
|
||||
}
|
||||
for (File s : source.listFiles()) {
|
||||
File d = new File(destination, s.getName());
|
||||
if (deleteDestinationOnExit) {
|
||||
d.deleteOnExit();
|
||||
}
|
||||
if (s.isFile()) {
|
||||
try {
|
||||
FileCopyUtils.copy(s, d);
|
||||
} catch (IOException ioe) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// It's a sub-directory, so copy it
|
||||
d.mkdir();
|
||||
if (!copyRecursively(s, d, deleteDestinationOnExit)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the provided fileName denotes an absolute path on the file system.
|
||||
* On Windows, this includes both paths with and without drive letters, where the latter have to start with '\'.
|
||||
* No check is performed to see if the file actually exists!
|
||||
*
|
||||
* @param fileName name of a file, which could be an absolute path
|
||||
* @return true if the fileName looks like an absolute path for the current OS
|
||||
*/
|
||||
public static boolean denotesAbsolutePath(final String fileName) {
|
||||
if (OsUtils.isWindows()) {
|
||||
// first check for drive letter
|
||||
if (WINDOWS_DRIVE_PATH.matcher(fileName).matches()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return fileName.startsWith(File.separator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the part of the given path that represents a directory, in other
|
||||
* words the given path if it's already a directory, or the parent directory
|
||||
* if it's a file.
|
||||
*
|
||||
* @param fileIdentifier the path to parse (required)
|
||||
* @return see above
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public static String getFirstDirectory(String fileIdentifier) {
|
||||
fileIdentifier = removeTrailingSeparator(fileIdentifier);
|
||||
if (new File(fileIdentifier).isDirectory()) {
|
||||
return fileIdentifier;
|
||||
}
|
||||
return backOneDirectory(fileIdentifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the given file system path minus its last element
|
||||
*
|
||||
* @param fileIdentifier
|
||||
* @return
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public static String backOneDirectory(String fileIdentifier) {
|
||||
fileIdentifier = removeTrailingSeparator(fileIdentifier);
|
||||
fileIdentifier = fileIdentifier.substring(0, fileIdentifier.lastIndexOf(File.separator));
|
||||
return removeTrailingSeparator(fileIdentifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes any trailing {@link File#separator}s from the given path
|
||||
*
|
||||
* @param path the path to modify (can be <code>null</code>)
|
||||
* @return the modified path
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public static String removeTrailingSeparator(String path) {
|
||||
while (path != null && path.endsWith(File.separator)) {
|
||||
path = path.substring(0, path.length() - File.separator.length());
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the given canonical path matches the given Ant-style pattern
|
||||
*
|
||||
* @param antPattern the pattern to check against (can't be blank)
|
||||
* @param canonicalPath the path to check (can't be blank)
|
||||
* @return see above
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public static boolean matchesAntPath(final String antPattern, final String canonicalPath) {
|
||||
Assert.hasText(antPattern, "Ant pattern required");
|
||||
Assert.hasText(canonicalPath, "Canonical path required");
|
||||
return PATH_MATCHER.match(antPattern, canonicalPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes any leading or trailing {@link File#separator}s from the given path.
|
||||
*
|
||||
* @param path the path to modify (can be <code>null</code>)
|
||||
* @return the path, modified as above, or <code>null</code> if <code>null</code> was given
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public static String removeLeadingAndTrailingSeparators(String path) {
|
||||
if (!StringUtils.hasText(path)) {
|
||||
return path;
|
||||
}
|
||||
while (path.endsWith(File.separator)) {
|
||||
path = path.substring(0, path.length() - File.separator.length());
|
||||
}
|
||||
while (path.startsWith(File.separator)) {
|
||||
path = path.substring(File.separator.length());
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the given path has exactly one trailing {@link File#separator}
|
||||
*
|
||||
* @param path the path to modify (can't be <code>null</code>)
|
||||
* @return the normalised path
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public static String ensureTrailingSeparator(final String path) {
|
||||
Assert.notNull(path);
|
||||
return removeTrailingSeparator(path) + File.separatorChar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an operating-system-dependent path consisting of the given
|
||||
* elements, separated by {@link File#separator}.
|
||||
*
|
||||
* @param pathElements the path elements from uppermost downwards (can't be empty)
|
||||
* @return a non-blank string
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public static String getSystemDependentPath(final String... pathElements) {
|
||||
return getSystemDependentPath(Arrays.asList(pathElements));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an operating-system-dependent path consisting of the given
|
||||
* elements, separated by {@link File#separator}.
|
||||
*
|
||||
* @param pathElements the path elements from uppermost downwards (can't be empty)
|
||||
* @return a non-blank string
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public static String getSystemDependentPath(final Collection<String> pathElements) {
|
||||
Assert.notEmpty(pathElements);
|
||||
return StringUtils.collectionToDelimitedString(pathElements, File.separator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the canonical path of the given {@link File}.
|
||||
*
|
||||
* @param file the file for which to find the canonical path (can be <code>null</code>)
|
||||
* @return the canonical path, or <code>null</code> if a <code>null</code> file is given
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public static String getCanonicalPath(final File file) {
|
||||
if (file == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return file.getCanonicalPath();
|
||||
} catch (final IOException ioe) {
|
||||
throw new IllegalStateException("Cannot determine canonical path for '" + file + "'", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the platform-specific file separator as a regular expression.
|
||||
*
|
||||
* @return a non-blank regex
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public static String getFileSeparatorAsRegex() {
|
||||
final String fileSeparator = File.separator;
|
||||
if (fileSeparator.contains(BACKSLASH)) {
|
||||
// Escape the backslashes
|
||||
return fileSeparator.replace(BACKSLASH, ESCAPED_BACKSLASH);
|
||||
}
|
||||
return fileSeparator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the path to the requested file, relative to the given class.
|
||||
*
|
||||
* @param loadingClass the class to whose package the given file is relative (required)
|
||||
* @param relativeFilename the name of the file relative to that package (required)
|
||||
* @return the full classloader-specific path to the file (never <code>null</code>)
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public static String getPath(final Class<?> loadingClass, final String relativeFilename) {
|
||||
Assert.notNull(loadingClass, "Loading class required");
|
||||
Assert.hasText(relativeFilename, "Filename required");
|
||||
Assert.isTrue(!relativeFilename.startsWith("/"), "Filename shouldn't start with a slash");
|
||||
// Slashes instead of File.separatorChar is correct here, as these are classloader paths (not file system paths)
|
||||
return "/" + loadingClass.getPackage().getName().replace('.', '/') + "/" + relativeFilename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given file from the classpath.
|
||||
*
|
||||
* @param loadingClass the class from whose package to load the file (required)
|
||||
* @param filename the name of the file to load, relative to that package (required)
|
||||
* @return the file's input stream (never <code>null</code>)
|
||||
* @throws IllegalArgumentException if the given file cannot be found
|
||||
*/
|
||||
public static File getFile(final Class<?> loadingClass, final String filename) {
|
||||
final URL url = loadingClass.getResource(filename);
|
||||
Assert.notNull(url, "Could not locate '" + filename + "' in classpath of " + loadingClass.getName());
|
||||
try {
|
||||
return new File(url.toURI());
|
||||
} catch (URISyntaxException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given file from the classpath.
|
||||
*
|
||||
* @param loadingClass the class from whose package to load the file (required)
|
||||
* @param filename the name of the file to load, relative to that package (required)
|
||||
* @return the file's input stream (never <code>null</code>)
|
||||
* @throws IllegalArgumentException if the given file cannot be found
|
||||
*/
|
||||
public static InputStream getInputStream(final Class<?> loadingClass, final String filename) {
|
||||
final InputStream inputStream = loadingClass.getResourceAsStream(filename);
|
||||
Assert.notNull(inputStream, "Could not locate '" + filename + "' in classpath of " + loadingClass.getName());
|
||||
return inputStream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a banner from the given resource. Performs conversion of any line separator contained by the source to that of the running platform.
|
||||
*
|
||||
* @return platform-compatible banner as a String
|
||||
*/
|
||||
public static String readBanner(Reader reader) {
|
||||
try {
|
||||
String content = FileCopyUtils.copyToString(new BufferedReader(reader));
|
||||
return content.replaceAll("(\\r|\\n)+", OsUtils.LINE_SEPARATOR);
|
||||
} catch (Exception ex) {
|
||||
throw new IllegalStateException("Cannot read stream", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a banner from the given resource. Performs conversion of any line separator contained by the source to that of the running platform.
|
||||
*
|
||||
* @return platform-compatible banner as a String
|
||||
*/
|
||||
public static String readBanner(final Class<?> loadingClass, String resourceName) {
|
||||
return readBanner(new InputStreamReader(getInputStream(loadingClass, resourceName)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the contents of the given File as a String.
|
||||
*
|
||||
* @param file the file to read from (must be an existing file)
|
||||
* @return the contents
|
||||
* @throws IllegalStateException in case of I/O errors
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public static String read(final File file) {
|
||||
try {
|
||||
return FileCopyUtils.copyToString(new FileReader(file));
|
||||
} catch (final IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor is private to prevent instantiation
|
||||
*
|
||||
* @since 1.2.0
|
||||
*/
|
||||
private FileUtils() {
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.support.util;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
/**
|
||||
* Static helper methods relating to I/O. Inspired by the eponymous class in
|
||||
* Apache Commons I/O.
|
||||
*
|
||||
* @author Andrew Swan
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public final class IOUtils {
|
||||
|
||||
/**
|
||||
* Quietly closes each of the given {@link Closeable}s, i.e. eats any
|
||||
* {@link IOException}s arising.
|
||||
*
|
||||
* @param closeables the closeables to close (any of which can be
|
||||
* <code>null</code> or already closed)
|
||||
*/
|
||||
public static void closeQuietly(final Closeable... closeables) {
|
||||
for (final Closeable closeable : closeables) {
|
||||
if (closeable != null) {
|
||||
try {
|
||||
closeable.close();
|
||||
} catch (IOException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Quietly closes each of the given {@link ZipFile}s, i.e. eats any
|
||||
* {@link IOException}s arising.
|
||||
*
|
||||
* @param zipFiles the zipFiles to close (any of which can be
|
||||
* <code>null</code> or already closed)
|
||||
*/
|
||||
public static void closeQuietly(final ZipFile... zipFiles) {
|
||||
for (final ZipFile zipFile : zipFiles) {
|
||||
if (zipFile != null) {
|
||||
try {
|
||||
zipFile.close();
|
||||
} catch (IOException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor is private to prevent instantiation
|
||||
*/
|
||||
private IOUtils() {}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.support.util;
|
||||
|
||||
/**
|
||||
* A class which contains a number of number manipulation operations
|
||||
*
|
||||
* @author James Tyrrell
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public class MathUtils {
|
||||
|
||||
public static double round(final double valueToRound, final int numberOfDecimalPlaces) {
|
||||
double multiplicationFactor = Math.pow(10, numberOfDecimalPlaces);
|
||||
double interestedInZeroDPs = valueToRound * multiplicationFactor;
|
||||
return Math.round(interestedInZeroDPs) / multiplicationFactor;
|
||||
}
|
||||
}
|
||||
@@ -1,193 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.support.util;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* NaturalOrderComparator.java -- Perform natural order comparisons of strings in Java.
|
||||
* Copyright (C) 2003 by Pierre-Luc Paour <natorder@paour.com>
|
||||
* Based on the C version by Martin Pool, of which this is more or less a straight conversion.
|
||||
* Copyright (C) 2000 by Martin Pool <mbp@humbug.org.au>
|
||||
*
|
||||
* This software is provided as-is, without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgement in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
public class NaturalOrderComparator<E> implements Comparator<E> {
|
||||
|
||||
/**
|
||||
* Returns the character at the given position of the given string;
|
||||
* equivalent to {@link String#charAt(int)}, but handles overly large
|
||||
* indices.
|
||||
*
|
||||
* @param s the string to read (can't be <code>null</code>)
|
||||
* @param i the index at which to read (zero-based)
|
||||
* @return 0 if the given index is beyond the end of the string
|
||||
*/
|
||||
static char charAt(final String s, final int i) {
|
||||
if (i >= s.length()) {
|
||||
return 0;
|
||||
}
|
||||
return s.charAt(i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the given character is whitespace
|
||||
*
|
||||
* @param c the character to check
|
||||
* @return see above
|
||||
*/
|
||||
public static boolean isSpace(final char c) {
|
||||
switch (c) {
|
||||
case ' ':
|
||||
return true;
|
||||
case '\n':
|
||||
return true;
|
||||
case '\t':
|
||||
return true;
|
||||
case '\f':
|
||||
return true;
|
||||
case '\r':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int compareRight(final String a, final String b) {
|
||||
int bias = 0;
|
||||
int ia = 0;
|
||||
int ib = 0;
|
||||
|
||||
// The longest run of digits wins. That aside, the greatest
|
||||
// value wins, but we can't know that it will until we've scanned
|
||||
// both numbers to know that they have the same magnitude, so we
|
||||
// remember it in BIAS.
|
||||
for (; ; ia++, ib++) {
|
||||
char ca = charAt(a, ia);
|
||||
char cb = charAt(b, ib);
|
||||
|
||||
if (!Character.isDigit(ca) && !Character.isDigit(cb)) {
|
||||
return bias;
|
||||
} else if (!Character.isDigit(ca)) {
|
||||
return -1;
|
||||
} else if (!Character.isDigit(cb)) {
|
||||
return +1;
|
||||
} else if (ca < cb) {
|
||||
if (bias == 0) {
|
||||
bias = -1;
|
||||
}
|
||||
} else if (ca > cb) {
|
||||
if (bias == 0)
|
||||
bias = +1;
|
||||
} else if (ca == 0 && cb == 0) {
|
||||
return bias;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected String stringify(final E object) {
|
||||
return object.toString();
|
||||
}
|
||||
|
||||
public int compare(final E o1, final E o2) {
|
||||
if (o1 == null && o2 == null) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (o1 == null) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (o2 == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
String a = stringify(o1);
|
||||
String b = stringify(o2);
|
||||
|
||||
int ia = 0, ib = 0;
|
||||
int nza = 0, nzb = 0;
|
||||
char ca, cb;
|
||||
int result;
|
||||
|
||||
while (true) {
|
||||
// Only count the number of zeroes leading the last number compared
|
||||
nza = nzb = 0;
|
||||
|
||||
ca = charAt(a, ia);
|
||||
cb = charAt(b, ib);
|
||||
|
||||
// Skip over leading spaces or zeros
|
||||
while (isSpace(ca) || ca == '0') {
|
||||
if (ca == '0') {
|
||||
nza++;
|
||||
} else {
|
||||
// Only count consecutive zeroes
|
||||
nza = 0;
|
||||
}
|
||||
|
||||
ca = charAt(a, ++ia);
|
||||
}
|
||||
|
||||
while (isSpace(cb) || cb == '0') {
|
||||
if (cb == '0') {
|
||||
nzb++;
|
||||
} else {
|
||||
// Only count consecutive zeroes
|
||||
nzb = 0;
|
||||
}
|
||||
|
||||
cb = charAt(b, ++ib);
|
||||
}
|
||||
|
||||
// Process run of digits
|
||||
if (Character.isDigit(ca) && Character.isDigit(cb)) {
|
||||
if ((result = compareRight(a.substring(ia), b.substring(ib))) != 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (ca == 0 && cb == 0) {
|
||||
// The strings compare the same. Perhaps the caller
|
||||
// will want to call strcmp to break the tie.
|
||||
return nza - nzb;
|
||||
}
|
||||
|
||||
if (ca < cb) {
|
||||
return -1;
|
||||
} else if (ca > cb) {
|
||||
return +1;
|
||||
}
|
||||
|
||||
++ia;
|
||||
++ib;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.support.util;
|
||||
|
||||
/**
|
||||
* Utilities for handling OS-specific behavior.
|
||||
*
|
||||
* @author Joris Kuipers
|
||||
* @since 1.1.1
|
||||
*/
|
||||
public class OsUtils {
|
||||
|
||||
public static final String LINE_SEPARATOR = System.getProperty("line.separator");
|
||||
|
||||
private static final boolean WINDOWS_OS = System.getProperty("os.name").toLowerCase().contains("windows");
|
||||
|
||||
public static boolean isWindows() {
|
||||
return WINDOWS_OS;
|
||||
}
|
||||
}
|
||||
@@ -1,279 +0,0 @@
|
||||
/*
|
||||
* Copyright 2009-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.support.util;
|
||||
|
||||
|
||||
/**
|
||||
* Utility methods for Strings focused on the use in formatting of tables. padLeft methods taken from
|
||||
* Commons Lang 2.6 to avoid an extra compile time dependency.
|
||||
*
|
||||
* @author Gunnar Hillert
|
||||
* @author Mark Pollack
|
||||
*
|
||||
*/
|
||||
public class StringUtils {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The maximum size to which the padding constant(s) can expand.
|
||||
* </p>
|
||||
*/
|
||||
private static final int PAD_LIMIT = 8192;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Left pad a String with spaces (' ').
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The String is padded to the size of <code>size</code>.
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.leftPad(null, *) = null
|
||||
* StringUtils.leftPad("", 3) = " "
|
||||
* StringUtils.leftPad("bat", 3) = "bat"
|
||||
* StringUtils.leftPad("bat", 5) = " bat"
|
||||
* StringUtils.leftPad("bat", 1) = "bat"
|
||||
* StringUtils.leftPad("bat", -1) = "bat"
|
||||
* </pre>
|
||||
*
|
||||
* @param str
|
||||
* the String to pad out, may be null
|
||||
* @param size
|
||||
* the size to pad to
|
||||
* @return left padded String or original String if no padding is necessary,
|
||||
* <code>null</code> if null String input
|
||||
*/
|
||||
public static String padLeft(String str, int size) {
|
||||
return padLeft(str, size, ' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Left pad a String with a specified character.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Pad to a size of <code>size</code>.
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.leftPad(null, *, *) = null
|
||||
* StringUtils.leftPad("", 3, 'z') = "zzz"
|
||||
* StringUtils.leftPad("bat", 3, 'z') = "bat"
|
||||
* StringUtils.leftPad("bat", 5, 'z') = "zzbat"
|
||||
* StringUtils.leftPad("bat", 1, 'z') = "bat"
|
||||
* StringUtils.leftPad("bat", -1, 'z') = "bat"
|
||||
* </pre>
|
||||
*
|
||||
* @param str
|
||||
* the String to pad out, may be null
|
||||
* @param size
|
||||
* the size to pad to
|
||||
* @param padChar
|
||||
* the character to pad with
|
||||
* @return left padded String or original String if no padding is necessary,
|
||||
* <code>null</code> if null String input
|
||||
* @since 2.0
|
||||
*/
|
||||
public static String padLeft(String str, int size, char padChar) {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
int pads = size - str.length();
|
||||
if (pads <= 0) {
|
||||
return str; // returns original String when possible
|
||||
}
|
||||
if (pads > PAD_LIMIT) {
|
||||
return padLeft(str, size, String.valueOf(padChar));
|
||||
}
|
||||
return padding(pads, padChar).concat(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Left pad a String with a specified String.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Pad to a size of <code>size</code>.
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.leftPad(null, *, *) = null
|
||||
* StringUtils.leftPad("", 3, "z") = "zzz"
|
||||
* StringUtils.leftPad("bat", 3, "yz") = "bat"
|
||||
* StringUtils.leftPad("bat", 5, "yz") = "yzbat"
|
||||
* StringUtils.leftPad("bat", 8, "yz") = "yzyzybat"
|
||||
* StringUtils.leftPad("bat", 1, "yz") = "bat"
|
||||
* StringUtils.leftPad("bat", -1, "yz") = "bat"
|
||||
* StringUtils.leftPad("bat", 5, null) = " bat"
|
||||
* StringUtils.leftPad("bat", 5, "") = " bat"
|
||||
* </pre>
|
||||
*
|
||||
* @param str
|
||||
* the String to pad out, may be null
|
||||
* @param size
|
||||
* the size to pad to
|
||||
* @param padStr
|
||||
* the String to pad with, null or empty treated as single space
|
||||
* @return left padded String or original String if no padding is necessary,
|
||||
* <code>null</code> if null String input
|
||||
*/
|
||||
public static String padLeft(String str, int size, String padStr) {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
if (isEmpty(padStr)) {
|
||||
padStr = " ";
|
||||
}
|
||||
int padLen = padStr.length();
|
||||
int strLen = str.length();
|
||||
int pads = size - strLen;
|
||||
if (pads <= 0) {
|
||||
return str; // returns original String when possible
|
||||
}
|
||||
if (padLen == 1 && pads <= PAD_LIMIT) {
|
||||
return padLeft(str, size, padStr.charAt(0));
|
||||
}
|
||||
|
||||
if (pads == padLen) {
|
||||
return padStr.concat(str);
|
||||
} else if (pads < padLen) {
|
||||
return padStr.substring(0, pads).concat(str);
|
||||
} else {
|
||||
char[] padding = new char[pads];
|
||||
char[] padChars = padStr.toCharArray();
|
||||
for (int i = 0; i < pads; i++) {
|
||||
padding[i] = padChars[i % padLen];
|
||||
}
|
||||
return new String(padding).concat(str);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Checks if a String is empty ("") or null.
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.isEmpty(null) = true
|
||||
* StringUtils.isEmpty("") = true
|
||||
* StringUtils.isEmpty(" ") = false
|
||||
* StringUtils.isEmpty("bob") = false
|
||||
* StringUtils.isEmpty(" bob ") = false
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* NOTE: This method changed in Lang version 2.0. It no longer trims the
|
||||
* String. That functionality is available in isBlank().
|
||||
* </p>
|
||||
*
|
||||
* @param str
|
||||
* the String to check, may be null
|
||||
* @return <code>true</code> if the String is empty or null
|
||||
*/
|
||||
public static boolean isEmpty(String str) {
|
||||
return str == null || str.length() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns padding using the specified delimiter repeated to a given length.
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.padding(0, 'e') = ""
|
||||
* StringUtils.padding(3, 'e') = "eee"
|
||||
* StringUtils.padding(-2, 'e') = IndexOutOfBoundsException
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* Note: this method doesn't not support padding with <a
|
||||
* href="http://www.unicode.org/glossary/#supplementary_character">Unicode
|
||||
* Supplementary Characters</a> as they require a pair of <code>char</code>s
|
||||
* to be represented. If you are needing to support full I18N of your
|
||||
* applications consider using {@link #repeat(String, int)} instead.
|
||||
* </p>
|
||||
*
|
||||
* @param repeat
|
||||
* number of times to repeat delim
|
||||
* @param padChar
|
||||
* character to repeat
|
||||
* @return String with repeated character
|
||||
* @throws IndexOutOfBoundsException
|
||||
* if <code>repeat < 0</code>
|
||||
* @see #repeat(String, int)
|
||||
*/
|
||||
private static String padding(int repeat, char padChar)
|
||||
throws IndexOutOfBoundsException {
|
||||
if (repeat < 0) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"Cannot pad a negative amount: " + repeat);
|
||||
}
|
||||
final char[] buf = new char[repeat];
|
||||
for (int i = 0; i < buf.length; i++) {
|
||||
buf[i] = padChar;
|
||||
}
|
||||
return new String(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Right-pad a String with a configurable padding character.
|
||||
*
|
||||
* @param inputString
|
||||
* The String to pad. A {@code null} String will be treated like
|
||||
* an empty String.
|
||||
* @param size
|
||||
* Pad String by the number of characters.
|
||||
* @param paddingChar
|
||||
* The character to pad the String with.
|
||||
* @return The padded String. If the provided String is null, an empty
|
||||
* String is returned.
|
||||
*/
|
||||
public static String padRight(String inputString, int size, char paddingChar) {
|
||||
|
||||
final String stringToPad;
|
||||
|
||||
if (inputString == null) {
|
||||
stringToPad = "";
|
||||
} else {
|
||||
stringToPad = inputString;
|
||||
}
|
||||
|
||||
StringBuilder padded = new StringBuilder(stringToPad);
|
||||
while (padded.length() < size) {
|
||||
padded.append(paddingChar);
|
||||
}
|
||||
return padded.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Right-pad the provided String with empty spaces.
|
||||
*
|
||||
* @param string
|
||||
* The String to pad
|
||||
* @param size
|
||||
* Pad String by the number of characters.
|
||||
* @return The padded String. If the provided String is null, an empty
|
||||
* String is returned.
|
||||
*/
|
||||
public static String padRight(String string, int size) {
|
||||
return padRight(string, size, ' ');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.support.util;
|
||||
|
||||
|
||||
/**
|
||||
* @author Jarred Li
|
||||
*/
|
||||
public class VersionUtils {
|
||||
|
||||
/**
|
||||
* Returns the full version string of the present Spring Shell codebase,
|
||||
* or <code>null</code> if it cannot be determined.
|
||||
* @see java.lang.Package#getImplementationVersion()
|
||||
*/
|
||||
public static String versionInfo() {
|
||||
Package pkg = VersionUtils.class.getPackage();
|
||||
String version = null;
|
||||
if (pkg != null) {
|
||||
version = pkg.getImplementationVersion();
|
||||
}
|
||||
return (version != null ? version : "Unknown Version");
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.shell.table;
|
||||
|
||||
/**
|
||||
* A cell sizing strategy that forces a fixed width, expressed in number of characters.
|
||||
*
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
public class AbsoluteWidthSizeConstraints implements SizeConstraints {
|
||||
|
||||
private final int width;
|
||||
|
||||
public AbsoluteWidthSizeConstraints(int width) {
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Extent width(String[] raw, int previous, int tableWidth) {
|
||||
return new Extent(width, width);
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.shell.table;
|
||||
|
||||
/**
|
||||
* A strategy interface for performing text alignment.
|
||||
*
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
public interface Aligner {
|
||||
|
||||
/**
|
||||
* Perform text alignment, returning a String array that MUST contain
|
||||
* {@code cellHeight} lines, each of which MUST be {@code cellWidth} chars in length.
|
||||
*
|
||||
* <p>Input array is guaranteed to contain lines that have length equal to {@cellWidth}. There
|
||||
* is no guarantee on the input number of lines though.</p>
|
||||
*/
|
||||
String[] align(String[] text, int cellWidth, int cellHeight);
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.shell.table;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A TableModel backed by a row-first array.
|
||||
*
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
public class ArrayTableModel extends TableModel {
|
||||
|
||||
private Object[][] data;
|
||||
|
||||
public ArrayTableModel(Object[][] data) {
|
||||
this.data = data;
|
||||
int width = data.length > 0 ? data[0].length : 0;
|
||||
for (int row = 0; row < data.length; row++) {
|
||||
Assert.isTrue(width == data[row].length, "All rows of array data must be of same length");
|
||||
}
|
||||
}
|
||||
|
||||
public int getRowCount() {
|
||||
return data.length;
|
||||
}
|
||||
|
||||
public int getColumnCount() {
|
||||
return data.length > 0 ? data[0].length : 0;
|
||||
}
|
||||
|
||||
public Object getValue(int row, int column) {
|
||||
return data[row][column];
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.shell.table;
|
||||
|
||||
/**
|
||||
* A SizeConstraints implementation that splits lines at space boundaries
|
||||
* and returns an extent with minimal and maximal width requirements.
|
||||
*
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
public class AutoSizeConstraints implements SizeConstraints {
|
||||
|
||||
@Override
|
||||
public Extent width(String[] raw, int tableWidth, int nbColumns) {
|
||||
int max = 0;
|
||||
int min = 0;
|
||||
for (String line : raw) {
|
||||
String[] words = line.split(" ");
|
||||
for (String word : words) {
|
||||
min = Math.max(min, word.length());
|
||||
}
|
||||
max = Math.max(max, line.length());
|
||||
}
|
||||
return new Extent(min, max);
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.shell.table;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.BeanWrapperImpl;
|
||||
|
||||
/**
|
||||
* A table model that is backed by a list of beans.
|
||||
*
|
||||
* <p>One can control which properties are exposed (and their order). There is also
|
||||
* a convenience constructor for adding a special header row.</p>
|
||||
*
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
public class BeanListTableModel<T> extends TableModel {
|
||||
|
||||
private final List<BeanWrapper> data;
|
||||
|
||||
private final List<String> propertyNames;
|
||||
|
||||
private final List<Object> headerRow;
|
||||
|
||||
public BeanListTableModel(Class<T> clazz, Iterable<T> list) {
|
||||
this.data = new ArrayList<BeanWrapper>();
|
||||
for (T bean : list) {
|
||||
this.data.add(new BeanWrapperImpl(bean));
|
||||
}
|
||||
this.headerRow = null;
|
||||
propertyNames = new ArrayList<String>();
|
||||
for (PropertyDescriptor propertyName : BeanUtils.getPropertyDescriptors(clazz)) {
|
||||
if ("class".equals(propertyName.getName())) {
|
||||
continue;
|
||||
}
|
||||
propertyNames.add(propertyName.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public BeanListTableModel(Iterable<T> list, String... propertyNames) {
|
||||
this.data = new ArrayList<BeanWrapper>();
|
||||
for (T bean : list) {
|
||||
this.data.add(new BeanWrapperImpl(bean));
|
||||
}
|
||||
this.headerRow = null;
|
||||
this.propertyNames = Arrays.asList(propertyNames);
|
||||
}
|
||||
|
||||
public BeanListTableModel(Iterable<T> list, LinkedHashMap<String, Object> header) {
|
||||
this.data = new ArrayList<BeanWrapper>();
|
||||
for (T bean : list) {
|
||||
this.data.add(new BeanWrapperImpl(bean));
|
||||
}
|
||||
this.headerRow = new ArrayList<Object>(header.values());
|
||||
propertyNames = new ArrayList<String>(header.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return headerRow == null ? data.size() : 1 + data.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return propertyNames.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(int row, int column) {
|
||||
if (headerRow != null && row == 0) {
|
||||
return headerRow.get(column);
|
||||
}
|
||||
else {
|
||||
int rowToUse = headerRow == null ? row : row - 1;
|
||||
String propertyName = propertyNames.get(column);
|
||||
return data.get(rowToUse).getPropertyValue(propertyName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.shell.table;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* This represents a directive to set some borders on cells of a table.
|
||||
* Multiple specifications can be combined on a single table.
|
||||
*
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
public class BorderSpecification {
|
||||
|
||||
public static final int NONE = 0;
|
||||
|
||||
public static final int TOP = 1;
|
||||
|
||||
public static final int BOTTOM = 2;
|
||||
|
||||
public static final int LEFT = 4;
|
||||
|
||||
public static final int RIGHT = 8;
|
||||
|
||||
public static final int INNER_VERTICAL = 16;
|
||||
|
||||
public static final int INNER_HORIZONTAL = 32;
|
||||
|
||||
public static final int OUTLINE = TOP | BOTTOM | LEFT | RIGHT;
|
||||
|
||||
public static final int FULL = OUTLINE | INNER_HORIZONTAL | INNER_VERTICAL;
|
||||
|
||||
public static final int INNER = INNER_HORIZONTAL | INNER_VERTICAL;
|
||||
|
||||
private final int row1, row2, column1, column2;
|
||||
|
||||
private final int match;
|
||||
|
||||
private final BorderStyle style;
|
||||
|
||||
/**
|
||||
* Specifications are created by {@link Table#addBorder(int, int, int, int, int, BorderStyle)}.
|
||||
*/
|
||||
/*default*/ BorderSpecification(int row1, int column1, int row2, int column2, int match, BorderStyle style) {
|
||||
this.row1 = row1;
|
||||
this.row2 = row2;
|
||||
this.column1 = column1;
|
||||
this.column2 = column2;
|
||||
this.match = match;
|
||||
this.style = style;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this specification result in the need to paint a vertical bar at row,column?
|
||||
*/
|
||||
/*default*/ char verticals(int row, int column) {
|
||||
boolean result = (match & LEFT) == LEFT && column == column1;
|
||||
result |= (match & INNER_VERTICAL) == INNER_VERTICAL && column > column1 && column < column2;
|
||||
result |= (match & RIGHT) == RIGHT && column == column2;
|
||||
|
||||
result &= row >= row1;
|
||||
result &= row < row2;
|
||||
return result ? style.verticalGlyph() : BorderStyle.NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this specification result in the need to paint an horizontal bar at row,column?
|
||||
*/
|
||||
/*default*/ char horizontals(int row, int column) {
|
||||
boolean result = (match & TOP) == TOP && row == row1;
|
||||
result |= (match & INNER_HORIZONTAL) == INNER_HORIZONTAL && row > row1 && row < row2;
|
||||
result |= (match & BOTTOM) == BOTTOM && row == row2;
|
||||
|
||||
result &= column >= column1;
|
||||
result &= column < column2;
|
||||
return result ? style.horizontalGlyph() : BorderStyle.NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s[(%d, %d)->(%d, %d), %s, %s]", getClass().getSimpleName(), row1, column1, row2, column2, style, matchConstants());
|
||||
}
|
||||
|
||||
private String matchConstants() {
|
||||
try {
|
||||
for (String field : new String[] {"NONE", "INNER", "FULL", "OUTLINE"}) {
|
||||
int value = ReflectionUtils.findField(getClass(), field).getInt(null);
|
||||
if (match == value) {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
List<String> constants = new ArrayList<String>();
|
||||
for (String field : new String[] {"TOP", "BOTTOM", "LEFT", "RIGHT", "INNER_HORIZONTAL", "INNER_VERTICAL"}) {
|
||||
int value = ReflectionUtils.findField(getClass(), field).getInt(null);
|
||||
if ((match & value) == value) {
|
||||
constants.add(field);
|
||||
}
|
||||
}
|
||||
return StringUtils.collectionToDelimitedString(constants, "|");
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,242 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.table;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Provides support for different styles of borders, using simple or fancy ascii art.
|
||||
*
|
||||
* @see <a href="https://en.wikipedia.org/wiki/Box-drawing_character">https://en.wikipedia.org/wiki/Box-drawing_character</a>
|
||||
*
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
public enum BorderStyle {
|
||||
|
||||
/**
|
||||
* A simplistic style, using characters that ought to always be available in all systems (pipe and minus).
|
||||
*/
|
||||
oldschool('|', '-'),
|
||||
|
||||
/**
|
||||
* A border style that uses dedicated light box drawing characters from the unicode set.
|
||||
*/
|
||||
fancy_light('│', '─'),
|
||||
|
||||
/**
|
||||
* A border style that uses dedicated fat box drawing characters from the unicode set.
|
||||
*/
|
||||
fancy_heavy('┃', '━'),
|
||||
|
||||
/**
|
||||
* A border style that uses dedicated double-light box drawing characters from the unicode set.
|
||||
*/
|
||||
fancy_double('║', '═'),
|
||||
|
||||
/**
|
||||
* A border style that uses space characters, giving some space between columns.
|
||||
*/
|
||||
air(' ', ' '),
|
||||
|
||||
/**
|
||||
* A border style that uses dedicated double dash light box drawing characters from the unicode set.
|
||||
*/
|
||||
fancy_light_double_dash('╎', '╌'),
|
||||
|
||||
/**
|
||||
* A border style that uses dedicated double dash light box drawing characters from the unicode set.
|
||||
*/
|
||||
fancy_light_triple_dash('┆', '┄'),
|
||||
|
||||
/**
|
||||
* A border style that uses dedicated double dash light box drawing characters from the unicode set.
|
||||
*/
|
||||
fancy_light_quadruple_dash('┊', '┈'),
|
||||
|
||||
/**
|
||||
* A border style that uses dedicated double dash heavy box drawing characters from the unicode set.
|
||||
*/
|
||||
fancy_heavy_double_dash('╏', '╍'),
|
||||
|
||||
/**
|
||||
* A border style that uses dedicated double dash heavy box drawing characters from the unicode set.
|
||||
*/
|
||||
fancy_heavy_triple_dash('┇', '┅'),
|
||||
|
||||
/**
|
||||
* A border style that uses dedicated double dash heavy box drawing characters from the unicode set.
|
||||
*/
|
||||
fancy_heavy_quadruple_dash('┋', '┉'),
|
||||
|
||||
;
|
||||
|
||||
private char vertical;
|
||||
|
||||
private char horizontal;
|
||||
|
||||
public static final char NONE = '\u0000';
|
||||
|
||||
private static Map<Long, Character> CORNERS = new HashMap<Long, Character>();
|
||||
|
||||
private static Map<Character, Character> EQUIVALENTS = new HashMap<Character, Character>();
|
||||
|
||||
public char verticalGlyph() {
|
||||
return vertical;
|
||||
}
|
||||
|
||||
public char horizontalGlyph() {
|
||||
return horizontal;
|
||||
}
|
||||
|
||||
static {
|
||||
registerCorners("─│┌┐└┘├┤┬┴┼");
|
||||
registerCorners("━┃┏┓┗┛┣┫┳┻╋");
|
||||
|
||||
// double dashes
|
||||
registerCorners("╌╎┌┐└┘├┤┬┴┼");
|
||||
registerCorners("╍╏┏┓┗┛┣┫┳┻╋");
|
||||
|
||||
// triple dashes
|
||||
registerCorners("┈┆┌┐└┘├┤┬┴┼");
|
||||
registerCorners("┅┇┏┓┗┛┣┫┳┻╋");
|
||||
|
||||
// quad dashes
|
||||
registerCorners("┈┊┌┐└┘├┤┬┴┼");
|
||||
registerCorners("┉┋┏┓┗┛┣┫┳┻╋");
|
||||
|
||||
// double lines
|
||||
registerCorners("═║╔╗╚╝╠╣╦╩╬");
|
||||
// oldschool
|
||||
registerCorners("-|+++++++++");
|
||||
// air style
|
||||
registerCorners(" ");
|
||||
|
||||
// Register some mixed-style combinations
|
||||
// light + heavy
|
||||
registerCorner('│', '│', '━', NONE, '┥');
|
||||
registerCorner('│', '│', NONE, '━', '┝');
|
||||
registerCorner('┃', NONE, '─', '─', '┸');
|
||||
registerCorner(NONE, '┃', '─', '─', '┰');
|
||||
// heavy + light
|
||||
registerCorner('┃', '┃', '─', NONE, '┨');
|
||||
registerCorner('┃', '┃', NONE, '─', '┠');
|
||||
registerCorner('│', NONE, '━', '━', '┷');
|
||||
registerCorner(NONE, '│', '━', '━', '┯');
|
||||
// double + single
|
||||
registerCorner('║', '║', '─', NONE, '╢');
|
||||
registerCorner('║', '║', NONE, '─', '╟');
|
||||
registerCorner('│', NONE, '═', '═', '╧');
|
||||
registerCorner(NONE, '│', '═', '═', '╤');
|
||||
// single + double
|
||||
registerCorner('│', '│', '═', NONE, '╡');
|
||||
registerCorner('│', '│', NONE, '═', '╞');
|
||||
registerCorner('║', NONE, '─', '─', '╨');
|
||||
registerCorner(NONE, '║', '─', '─', '╥');
|
||||
// heavy + light, 90°
|
||||
registerCorner('┃', '│', '━', '─', '╃');
|
||||
registerCorner('│', '┃', '─', '━', '╆');
|
||||
registerCorner('┃', '│', '─', '━', '╄');
|
||||
registerCorner('│', '┃', '━', '─', '╅');
|
||||
// light crossing (heavy or double)
|
||||
registerCorner('│', '│', '━', '━', '┿');
|
||||
registerCorner('│', '│', '═', '═', '╪');
|
||||
registerCorner('┃', '┃', '─', '─', '╂');
|
||||
registerCorner('║', '║', '─', '─', '╫');
|
||||
|
||||
// Dashed variants crossing others behave like regular corners
|
||||
registerSameCorners(fancy_light_double_dash, fancy_light);
|
||||
registerSameCorners(fancy_light_triple_dash, fancy_light);
|
||||
registerSameCorners(fancy_light_quadruple_dash, fancy_light);
|
||||
registerSameCorners(fancy_heavy_double_dash, fancy_heavy);
|
||||
registerSameCorners(fancy_heavy_triple_dash, fancy_heavy);
|
||||
registerSameCorners(fancy_heavy_quadruple_dash, fancy_heavy);
|
||||
|
||||
|
||||
// Air-style glyphs are easy to combine with others. Register some combinations
|
||||
registerMixedWithAirCombinations(oldschool.vertical, oldschool.horizontal);
|
||||
registerMixedWithAirCombinations(fancy_light.vertical, fancy_light.horizontal);
|
||||
registerMixedWithAirCombinations(fancy_double.vertical, fancy_double.horizontal);
|
||||
registerMixedWithAirCombinations(fancy_heavy.vertical, fancy_heavy.horizontal);
|
||||
|
||||
registerMixedWithAirCombinations(fancy_light_double_dash.vertical, fancy_light_double_dash.horizontal);
|
||||
registerMixedWithAirCombinations(fancy_light_triple_dash.vertical, fancy_light_triple_dash.horizontal);
|
||||
registerMixedWithAirCombinations(fancy_light_quadruple_dash.vertical, fancy_light_quadruple_dash.horizontal);
|
||||
registerMixedWithAirCombinations(fancy_heavy_double_dash.vertical, fancy_heavy_double_dash.horizontal);
|
||||
registerMixedWithAirCombinations(fancy_heavy_triple_dash.vertical, fancy_heavy_triple_dash.horizontal);
|
||||
registerMixedWithAirCombinations(fancy_heavy_quadruple_dash.vertical, fancy_heavy_quadruple_dash.horizontal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the fact that for corner purposes, style1 behaves like style2.
|
||||
*/
|
||||
private static void registerSameCorners(BorderStyle style1, BorderStyle style2) {
|
||||
EQUIVALENTS.put(style1.horizontal, style2.horizontal);
|
||||
EQUIVALENTS.put(style1.vertical, style2.vertical);
|
||||
}
|
||||
|
||||
private static void registerMixedWithAirCombinations(char vertical, char horizontal) {
|
||||
registerCorner(vertical, vertical, ' ', NONE, vertical);
|
||||
registerCorner(vertical, vertical, NONE, ' ', vertical);
|
||||
registerCorner(vertical, vertical, ' ', ' ', vertical);
|
||||
registerCorner(' ', NONE, horizontal, horizontal, horizontal);
|
||||
registerCorner(NONE, ' ', horizontal, horizontal, horizontal);
|
||||
registerCorner(' ', ' ', horizontal, horizontal, horizontal);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Register corner glyphs for a given set, not taking care of mixed style intersections.
|
||||
*/
|
||||
private static void registerCorners(String list) {
|
||||
char horizontal = list.charAt(0);
|
||||
char vertical = list.charAt(1);
|
||||
registerCorner(NONE, vertical, NONE, horizontal, list.charAt(2));
|
||||
registerCorner(NONE, vertical, horizontal, NONE, list.charAt(3));
|
||||
registerCorner(vertical, NONE, NONE, horizontal, list.charAt(4));
|
||||
registerCorner(vertical, NONE, horizontal, NONE, list.charAt(5));
|
||||
registerCorner(vertical, vertical, NONE, horizontal, list.charAt(6));
|
||||
registerCorner(vertical, vertical, horizontal, NONE, list.charAt(7));
|
||||
registerCorner(NONE, vertical, horizontal, horizontal, list.charAt(8));
|
||||
registerCorner(vertical, NONE, horizontal, horizontal, list.charAt(9));
|
||||
registerCorner(vertical, vertical, horizontal, horizontal, list.charAt(10));
|
||||
|
||||
}
|
||||
|
||||
private static void registerCorner(char above, char below, char left, char right, char corner) {
|
||||
long key = key(above, below, left, right);
|
||||
CORNERS.put(key, corner);
|
||||
}
|
||||
|
||||
public static char intersection(char above, char below, char left, char right) {
|
||||
above = EQUIVALENTS.get(above) != null ? EQUIVALENTS.get(above) : above;
|
||||
below = EQUIVALENTS.get(below) != null ? EQUIVALENTS.get(below) : below;
|
||||
left = EQUIVALENTS.get(left) != null ? EQUIVALENTS.get(left) : left;
|
||||
right = EQUIVALENTS.get(right) != null ? EQUIVALENTS.get(right) : right;
|
||||
Character character = CORNERS.get(key(above, below, left, right));
|
||||
return character != null ? character : NONE;
|
||||
}
|
||||
|
||||
private static long key(char above, char below, char left, char right) {
|
||||
return (long) above << 48 | (long) below << 32 | (long) left << 16 | (long) right;
|
||||
}
|
||||
|
||||
BorderStyle(char vertical, char horizontal) {
|
||||
this.vertical = vertical;
|
||||
this.horizontal = horizontal;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.shell.table;
|
||||
|
||||
/**
|
||||
* This is used to specify where some components of a Table may be applied.
|
||||
*
|
||||
* <p>Some commonly used matchers can be created <i>via</i> {@link CellMatchers}.</p>
|
||||
*
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
public interface CellMatcher {
|
||||
|
||||
/**
|
||||
* Return whether a given cell of the table should match.
|
||||
*/
|
||||
public boolean matches(int row, int column, TableModel model);
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.shell.table;
|
||||
|
||||
/**
|
||||
* Contains factory methods for commonly used {@link CellMatcher}s.
|
||||
*
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
public class CellMatchers {
|
||||
|
||||
/**
|
||||
* Return a matcher that applies to every cell of the table.
|
||||
*/
|
||||
public static CellMatcher table() {
|
||||
return new CellMatcher() {
|
||||
public boolean matches(int row, int column, TableModel model) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a matcher that applies to every cell of some column of the table.
|
||||
*/
|
||||
public static CellMatcher column(final int col) {
|
||||
return new CellMatcher() {
|
||||
public boolean matches(int row, int column, TableModel model) {
|
||||
return col == column;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a matcher that applies to every cell of some row of the table.
|
||||
*/
|
||||
public static CellMatcher row(final int theRow) {
|
||||
return new CellMatcher() {
|
||||
public boolean matches(int row, int column, TableModel model) {
|
||||
return theRow == row;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static CellMatcher ofType(final Class<?> clazz) {
|
||||
return new CellMatcher() {
|
||||
@Override
|
||||
public boolean matches(int row, int column, TableModel model) {
|
||||
Object value = model.getValue(row, column);
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return clazz.isAssignableFrom(value.getClass());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.shell.table;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A decorator Aligner that checks the Aligner invariants contract, useful for debugging.
|
||||
*
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
public class DebugAligner implements Aligner {
|
||||
|
||||
private final Aligner delegate;
|
||||
|
||||
public DebugAligner(Aligner delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] align(String[] text, int cellWidth, int cellHeight) {
|
||||
String[] result = delegate.align(text, cellWidth, cellHeight);
|
||||
Assert.isTrue(result.length == cellHeight, String.format("%s had the wrong number of lines (%d), expected %d",
|
||||
Arrays.asList(result), result.length, cellHeight));
|
||||
for (String s : result) {
|
||||
Assert.isTrue(s.length() == cellWidth, String.format("'%s' had wrong length (%d), expected %d", s, s.length(),
|
||||
cellWidth));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.shell.table;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A TextWrapper that delegates to another but makes sure that the contract is not violated.
|
||||
*
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
public class DebugTextWrapper implements TextWrapper {
|
||||
|
||||
private final TextWrapper delegate;
|
||||
|
||||
public DebugTextWrapper(TextWrapper delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] wrap(String[] original, int columnWidth) {
|
||||
String[] result = delegate.wrap(original, columnWidth);
|
||||
for (String s : result) {
|
||||
Assert.isTrue(s.length() == columnWidth, String.format("'%s' has the wrong length (%d), expected %d", s, s.length(), columnWidth));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user