Adds ResultHandler, a way to dynamically deal with method execution results

This commit is contained in:
Eric Bottard
2015-12-26 23:47:24 +01:00
parent d68589a69f
commit ff68779937
8 changed files with 279 additions and 14 deletions

View File

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

View File

@@ -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<String, Object> commandBeans;
private ApplicationContext applicationContext;
private Map<String, MethodTarget> methodTargets = new HashMap<>();
private LineReader lineReader;
@Autowired
private Terminal terminal;
@Autowired
private List<ParameterResolver> 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 {
}
}
}
}

View File

@@ -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<AttributedCharSequence> {
private final Terminal terminal;
@Autowired
public AttributedCharSequenceResultHandler(Terminal terminal) {
this.terminal = terminal;
}
@Override
public void handleResult(AttributedCharSequence result) {
System.out.println(result.toAnsi(terminal));
}
}

View File

@@ -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<Object> {
@Override
public void handleResult(Object result) {
System.out.println(String.valueOf(result));
}
}

View File

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

View File

@@ -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<T> {
/**
* Deal with some method execution result, whether it was the normal return value, or some kind
* of {@link Throwable}.
*/
void handleResult(T result);
}

View File

@@ -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<Class<?>, 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<ResultHandler<?>> resultHandlers) {
for (ResultHandler<?> resultHandler : resultHandlers) {
Type type = ((ParameterizedType) resultHandler.getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0];
this.resultHandlers.put((Class<?>) type, resultHandler);
}
}
}

View File

@@ -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<Throwable> {
@Override
public void handleResult(Throwable result) {
System.out.println(new AttributedString(result.getMessage(),
AttributedStyle.DEFAULT.foreground(AttributedStyle.RED)).toAnsi());
}
}