Skip to content

Commit

Permalink
feat: adding required on number input
Browse files Browse the repository at this point in the history
  • Loading branch information
Nicola Di Falco committed Aug 7, 2023
1 parent bda104c commit 329067f
Show file tree
Hide file tree
Showing 10 changed files with 141 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public class NumberInput extends AbstractTextComponent<Number, NumberInputContex
private static final Logger log = LoggerFactory.getLogger(NumberInput.class);
private final Number defaultValue;
private Class<? extends Number> clazz;
private boolean required;
private NumberInputContext currentContext;

public NumberInput(Terminal terminal) {
Expand All @@ -55,32 +56,41 @@ public NumberInput(Terminal terminal, String name) {
}

public NumberInput(Terminal terminal, String name, Number defaultValue) {
this(terminal, name, defaultValue, Integer.class, null);
this(terminal, name, defaultValue, Integer.class);
}

public NumberInput(Terminal terminal, String name, Number defaultValue, Class<? extends Number> clazz) {
this(terminal, name, defaultValue, clazz, null);
this(terminal, name, defaultValue, clazz, false);
}

public NumberInput(Terminal terminal, String name, Number defaultValue, Class<? extends Number> clazz,
public NumberInput(Terminal terminal, String name, Number defaultValue, Class<? extends Number> clazz, boolean required) {
this(terminal, name, defaultValue, clazz, required, null);
}

public NumberInput(Terminal terminal, String name, Number defaultValue, Class<? extends Number> clazz, boolean required,
Function<NumberInputContext, List<AttributedString>> renderer) {
super(terminal, name, null);
setRenderer(renderer != null ? renderer : new DefaultRenderer());
setTemplateLocation("classpath:org/springframework/shell/component/number-input-default.stg");
this.defaultValue = defaultValue;
this.clazz = clazz;
this.required = required;
}

public void setNumberClass(Class<? extends Number> clazz) {
this.clazz = clazz;
}

public void setRequired(boolean required) {
this.required = required;
}

@Override
public NumberInputContext getThisContext(ComponentContext<?> context) {
if (context != null && currentContext == context) {
return currentContext;
}
currentContext = NumberInputContext.of(defaultValue, clazz);
currentContext = NumberInputContext.of(defaultValue, clazz, required);
currentContext.setName(getName());
Optional.ofNullable(context).map(ComponentContext::stream)
.ifPresent(entryStream -> entryStream.forEach(e -> currentContext.put(e.getKey(), e.getValue())));
Expand Down Expand Up @@ -121,6 +131,9 @@ protected boolean read(BindingReader bindingReader, KeyMap<String> keyMap, Numbe
}
else if (context.getDefaultValue() != null) {
context.setResultValue(context.getDefaultValue());
} else if (required) {
context.setMessage("This field is mandatory", TextComponentContext.MessageLevel.ERROR);
break;
}
return true;
default:
Expand Down Expand Up @@ -186,6 +199,20 @@ public interface NumberInputContext extends TextComponentContext<Number, NumberI
*/
void setDefaultClass(Class<? extends Number> defaultClass);

/**
* Sets flag for mandatory input.
*
* @param required true if input is required
*/
void setRequired(boolean required);

/**
* Returns flag if input is required.
*
* @return true if input is required, false otherwise
*/
boolean isRequired();

/**
* Gets an empty {@link NumberInputContext}.
*
Expand All @@ -201,7 +228,7 @@ public static NumberInputContext empty() {
* @return number input context
*/
public static NumberInputContext of(Number defaultValue) {
return new DefaultNumberInputContext(defaultValue, Integer.class);
return new DefaultNumberInputContext(defaultValue, Integer.class, false);
}

/**
Expand All @@ -210,18 +237,29 @@ public static NumberInputContext of(Number defaultValue) {
* @return number input context
*/
public static NumberInputContext of(Number defaultValue, Class<? extends Number> defaultClass) {
return new DefaultNumberInputContext(defaultValue, defaultClass);
return new DefaultNumberInputContext(defaultValue, defaultClass, false);
}

/**
* Gets an {@link NumberInputContext}.
*
* @return number input context
*/
public static NumberInputContext of(Number defaultValue, Class<? extends Number> defaultClass, boolean required) {
return new DefaultNumberInputContext(defaultValue, defaultClass, required);
}
}

private static class DefaultNumberInputContext extends BaseTextComponentContext<Number, NumberInputContext> implements NumberInputContext {

private Number defaultValue;
private Class<? extends Number> defaultClass;
private boolean required;

public DefaultNumberInputContext(Number defaultValue, Class<? extends Number> defaultClass) {
public DefaultNumberInputContext(Number defaultValue, Class<? extends Number> defaultClass, boolean required) {
this.defaultValue = defaultValue;
this.defaultClass = defaultClass;
this.required = required;
}

@Override
Expand All @@ -244,11 +282,22 @@ public void setDefaultClass(Class<? extends Number> defaultClass) {
this.defaultClass = defaultClass;
}

@Override
public void setRequired(boolean required) {
this.required = required;
}

@Override
public boolean isRequired() {
return required;
}

@Override
public Map<String, Object> toTemplateModel() {
Map<String, Object> attributes = super.toTemplateModel();
attributes.put("defaultValue", getDefaultValue() != null ? getDefaultValue() : null);
attributes.put("defaultClass", getDefaultClass().getSimpleName());
attributes.put("required", isRequired());
Map<String, Object> model = new HashMap<>();
model.put("model", attributes);
return model;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public abstract class BaseNumberInput extends BaseInput<NumberInputSpec> impleme
private ResultMode resultMode;
private Number defaultValue;
private Class<? extends Number> clazz = Integer.class;
private boolean required = false;
private Function<NumberInputContext, List<AttributedString>> renderer;
private final List<Consumer<NumberInputContext>> preHandlers = new ArrayList<>();
private final List<Consumer<NumberInputContext>> postHandlers = new ArrayList<>();
Expand Down Expand Up @@ -78,6 +79,12 @@ public NumberInputSpec numberClass(Class<? extends Number> clazz) {
return this;
}

@Override
public NumberInputSpec required() {
this.required = true;
return this;
}

@Override
public NumberInputSpec renderer(Function<NumberInputContext, List<AttributedString>> renderer) {
this.renderer = renderer;
Expand Down Expand Up @@ -145,6 +152,10 @@ public Class<? extends Number> getNumberClass() {
return clazz;
}

public boolean isRequired() {
return required;
}

public Function<NumberInputContext, List<AttributedString>> getRenderer() {
return renderer;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,7 @@ private Stream<OrderedInputOperation> stringInputsStream() {

private Stream<OrderedInputOperation> numberInputsStream() {
return numberInputs.stream().map(input -> {
NumberInput selector = new NumberInput(terminal, input.getName(), input.getDefaultValue(), input.getNumberClass());
NumberInput selector = new NumberInput(terminal, input.getName(), input.getDefaultValue(), input.getNumberClass(), input.isRequired());
UnaryOperator<ComponentContext<?>> operation = context -> {
if (input.getResultMode() == ResultMode.ACCEPT && input.isStoreResult()
&& input.getResultValue() != null) {
Expand All @@ -533,7 +533,10 @@ private Stream<OrderedInputOperation> numberInputsStream() {
}
if (input.isStoreResult()) {
if (input.getResultMode() == ResultMode.VERIFY && input.getResultValue() != null) {
selector.addPreRunHandler(c -> c.setDefaultValue(input.getResultValue()));
selector.addPreRunHandler(c -> {
c.setDefaultValue(input.getResultValue());
c.setRequired(input.isRequired());
});
}
selector.addPostRunHandler(c -> c.put(input.getId(), c.getResultValue()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ public interface NumberInputSpec extends BaseInputSpec<NumberInputSpec> {
*/
NumberInputSpec numberClass(Class<? extends Number> clazz);

/**
* Sets input to required
*
* @return a builder
*/
NumberInputSpec required();

/**
* Sets a renderer function.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ info(model) ::= <%
<if(model.input)>
<model.input>
<else>
<if(model.required)>
<("[Required]"); format="style-value">
<endif>
<("[Number Type: "); format="style-value"><model.defaultClass; format="style-value"><("]"); format="style-value">
<if(model.defaultValue)>
<("[Default "); format="style-value"><model.defaultValue; format="style-value"><("]"); format="style-value">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,4 +245,37 @@ public void testResultUserInputInvalidInput() throws InterruptedException, IOExc
assertThat(run1Context).isNotNull();
assertThat(run1Context.getResultValue()).isNull();
}

@Test
public void testResultMandatoryInput() throws InterruptedException {
ComponentContext<?> empty = ComponentContext.empty();
NumberInput component1 = new NumberInput(getTerminal());
component1.setResourceLoader(new DefaultResourceLoader());
component1.setTemplateExecutor(getTemplateExecutor());
component1.setRequired(true);

service.execute(() -> {
NumberInputContext run1Context = component1.run(empty);
result1.set(run1Context);
latch1.countDown();
});

TestBuffer testBuffer = new TestBuffer().cr();
write(testBuffer.getBytes());

latch1.await(2, TimeUnit.SECONDS);

NumberInputContext run1Context = result1.get();
assertThat(consoleOut()).contains("This field is mandatory");
assertThat(run1Context).isNull();

testBuffer.append("2").cr();
write(testBuffer.getBytes());

latch1.await(2, TimeUnit.SECONDS);
run1Context = result1.get();

assertThat(run1Context).isNotNull();
assertThat(run1Context.getResultValue()).isEqualTo(2);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ public void testSimpleFlow() throws InterruptedException {
.defaultValue(20.5)
.numberClass(Double.class)
.and()
.withNumberInput("number3")
.name("Number3")
.required()
.and()
.withPathInput("path1")
.name("Path1")
.and()
Expand Down Expand Up @@ -93,6 +97,9 @@ public void testSimpleFlow() throws InterruptedException {
// number2
testBuffer = new TestBuffer().cr();
write(testBuffer.getBytes());
// number3
testBuffer = new TestBuffer().cr().append("5").cr();
write(testBuffer.getBytes());
// path1
testBuffer = new TestBuffer().append("fakedir").cr();
write(testBuffer.getBytes());
Expand All @@ -110,13 +117,15 @@ public void testSimpleFlow() throws InterruptedException {
String field2 = inputWizardResult.getContext().get("field2");
Integer number1 = inputWizardResult.getContext().get("number1");
Double number2 = inputWizardResult.getContext().get("number2");
Integer number3 = inputWizardResult.getContext().get("number3");
Path path1 = inputWizardResult.getContext().get("path1");
String single1 = inputWizardResult.getContext().get("single1");
List<String> multi1 = inputWizardResult.getContext().get("multi1");
assertThat(field1).isEqualTo("defaultField1Value");
assertThat(field2).isEqualTo("Field2Value");
assertThat(number1).isEqualTo(35);
assertThat(number2).isEqualTo(20.5);
assertThat(number3).isEqualTo(5);
assertThat(path1.toString()).contains("fakedir");
assertThat(single1).isEqualTo("value1");
assertThat(multi1).containsExactlyInAnyOrder("value2");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ The context object is `NumberInputContext`. The following table lists its contex
|`defaultClass`
|The default number class to use, if set. Otherwise, Integer.class.

|`required`
|`true` if the input is required. Otherwise, false.

|`model`
|The parent context variables (see <<textcomponentcontext-template-variables>>).
|===
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,16 @@ public String numberInputDouble() {
return "Got value " + context.getResultValue();
}

@ShellMethod(key = "component number required", value = "Number input", group = "Components")
public String numberInputRequired() {
NumberInput component = new NumberInput(getTerminal(), "Enter value");
component.setRequired(true);
component.setResourceLoader(getResourceLoader());
component.setTemplateExecutor(getTemplateExecutor());
NumberInputContext context = component.run(NumberInputContext.empty());
return "Got value " + context.getResultValue();
}

@ShellMethod(key = "component path", value = "Path input", group = "Components")
public String pathInput() {
PathInput component = new PathInput(getTerminal(), "Enter value");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ public void showcase1() {
.defaultValue(20.5)
.numberClass(Double.class)
.and()
.withNumberInput("number3")
.name("Field3")
.required()
.and()
.withConfirmationInput("confirmation1")
.name("Confirmation1")
.and()
Expand Down

0 comments on commit 329067f

Please sign in to comment.