Remove everything prior to Shell 2 migration

This commit is contained in:
Eric Bottard
2017-08-08 11:04:41 +02:00
parent 7424ccad05
commit 3cd5a1be3b
233 changed files with 0 additions and 17574 deletions

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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]");
}
}

View File

@@ -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);
}

View File

@@ -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));
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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() {}
}

View File

@@ -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() + "]");
}
}
}
}

View File

@@ -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;
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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"));
}
}

View File

@@ -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());
}
}
}

View File

@@ -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 {}

View File

@@ -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 + "]";
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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"));
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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 "";
}

View File

@@ -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 "";
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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";
}
}

View File

@@ -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";
}
}

View File

@@ -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";
}
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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";
}
}

View File

@@ -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;
}
}

View File

@@ -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() {
}
}

View File

@@ -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() {}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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 &lt; 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, ' ');
}
}

View File

@@ -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");
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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];
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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());
}
}
};
}
}

View File

@@ -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;
}
}

View File

@@ -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