From ff6877993769ae8ad9765feef46d5fd1d5394952 Mon Sep 17 00:00:00 2001 From: Eric Bottard Date: Sat, 26 Dec 2015 23:47:24 +0100 Subject: [PATCH] Adds ResultHandler, a way to dynamically deal with method execution results --- .../org/springframework/shell2/Bootstrap.java | 10 +++ .../springframework/shell2/JLineShell.java | 22 +++--- .../AttributedCharSequenceResultHandler.java | 44 ++++++++++++ .../shell2/result/DefaultResultHandler.java | 34 +++++++++ .../shell2/result/IterableResultHandler.java | 44 ++++++++++++ .../shell2/result/ResultHandler.java | 32 +++++++++ .../shell2/result/ResultHandlers.java | 70 +++++++++++++++++++ .../shell2/result/ThrowableResultHandler.java | 37 ++++++++++ 8 files changed, 279 insertions(+), 14 deletions(-) create mode 100644 src/main/java/org/springframework/shell2/result/AttributedCharSequenceResultHandler.java create mode 100644 src/main/java/org/springframework/shell2/result/DefaultResultHandler.java create mode 100644 src/main/java/org/springframework/shell2/result/IterableResultHandler.java create mode 100644 src/main/java/org/springframework/shell2/result/ResultHandler.java create mode 100644 src/main/java/org/springframework/shell2/result/ResultHandlers.java create mode 100644 src/main/java/org/springframework/shell2/result/ThrowableResultHandler.java diff --git a/src/main/java/org/springframework/shell2/Bootstrap.java b/src/main/java/org/springframework/shell2/Bootstrap.java index 011c7198..0f9301eb 100644 --- a/src/main/java/org/springframework/shell2/Bootstrap.java +++ b/src/main/java/org/springframework/shell2/Bootstrap.java @@ -16,6 +16,11 @@ package org.springframework.shell2; +import java.io.IOException; + +import org.jline.terminal.Terminal; +import org.jline.terminal.TerminalBuilder; + import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; @@ -51,4 +56,9 @@ public class Bootstrap { return new DefaultParameterResolver(conversionService); } + @Bean + public Terminal terminal() throws IOException { + return TerminalBuilder.builder().build(); + } + } diff --git a/src/main/java/org/springframework/shell2/JLineShell.java b/src/main/java/org/springframework/shell2/JLineShell.java index 3b32d8e1..98f45edc 100644 --- a/src/main/java/org/springframework/shell2/JLineShell.java +++ b/src/main/java/org/springframework/shell2/JLineShell.java @@ -40,8 +40,6 @@ import org.jline.reader.LineReaderBuilder; import org.jline.reader.ParsedLine; import org.jline.reader.impl.DefaultParser; import org.jline.terminal.Terminal; -import org.jline.terminal.TerminalBuilder; -import org.jline.utils.AttributedCharSequence; import org.jline.utils.AttributedString; import org.jline.utils.AttributedStringBuilder; import org.jline.utils.AttributedStyle; @@ -50,7 +48,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.core.MethodParameter; -import org.springframework.core.convert.ConversionService; +import org.springframework.shell2.result.ResultHandlers; import org.springframework.stereotype.Component; import org.springframework.util.ReflectionUtils; @@ -61,17 +59,18 @@ import org.springframework.util.ReflectionUtils; public class JLineShell implements Shell { @Autowired - private ApplicationContext applicationContext; + private ResultHandlers resultHandlers; @Autowired - private ConversionService conversionService; - - private Map commandBeans; + private ApplicationContext applicationContext; private Map methodTargets = new HashMap<>(); private LineReader lineReader; + @Autowired + private Terminal terminal; + @Autowired private List parameterResolvers = new ArrayList<>(); @@ -86,7 +85,6 @@ public class JLineShell implements Shell { methodTargets.putAll(resolver.resolve(applicationContext)); } - Terminal terminal = TerminalBuilder.builder().build(); LineReaderBuilder lineReaderBuilder = LineReaderBuilder.builder() .terminal(terminal) .appName("Foo") @@ -189,12 +187,7 @@ public class JLineShell implements Shell { result = e; } - if (result instanceof AttributedCharSequence) { - System.out.println(((AttributedCharSequence) result).toAnsi(lineReader.getTerminal())); - } - else { - System.out.println(String.valueOf(result)); - } + resultHandlers.handleResult(result); } else { @@ -202,4 +195,5 @@ public class JLineShell implements Shell { } } } + } diff --git a/src/main/java/org/springframework/shell2/result/AttributedCharSequenceResultHandler.java b/src/main/java/org/springframework/shell2/result/AttributedCharSequenceResultHandler.java new file mode 100644 index 00000000..00368af8 --- /dev/null +++ b/src/main/java/org/springframework/shell2/result/AttributedCharSequenceResultHandler.java @@ -0,0 +1,44 @@ +/* + * 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.shell2.result; + +import org.jline.terminal.Terminal; +import org.jline.utils.AttributedCharSequence; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * A {@link ResultHandler} that knows how to render JLine's {@link AttributedCharSequence}. + * + * @author Eric Bottard + */ +@Component +public class AttributedCharSequenceResultHandler implements ResultHandler { + + private final Terminal terminal; + + @Autowired + public AttributedCharSequenceResultHandler(Terminal terminal) { + this.terminal = terminal; + } + + @Override + public void handleResult(AttributedCharSequence result) { + System.out.println(result.toAnsi(terminal)); + } +} diff --git a/src/main/java/org/springframework/shell2/result/DefaultResultHandler.java b/src/main/java/org/springframework/shell2/result/DefaultResultHandler.java new file mode 100644 index 00000000..ce21704f --- /dev/null +++ b/src/main/java/org/springframework/shell2/result/DefaultResultHandler.java @@ -0,0 +1,34 @@ +/* + * 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.shell2.result; + +import org.springframework.stereotype.Component; + +/** + * A simple {@link ResultHandler} that deals with Objects (hence comes as a last resort) + * and prints the {@link Object#toString()} value of results to standard out. + * + * @author Eric Bottard + */ +@Component +public class DefaultResultHandler implements ResultHandler { + + @Override + public void handleResult(Object result) { + System.out.println(String.valueOf(result)); + } +} diff --git a/src/main/java/org/springframework/shell2/result/IterableResultHandler.java b/src/main/java/org/springframework/shell2/result/IterableResultHandler.java new file mode 100644 index 00000000..de624a3a --- /dev/null +++ b/src/main/java/org/springframework/shell2/result/IterableResultHandler.java @@ -0,0 +1,44 @@ +/* + * 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.shell2.result; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * A {@link ResultHandler} that deals with {@link Iterable}s and delegates to + * {@link ResultHandlers} for each element in turn. + * + * @author Eric Bottard + */ +@Component +public class IterableResultHandler implements ResultHandler { + + private ResultHandlers resultHandlers; + + @Autowired + public void setResultHandlers(ResultHandlers resultHandlers) { + this.resultHandlers = resultHandlers; + } + + @Override + public void handleResult(Iterable result) { + for (Object o : result) { + resultHandlers.handleResult(o); + } + } +} diff --git a/src/main/java/org/springframework/shell2/result/ResultHandler.java b/src/main/java/org/springframework/shell2/result/ResultHandler.java new file mode 100644 index 00000000..100643f1 --- /dev/null +++ b/src/main/java/org/springframework/shell2/result/ResultHandler.java @@ -0,0 +1,32 @@ +/* + * 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.shell2.result; + +/** + * Implementations know how to deal with results of method invocations, whether normal results or exceptions thrown. + * + * @author Eric Bottard + */ +public interface ResultHandler { + + /** + * Deal with some method execution result, whether it was the normal return value, or some kind + * of {@link Throwable}. + */ + void handleResult(T result); + +} diff --git a/src/main/java/org/springframework/shell2/result/ResultHandlers.java b/src/main/java/org/springframework/shell2/result/ResultHandlers.java new file mode 100644 index 00000000..574bd311 --- /dev/null +++ b/src/main/java/org/springframework/shell2/result/ResultHandlers.java @@ -0,0 +1,70 @@ +/* + * 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.shell2.result; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * A unique entry point delegating to the most appropriate {@link ResultHandler}, + * according to the type of result to handle. + * + * @author Eric Bottard + */ +@Component +public class ResultHandlers { + + private Map, ResultHandler> resultHandlers = new HashMap<>(); + + @SuppressWarnings("unchecked") + public void handleResult(Object result) { + Class clazz = result == null ? null : result.getClass(); + ResultHandler handler = getResultHandler(clazz); + handler.handleResult(result); + } + + private ResultHandler getResultHandler(Class clazz) { + ResultHandler handler = resultHandlers.get(clazz); + if (handler != null) { + return handler; + } + else { + for (Class type : clazz.getInterfaces()) { + handler = getResultHandler(type); + if (handler != null) { + return handler; + } + } + return clazz.getSuperclass() != null ? getResultHandler(clazz.getSuperclass()) : null; + } + } + + @Autowired + public void setResultHandlers(Set> resultHandlers) { + for (ResultHandler resultHandler : resultHandlers) { + Type type = ((ParameterizedType) resultHandler.getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0]; + this.resultHandlers.put((Class) type, resultHandler); + } + } + +} diff --git a/src/main/java/org/springframework/shell2/result/ThrowableResultHandler.java b/src/main/java/org/springframework/shell2/result/ThrowableResultHandler.java new file mode 100644 index 00000000..6cfa1e6a --- /dev/null +++ b/src/main/java/org/springframework/shell2/result/ThrowableResultHandler.java @@ -0,0 +1,37 @@ +/* + * 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.shell2.result; + +import org.jline.utils.AttributedString; +import org.jline.utils.AttributedStyle; + +import org.springframework.stereotype.Component; + +/** + * A {@link ResultHandler} that prints thrown exceptions messages in red. + * + * @author Eric Bottard + */ +@Component +public class ThrowableResultHandler implements ResultHandler { + + @Override + public void handleResult(Throwable result) { + System.out.println(new AttributedString(result.getMessage(), + AttributedStyle.DEFAULT.foreground(AttributedStyle.RED)).toAnsi()); + } +}