Flow system for UI components
- Implement basic flow system with builder patterns to easily use components and hook those together as a flow. - Fixes #364
This commit is contained in:
@@ -63,7 +63,7 @@ public class ConfirmationInput extends AbstractTextComponent<Boolean, Confirmati
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfirmationInputContext getThisContext(ComponentContext<?> context) {
|
||||
public ConfirmationInputContext getThisContext(ComponentContext<?> context) {
|
||||
if (context != null && currentContext == context) {
|
||||
return currentContext;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ public class MultiItemSelector<T, I extends Nameable & Matchable & Enableable &
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MultiItemSelectorContext<T, I> getThisContext(ComponentContext<?> context) {
|
||||
public MultiItemSelectorContext<T, I> getThisContext(ComponentContext<?> context) {
|
||||
if (context != null && currentContext == context) {
|
||||
return currentContext;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ public class PathInput extends AbstractTextComponent<Path, PathInputContext> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PathInputContext getThisContext(ComponentContext<?> context) {
|
||||
public PathInputContext getThisContext(ComponentContext<?> context) {
|
||||
if (context != null && currentContext == context) {
|
||||
return currentContext;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ public class SingleItemSelector<T, I extends Nameable & Matchable & Enableable &
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SingleItemSelectorContext<T, I> getThisContext(ComponentContext<?> context) {
|
||||
public SingleItemSelectorContext<T, I> getThisContext(ComponentContext<?> context) {
|
||||
if (context != null && currentContext == context) {
|
||||
return currentContext;
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ public class StringInput extends AbstractTextComponent<String, StringInputContex
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StringInputContext getThisContext(ComponentContext<?> context) {
|
||||
public StringInputContext getThisContext(ComponentContext<?> context) {
|
||||
if (context != null && currentContext == context) {
|
||||
return currentContext;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.component.flow;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jline.utils.AttributedString;
|
||||
|
||||
import org.springframework.shell.component.ConfirmationInput.ConfirmationInputContext;
|
||||
import org.springframework.shell.component.flow.ComponentFlow.BaseBuilder;
|
||||
import org.springframework.shell.component.flow.ComponentFlow.Builder;
|
||||
|
||||
/**
|
||||
* Base impl for {@link ConfirmationInputSpec}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public abstract class BaseConfirmationInput extends BaseInput<ConfirmationInputSpec> implements ConfirmationInputSpec {
|
||||
|
||||
private String name;
|
||||
private Boolean defaultValue;
|
||||
private Function<ConfirmationInputContext, List<AttributedString>> renderer;
|
||||
private List<Consumer<ConfirmationInputContext>> preHandlers = new ArrayList<>();
|
||||
private List<Consumer<ConfirmationInputContext>> postHandlers = new ArrayList<>();
|
||||
private boolean storeResult = true;
|
||||
private String templateLocation;
|
||||
private Function<ConfirmationInputContext, String> next;
|
||||
|
||||
public BaseConfirmationInput(BaseBuilder builder, String id) {
|
||||
super(builder, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfirmationInputSpec name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfirmationInputSpec defaultValue(Boolean defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfirmationInputSpec renderer(Function<ConfirmationInputContext, List<AttributedString>> renderer) {
|
||||
this.renderer = renderer;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfirmationInputSpec template(String location) {
|
||||
this.templateLocation = location;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfirmationInputSpec preHandler(Consumer<ConfirmationInputContext> handler) {
|
||||
this.preHandlers.add(handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfirmationInputSpec postHandler(Consumer<ConfirmationInputContext> handler) {
|
||||
this.postHandlers.add(handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfirmationInputSpec storeResult(boolean store) {
|
||||
this.storeResult = store;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfirmationInputSpec next(Function<ConfirmationInputContext, String> next) {
|
||||
this.next = next;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder and() {
|
||||
getBuilder().addConfirmationInput(this);
|
||||
return getBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfirmationInputSpec getThis() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public boolean getDefaultValue() {
|
||||
return defaultValue != null ? defaultValue : true;
|
||||
}
|
||||
|
||||
public Function<ConfirmationInputContext, List<AttributedString>> getRenderer() {
|
||||
return renderer;
|
||||
}
|
||||
|
||||
public String getTemplateLocation() {
|
||||
return templateLocation;
|
||||
}
|
||||
|
||||
public List<Consumer<ConfirmationInputContext>> getPreHandlers() {
|
||||
return preHandlers;
|
||||
}
|
||||
|
||||
public List<Consumer<ConfirmationInputContext>> getPostHandlers() {
|
||||
return postHandlers;
|
||||
}
|
||||
|
||||
public boolean isStoreResult() {
|
||||
return storeResult;
|
||||
}
|
||||
|
||||
public Function<ConfirmationInputContext, String> getNext() {
|
||||
return next;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.component.flow;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.shell.component.flow.ComponentFlow.BaseBuilder;
|
||||
|
||||
/**
|
||||
* Base impl for specs.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public abstract class BaseInput<T extends BaseInputSpec<T>> implements Ordered, BaseInputSpec<T> {
|
||||
|
||||
private final BaseBuilder builder;
|
||||
private final String id;
|
||||
private int order;
|
||||
|
||||
BaseInput(BaseBuilder builder, String id) {
|
||||
this.builder = builder;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T order(int order) {
|
||||
this.order = order;
|
||||
return getThis();
|
||||
}
|
||||
|
||||
public BaseBuilder getBuilder() {
|
||||
return builder;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.component.flow;
|
||||
|
||||
/**
|
||||
* Base spec for other specs.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public interface BaseInputSpec<T extends BaseInputSpec<T>> {
|
||||
|
||||
/**
|
||||
* Sets order of this component.
|
||||
*
|
||||
* @param order the order
|
||||
* @return a builder
|
||||
*/
|
||||
T order(int order);
|
||||
|
||||
/**
|
||||
* Usual this trick to get typed child.
|
||||
*
|
||||
* @return a builder
|
||||
*/
|
||||
T getThis();
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.component.flow;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jline.utils.AttributedString;
|
||||
|
||||
import org.springframework.shell.component.MultiItemSelector.MultiItemSelectorContext;
|
||||
import org.springframework.shell.component.flow.ComponentFlow.BaseBuilder;
|
||||
import org.springframework.shell.component.flow.ComponentFlow.Builder;
|
||||
import org.springframework.shell.component.support.SelectorItem;
|
||||
|
||||
/**
|
||||
* Base impl for {@link MultiItemSelectorSpec}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public abstract class BaseMultiItemSelector extends BaseInput<MultiItemSelectorSpec> implements MultiItemSelectorSpec {
|
||||
|
||||
private String name;
|
||||
private List<String> resultValues = new ArrayList<>();
|
||||
private ResultMode resultMode;
|
||||
private List<SelectItem> selectItems = new ArrayList<>();
|
||||
private Comparator<SelectorItem<String>> comparator;
|
||||
private Function<MultiItemSelectorContext<String, SelectorItem<String>>, List<AttributedString>> renderer;
|
||||
private Integer maxItems;
|
||||
private List<Consumer<MultiItemSelectorContext<String, SelectorItem<String>>>> preHandlers = new ArrayList<>();
|
||||
private List<Consumer<MultiItemSelectorContext<String, SelectorItem<String>>>> postHandlers = new ArrayList<>();
|
||||
private boolean storeResult = true;
|
||||
private String templateLocation;
|
||||
private Function<MultiItemSelectorContext<String, SelectorItem<String>>, String> next;
|
||||
|
||||
public BaseMultiItemSelector(BaseBuilder builder, String id) {
|
||||
super(builder, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiItemSelectorSpec name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiItemSelectorSpec resultValues(List<String> resultValues) {
|
||||
this.resultValues.addAll(resultValues);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiItemSelectorSpec resultMode(ResultMode resultMode) {
|
||||
this.resultMode = resultMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiItemSelectorSpec selectItems(List<SelectItem> selectItems) {
|
||||
this.selectItems = selectItems;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiItemSelectorSpec sort(Comparator<SelectorItem<String>> comparator) {
|
||||
this.comparator = comparator;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiItemSelectorSpec renderer(Function<MultiItemSelectorContext<String, SelectorItem<String>>, List<AttributedString>> renderer) {
|
||||
this.renderer = renderer;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiItemSelectorSpec template(String location) {
|
||||
this.templateLocation = location;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiItemSelectorSpec max(int max) {
|
||||
this.maxItems = max;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiItemSelectorSpec preHandler(Consumer<MultiItemSelectorContext<String, SelectorItem<String>>> handler) {
|
||||
this.preHandlers.add(handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiItemSelectorSpec postHandler(Consumer<MultiItemSelectorContext<String, SelectorItem<String>>> handler) {
|
||||
this.postHandlers.add(handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiItemSelectorSpec storeResult(boolean store) {
|
||||
this.storeResult = store;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiItemSelectorSpec next(
|
||||
Function<MultiItemSelectorContext<String, SelectorItem<String>>, String> next) {
|
||||
this.next = next;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder and() {
|
||||
getBuilder().addMultiItemSelector(this);
|
||||
return getBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiItemSelectorSpec getThis() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public List<String> getResultValues() {
|
||||
return resultValues;
|
||||
}
|
||||
|
||||
public ResultMode getResultMode() {
|
||||
return resultMode;
|
||||
}
|
||||
|
||||
public List<SelectItem> getSelectItems() {
|
||||
return selectItems;
|
||||
}
|
||||
|
||||
public Comparator<SelectorItem<String>> getComparator() {
|
||||
return comparator;
|
||||
}
|
||||
|
||||
public Function<MultiItemSelectorContext<String, SelectorItem<String>>, List<AttributedString>> getRenderer() {
|
||||
return renderer;
|
||||
}
|
||||
|
||||
public String getTemplateLocation() {
|
||||
return templateLocation;
|
||||
}
|
||||
|
||||
public Integer getMaxItems() {
|
||||
return maxItems;
|
||||
}
|
||||
|
||||
public List<Consumer<MultiItemSelectorContext<String, SelectorItem<String>>>> getPreHandlers() {
|
||||
return preHandlers;
|
||||
}
|
||||
|
||||
public List<Consumer<MultiItemSelectorContext<String, SelectorItem<String>>>> getPostHandlers() {
|
||||
return postHandlers;
|
||||
}
|
||||
|
||||
public boolean isStoreResult() {
|
||||
return storeResult;
|
||||
}
|
||||
|
||||
public Function<MultiItemSelectorContext<String, SelectorItem<String>>, String> getNext() {
|
||||
return next;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.component.flow;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jline.utils.AttributedString;
|
||||
|
||||
import org.springframework.shell.component.PathInput.PathInputContext;
|
||||
import org.springframework.shell.component.flow.ComponentFlow.BaseBuilder;
|
||||
import org.springframework.shell.component.flow.ComponentFlow.Builder;
|
||||
|
||||
/**
|
||||
* Base impl for {@link PathInputSpec}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public abstract class BasePathInput extends BaseInput<PathInputSpec> implements PathInputSpec {
|
||||
|
||||
private String name;
|
||||
private String resultValue;
|
||||
private ResultMode resultMode;
|
||||
private String defaultValue;
|
||||
private Function<PathInputContext, List<AttributedString>> renderer;
|
||||
private List<Consumer<PathInputContext>> preHandlers = new ArrayList<>();
|
||||
private List<Consumer<PathInputContext>> postHandlers = new ArrayList<>();
|
||||
private boolean storeResult = true;
|
||||
private String templateLocation;
|
||||
private Function<PathInputContext, String> next;
|
||||
|
||||
public BasePathInput(BaseBuilder builder, String id) {
|
||||
super(builder, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathInputSpec name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathInputSpec resultValue(String resultValue) {
|
||||
this.resultValue = resultValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathInputSpec resultMode(ResultMode resultMode) {
|
||||
this.resultMode = resultMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathInputSpec defaultValue(String defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathInputSpec renderer(Function<PathInputContext, List<AttributedString>> renderer) {
|
||||
this.renderer = renderer;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathInputSpec template(String location) {
|
||||
this.templateLocation = location;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathInputSpec preHandler(Consumer<PathInputContext> handler) {
|
||||
this.preHandlers.add(handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathInputSpec postHandler(Consumer<PathInputContext> handler) {
|
||||
this.postHandlers.add(handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathInputSpec storeResult(boolean store) {
|
||||
this.storeResult = store;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathInputSpec next(Function<PathInputContext, String> next) {
|
||||
this.next = next;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder and() {
|
||||
getBuilder().addPathInput(this);
|
||||
return getBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathInputSpec getThis() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getResultValue() {
|
||||
return resultValue;
|
||||
}
|
||||
|
||||
public ResultMode getResultMode() {
|
||||
return resultMode;
|
||||
}
|
||||
|
||||
public String getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public Function<PathInputContext, List<AttributedString>> getRenderer() {
|
||||
return renderer;
|
||||
}
|
||||
|
||||
public String getTemplateLocation() {
|
||||
return templateLocation;
|
||||
}
|
||||
|
||||
public List<Consumer<PathInputContext>> getPreHandlers() {
|
||||
return preHandlers;
|
||||
}
|
||||
|
||||
public List<Consumer<PathInputContext>> getPostHandlers() {
|
||||
return postHandlers;
|
||||
}
|
||||
|
||||
public boolean isStoreResult() {
|
||||
return storeResult;
|
||||
}
|
||||
|
||||
public Function<PathInputContext, String> getNext() {
|
||||
return next;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.component.flow;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jline.utils.AttributedString;
|
||||
|
||||
import org.springframework.shell.component.SingleItemSelector.SingleItemSelectorContext;
|
||||
import org.springframework.shell.component.flow.ComponentFlow.BaseBuilder;
|
||||
import org.springframework.shell.component.flow.ComponentFlow.Builder;
|
||||
import org.springframework.shell.component.support.SelectorItem;
|
||||
|
||||
/**
|
||||
* Base impl for {@link SingleItemSelectorSpec}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public abstract class BaseSingleItemSelector extends BaseInput<SingleItemSelectorSpec> implements SingleItemSelectorSpec {
|
||||
|
||||
private String name;
|
||||
private String resultValue;
|
||||
private ResultMode resultMode;
|
||||
private Map<String, String> selectItems = new HashMap<>();
|
||||
private Comparator<SelectorItem<String>> comparator;
|
||||
private Function<SingleItemSelectorContext<String, SelectorItem<String>>, List<AttributedString>> renderer;
|
||||
private Integer maxItems;
|
||||
private List<Consumer<SingleItemSelectorContext<String, SelectorItem<String>>>> preHandlers = new ArrayList<>();
|
||||
private List<Consumer<SingleItemSelectorContext<String, SelectorItem<String>>>> postHandlers = new ArrayList<>();
|
||||
private boolean storeResult = true;
|
||||
private String templateLocation;
|
||||
private Function<SingleItemSelectorContext<String, SelectorItem<String>>, String> next;
|
||||
|
||||
public BaseSingleItemSelector(BaseBuilder builder, String id) {
|
||||
super(builder, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SingleItemSelectorSpec name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SingleItemSelectorSpec resultValue(String resultValue) {
|
||||
this.resultValue = resultValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SingleItemSelectorSpec resultMode(ResultMode resultMode) {
|
||||
this.resultMode = resultMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SingleItemSelectorSpec selectItem(String name, String item) {
|
||||
this.selectItems.put(name, item);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SingleItemSelectorSpec selectItems(Map<String, String> selectItems) {
|
||||
this.selectItems.putAll(selectItems);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SingleItemSelectorSpec sort(Comparator<SelectorItem<String>> comparator) {
|
||||
this.comparator = comparator;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SingleItemSelectorSpec renderer(Function<SingleItemSelectorContext<String, SelectorItem<String>>, List<AttributedString>> renderer) {
|
||||
this.renderer = renderer;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SingleItemSelectorSpec template(String location) {
|
||||
this.templateLocation = location;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SingleItemSelectorSpec max(int max) {
|
||||
this.maxItems = max;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SingleItemSelectorSpec preHandler(Consumer<SingleItemSelectorContext<String, SelectorItem<String>>> handler) {
|
||||
this.preHandlers.add(handler);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SingleItemSelectorSpec postHandler(Consumer<SingleItemSelectorContext<String, SelectorItem<String>>> handler) {
|
||||
this.postHandlers.add(handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SingleItemSelectorSpec storeResult(boolean store) {
|
||||
this.storeResult = store;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SingleItemSelectorSpec next(
|
||||
Function<SingleItemSelectorContext<String, SelectorItem<String>>, String> next) {
|
||||
this.next = next;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder and() {
|
||||
getBuilder().addSingleItemSelector(this);
|
||||
return getBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SingleItemSelectorSpec getThis() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getResultValue() {
|
||||
return resultValue;
|
||||
}
|
||||
|
||||
public ResultMode getResultMode() {
|
||||
return resultMode;
|
||||
}
|
||||
|
||||
public Map<String, String> getSelectItems() {
|
||||
return selectItems;
|
||||
}
|
||||
|
||||
public Comparator<SelectorItem<String>> getComparator() {
|
||||
return comparator;
|
||||
}
|
||||
|
||||
public Function<SingleItemSelectorContext<String, SelectorItem<String>>, List<AttributedString>> getRenderer() {
|
||||
return renderer;
|
||||
}
|
||||
|
||||
public String getTemplateLocation() {
|
||||
return templateLocation;
|
||||
}
|
||||
|
||||
public Integer getMaxItems() {
|
||||
return maxItems;
|
||||
}
|
||||
|
||||
public List<Consumer<SingleItemSelectorContext<String, SelectorItem<String>>>> getPreHandlers() {
|
||||
return preHandlers;
|
||||
}
|
||||
|
||||
public List<Consumer<SingleItemSelectorContext<String, SelectorItem<String>>>> getPostHandlers() {
|
||||
return postHandlers;
|
||||
}
|
||||
|
||||
public boolean isStoreResult() {
|
||||
return storeResult;
|
||||
}
|
||||
|
||||
public Function<SingleItemSelectorContext<String, SelectorItem<String>>, String> getNext() {
|
||||
return next;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.component.flow;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jline.utils.AttributedString;
|
||||
|
||||
import org.springframework.shell.component.StringInput.StringInputContext;
|
||||
import org.springframework.shell.component.flow.ComponentFlow.BaseBuilder;
|
||||
import org.springframework.shell.component.flow.ComponentFlow.Builder;
|
||||
|
||||
/**
|
||||
* Base impl for {@link StringInputSpec}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public abstract class BaseStringInput extends BaseInput<StringInputSpec> implements StringInputSpec {
|
||||
|
||||
private String name;
|
||||
private String resultValue;
|
||||
private ResultMode resultMode;
|
||||
private String defaultValue;
|
||||
private Character maskCharacter;
|
||||
private Function<StringInputContext, List<AttributedString>> renderer;
|
||||
private List<Consumer<StringInputContext>> preHandlers = new ArrayList<>();
|
||||
private List<Consumer<StringInputContext>> postHandlers = new ArrayList<>();
|
||||
private boolean storeResult = true;
|
||||
private String templateLocation;
|
||||
private Function<StringInputContext, String> next;
|
||||
|
||||
public BaseStringInput(BaseBuilder builder, String id) {
|
||||
super(builder, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringInputSpec name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringInputSpec resultValue(String resultValue) {
|
||||
this.resultValue = resultValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringInputSpec resultMode(ResultMode resultMode) {
|
||||
this.resultMode = resultMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringInputSpec defaultValue(String defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringInputSpec maskCharacter(Character maskCharacter) {
|
||||
this.maskCharacter = maskCharacter;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringInputSpec renderer(Function<StringInputContext, List<AttributedString>> renderer) {
|
||||
this.renderer = renderer;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringInputSpec template(String location) {
|
||||
this.templateLocation = location;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringInputSpec preHandler(Consumer<StringInputContext> handler) {
|
||||
this.preHandlers.add(handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringInputSpec postHandler(Consumer<StringInputContext> handler) {
|
||||
this.postHandlers.add(handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringInputSpec storeResult(boolean store) {
|
||||
this.storeResult = store;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringInputSpec next(Function<StringInputContext, String> next) {
|
||||
this.next = next;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder and() {
|
||||
getBuilder().addStringInput(this);
|
||||
return getBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringInputSpec getThis() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getResultValue() {
|
||||
return resultValue;
|
||||
}
|
||||
|
||||
public ResultMode getResultMode() {
|
||||
return resultMode;
|
||||
}
|
||||
|
||||
public String getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public Character getMaskCharacter() {
|
||||
return maskCharacter;
|
||||
}
|
||||
|
||||
public Function<StringInputContext, List<AttributedString>> getRenderer() {
|
||||
return renderer;
|
||||
}
|
||||
|
||||
public String getTemplateLocation() {
|
||||
return templateLocation;
|
||||
}
|
||||
|
||||
public List<Consumer<StringInputContext>> getPreHandlers() {
|
||||
return preHandlers;
|
||||
}
|
||||
|
||||
public List<Consumer<StringInputContext>> getPostHandlers() {
|
||||
return postHandlers;
|
||||
}
|
||||
|
||||
public boolean isStoreResult() {
|
||||
return storeResult;
|
||||
}
|
||||
|
||||
public Function<StringInputContext, String> getNext() {
|
||||
return next;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,628 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.component.flow;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.jline.terminal.Terminal;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.core.OrderComparator;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.shell.component.ConfirmationInput;
|
||||
import org.springframework.shell.component.MultiItemSelector;
|
||||
import org.springframework.shell.component.MultiItemSelector.MultiItemSelectorContext;
|
||||
import org.springframework.shell.component.PathInput;
|
||||
import org.springframework.shell.component.PathInput.PathInputContext;
|
||||
import org.springframework.shell.component.SingleItemSelector;
|
||||
import org.springframework.shell.component.SingleItemSelector.SingleItemSelectorContext;
|
||||
import org.springframework.shell.component.StringInput;
|
||||
import org.springframework.shell.component.ConfirmationInput.ConfirmationInputContext;
|
||||
import org.springframework.shell.component.StringInput.StringInputContext;
|
||||
import org.springframework.shell.component.context.ComponentContext;
|
||||
import org.springframework.shell.component.support.SelectorItem;
|
||||
import org.springframework.shell.style.TemplateExecutor;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Wizart providing an implementation which allows more polished way to ask various inputs
|
||||
* from a user using shell style components for simple text/path input, single select and
|
||||
* multi-select.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public interface ComponentFlow {
|
||||
|
||||
/**
|
||||
* Run a wizard and returns a result from it.
|
||||
*
|
||||
* @return the input wizard result
|
||||
*/
|
||||
ComponentFlowResult run();
|
||||
|
||||
/**
|
||||
* Gets a new instance of an input wizard builder.
|
||||
*
|
||||
* @param terminal the terminal
|
||||
* @return the input wizard builder
|
||||
*/
|
||||
public static Builder builder(Terminal terminal) {
|
||||
return new DefaultBuilder(terminal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Results from a flow run.
|
||||
*/
|
||||
interface ComponentFlowResult {
|
||||
|
||||
/**
|
||||
* Gets a context.
|
||||
*
|
||||
* @return a context
|
||||
*/
|
||||
ComponentContext<?> getContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for a wizard builder.
|
||||
*/
|
||||
interface Builder {
|
||||
|
||||
/**
|
||||
* Gets a builder for string input.
|
||||
*
|
||||
* @param id the identifier
|
||||
* @return builder for string input
|
||||
*/
|
||||
StringInputSpec withStringInput(String id);
|
||||
|
||||
/**
|
||||
* Gets a builder for path input.
|
||||
*
|
||||
* @param id the identifier
|
||||
* @return builder for text input
|
||||
*/
|
||||
PathInputSpec withPathInput(String id);
|
||||
|
||||
/**
|
||||
* Gets a builder for confirmation input.
|
||||
*
|
||||
* @param id the identifier
|
||||
* @return builder for text input
|
||||
*/
|
||||
ConfirmationInputSpec withConfirmationInput(String id);
|
||||
|
||||
/**
|
||||
* Gets a builder for single item selector.
|
||||
*
|
||||
* @param id the identifier
|
||||
* @return builder for single item selector
|
||||
*/
|
||||
SingleItemSelectorSpec withSingleItemSelector(String id);
|
||||
|
||||
/**
|
||||
* Gets a builder for multi item selector.
|
||||
*
|
||||
* @param id the identifier
|
||||
* @return builder for multi item selector
|
||||
*/
|
||||
MultiItemSelectorSpec withMultiItemSelector(String id);
|
||||
|
||||
/**
|
||||
* Sets a {@link ResourceLoader}.
|
||||
*
|
||||
* @param resourceLoader the resource loader
|
||||
* @return a builder
|
||||
*/
|
||||
Builder resourceLoader(ResourceLoader resourceLoader);
|
||||
|
||||
/**
|
||||
* Sets a {@link TemplateExecutor}.
|
||||
*
|
||||
* @param templateExecutor the template executor
|
||||
* @return a builder
|
||||
*/
|
||||
Builder templateExecutor(TemplateExecutor templateExecutor);
|
||||
|
||||
/**
|
||||
* Builds instance of input wizard.
|
||||
*
|
||||
* @return instance of input wizard
|
||||
*/
|
||||
ComponentFlow build();
|
||||
}
|
||||
|
||||
static abstract class BaseBuilder implements Builder {
|
||||
|
||||
private Terminal terminal;
|
||||
private final List<BaseStringInput> stringInputs = new ArrayList<>();
|
||||
private final List<BasePathInput> pathInputs = new ArrayList<>();
|
||||
private final List<BaseConfirmationInput> confirmationInputs = new ArrayList<>();
|
||||
private final List<BaseSingleItemSelector> singleItemSelectors = new ArrayList<>();
|
||||
private final List<BaseMultiItemSelector> multiItemSelectors = new ArrayList<>();
|
||||
private final AtomicInteger order = new AtomicInteger();
|
||||
private final HashSet<String> uniqueIds = new HashSet<>();
|
||||
private ResourceLoader resourceLoader;
|
||||
private TemplateExecutor templateExecutor;
|
||||
|
||||
BaseBuilder(Terminal terminal) {
|
||||
this.terminal = terminal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComponentFlow build() {
|
||||
return new DefaultComponentFlow(terminal, resourceLoader, templateExecutor, stringInputs, pathInputs,
|
||||
confirmationInputs, singleItemSelectors, multiItemSelectors);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringInputSpec withStringInput(String id) {
|
||||
return new DefaultStringInputSpec(this, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathInputSpec withPathInput(String id) {
|
||||
return new DefaultPathInputSpec(this, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfirmationInputSpec withConfirmationInput(String id) {
|
||||
return new DefaultConfirmationInputSpec(this, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SingleItemSelectorSpec withSingleItemSelector(String id) {
|
||||
return new DefaultSingleInputSpec(this, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiItemSelectorSpec withMultiItemSelector(String id) {
|
||||
return new DefaultMultiInputSpec(this, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder resourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder templateExecutor(TemplateExecutor templateExecutor) {
|
||||
this.templateExecutor = templateExecutor;
|
||||
return this;
|
||||
}
|
||||
|
||||
void addStringInput(BaseStringInput input) {
|
||||
checkUniqueId(input.getId());
|
||||
input.setOrder(order.getAndIncrement());
|
||||
stringInputs.add(input);
|
||||
}
|
||||
|
||||
void addPathInput(BasePathInput input) {
|
||||
checkUniqueId(input.getId());
|
||||
input.setOrder(order.getAndIncrement());
|
||||
pathInputs.add(input);
|
||||
}
|
||||
|
||||
void addConfirmationInput(BaseConfirmationInput input) {
|
||||
checkUniqueId(input.getId());
|
||||
input.setOrder(order.getAndIncrement());
|
||||
confirmationInputs.add(input);
|
||||
}
|
||||
|
||||
void addSingleItemSelector(BaseSingleItemSelector input) {
|
||||
checkUniqueId(input.getId());
|
||||
input.setOrder(order.getAndIncrement());
|
||||
singleItemSelectors.add(input);
|
||||
}
|
||||
|
||||
void addMultiItemSelector(BaseMultiItemSelector input) {
|
||||
checkUniqueId(input.getId());
|
||||
input.setOrder(order.getAndIncrement());
|
||||
multiItemSelectors.add(input);
|
||||
}
|
||||
|
||||
ResourceLoader getResourceLoader() {
|
||||
return resourceLoader;
|
||||
}
|
||||
|
||||
TemplateExecutor getTemplateExecutor() {
|
||||
return templateExecutor;
|
||||
}
|
||||
|
||||
private void checkUniqueId(String id) {
|
||||
if (uniqueIds.contains(id)) {
|
||||
throw new IllegalArgumentException(String.format("Component with id %s is already registered", id));
|
||||
}
|
||||
uniqueIds.add(id);
|
||||
}
|
||||
}
|
||||
|
||||
static class DefaultBuilder extends BaseBuilder {
|
||||
|
||||
DefaultBuilder(Terminal terminal) {
|
||||
super(terminal);
|
||||
}
|
||||
}
|
||||
|
||||
static class DefaultComponentFlowResult implements ComponentFlowResult {
|
||||
|
||||
private ComponentContext<?> context;
|
||||
|
||||
DefaultComponentFlowResult(ComponentContext<?> context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public ComponentContext<?> getContext() {
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
||||
static class DefaultComponentFlow implements ComponentFlow {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(DefaultComponentFlow.class);
|
||||
private final Terminal terminal;
|
||||
private final List<BaseStringInput> stringInputs;
|
||||
private final List<BasePathInput> pathInputs;
|
||||
private final List<BaseConfirmationInput> confirmationInputs;
|
||||
private final List<BaseSingleItemSelector> singleInputs;
|
||||
private final List<BaseMultiItemSelector> multiInputs;
|
||||
private final ResourceLoader resourceLoader;
|
||||
private final TemplateExecutor templateExecutor;
|
||||
|
||||
DefaultComponentFlow(Terminal terminal, ResourceLoader resourceLoader, TemplateExecutor templateExecutor,
|
||||
List<BaseStringInput> stringInputs, List<BasePathInput> pathInputs, List<BaseConfirmationInput> confirmationInputs,
|
||||
List<BaseSingleItemSelector> singleInputs, List<BaseMultiItemSelector> multiInputs) {
|
||||
this.terminal = terminal;
|
||||
this.resourceLoader = resourceLoader;
|
||||
this.templateExecutor = templateExecutor;
|
||||
this.stringInputs = stringInputs;
|
||||
this.pathInputs = pathInputs;
|
||||
this.confirmationInputs = confirmationInputs;
|
||||
this.singleInputs = singleInputs;
|
||||
this.multiInputs = multiInputs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComponentFlowResult run() {
|
||||
return runGetResults();
|
||||
}
|
||||
|
||||
private static class OrderedInputOperationList {
|
||||
|
||||
private final Map<String, Node> map = new HashMap<>();
|
||||
private Node first;
|
||||
|
||||
OrderedInputOperationList(List<OrderedInputOperation> values) {
|
||||
Node ref = null;
|
||||
for (OrderedInputOperation oio : values) {
|
||||
Node node = new Node(oio);
|
||||
map.put(oio.id, node);
|
||||
if (ref != null) {
|
||||
ref.next = node;
|
||||
}
|
||||
ref = node;
|
||||
if (first == null) {
|
||||
first = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Node get(String id) {
|
||||
return map.get(id);
|
||||
}
|
||||
|
||||
Node getFirst() {
|
||||
return first;
|
||||
}
|
||||
|
||||
static class Node {
|
||||
OrderedInputOperation data;
|
||||
Node next;
|
||||
Node(OrderedInputOperation data) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DefaultComponentFlowResult runGetResults() {
|
||||
List<OrderedInputOperation> oios = Stream
|
||||
.of(stringInputsStream(), pathInputsStream(), confirmationInputsStream(),
|
||||
singleItemSelectorsStream(), multiItemSelectorsStream())
|
||||
.flatMap(oio -> oio)
|
||||
.sorted(OrderComparator.INSTANCE)
|
||||
.collect(Collectors.toList());
|
||||
OrderedInputOperationList oiol = new OrderedInputOperationList(oios);
|
||||
ComponentContext<?> context = ComponentContext.empty();
|
||||
|
||||
OrderedInputOperationList.Node node = oiol.getFirst();
|
||||
while (node != null) {
|
||||
log.debug("Calling apply for {}", node.data.id);
|
||||
context = node.data.getOperation().apply(context);
|
||||
if (node.data.next != null) {
|
||||
Optional<String> n = node.data.next.apply(context);
|
||||
if (n.isPresent()) {
|
||||
node = oiol.get(n.get());
|
||||
}
|
||||
else {
|
||||
node = node.next;
|
||||
}
|
||||
}
|
||||
else {
|
||||
node = node.next;
|
||||
}
|
||||
}
|
||||
return new DefaultComponentFlowResult(context);
|
||||
}
|
||||
|
||||
private Stream<OrderedInputOperation> stringInputsStream() {
|
||||
return stringInputs.stream().map(input -> {
|
||||
StringInput selector = new StringInput(terminal, input.getName(), input.getDefaultValue());
|
||||
Function<ComponentContext<?>, ComponentContext<?>> operation = (context) -> {
|
||||
if (input.getResultMode() == ResultMode.ACCEPT && input.isStoreResult()
|
||||
&& StringUtils.hasText(input.getResultValue())) {
|
||||
context.put(input.getId(), input.getResultValue());
|
||||
return context;
|
||||
}
|
||||
selector.setResourceLoader(resourceLoader);
|
||||
selector.setTemplateExecutor(templateExecutor);
|
||||
selector.setMaskCharater(input.getMaskCharacter());
|
||||
if (StringUtils.hasText(input.getTemplateLocation())) {
|
||||
selector.setTemplateLocation(input.getTemplateLocation());
|
||||
}
|
||||
if (input.getRenderer() != null) {
|
||||
selector.setRenderer(input.getRenderer());
|
||||
}
|
||||
if (input.isStoreResult()) {
|
||||
if (input.getResultMode() == ResultMode.VERIFY && StringUtils.hasText(input.getResultValue())) {
|
||||
selector.addPreRunHandler(c -> {
|
||||
c.setDefaultValue(input.getResultValue());
|
||||
});
|
||||
}
|
||||
selector.addPostRunHandler(c -> {
|
||||
c.put(input.getId(), c.getResultValue());
|
||||
});
|
||||
}
|
||||
for (Consumer<StringInputContext> handler : input.getPreHandlers()) {
|
||||
selector.addPreRunHandler(handler);
|
||||
}
|
||||
for (Consumer<StringInputContext> handler : input.getPostHandlers()) {
|
||||
selector.addPostRunHandler(handler);
|
||||
}
|
||||
return selector.run(context);
|
||||
};
|
||||
Function<StringInputContext, String> f1 = input.getNext();
|
||||
Function<ComponentContext<?>, Optional<String>> f2 = context -> f1 != null
|
||||
? Optional.ofNullable(f1.apply(selector.getThisContext(context)))
|
||||
: Optional.empty();
|
||||
return OrderedInputOperation.of(input.getId(), input.getOrder(), operation, f2);
|
||||
});
|
||||
}
|
||||
|
||||
private Stream<OrderedInputOperation> pathInputsStream() {
|
||||
return pathInputs.stream().map(input -> {
|
||||
PathInput selector = new PathInput(terminal, input.getName());
|
||||
Function<ComponentContext<?>, ComponentContext<?>> operation = (context) -> {
|
||||
if (input.getResultMode() == ResultMode.ACCEPT && input.isStoreResult()
|
||||
&& StringUtils.hasText(input.getResultValue())) {
|
||||
context.put(input.getId(), Paths.get(input.getResultValue()));
|
||||
return context;
|
||||
}
|
||||
selector.setResourceLoader(resourceLoader);
|
||||
selector.setTemplateExecutor(templateExecutor);
|
||||
if (StringUtils.hasText(input.getTemplateLocation())) {
|
||||
selector.setTemplateLocation(input.getTemplateLocation());
|
||||
}
|
||||
if (input.getRenderer() != null) {
|
||||
selector.setRenderer(input.getRenderer());
|
||||
}
|
||||
if (input.isStoreResult()) {
|
||||
selector.addPostRunHandler(c -> {
|
||||
c.put(input.getId(), c.getResultValue());
|
||||
});
|
||||
}
|
||||
for (Consumer<PathInputContext> handler : input.getPreHandlers()) {
|
||||
selector.addPreRunHandler(handler);
|
||||
}
|
||||
for (Consumer<PathInputContext> handler : input.getPostHandlers()) {
|
||||
selector.addPostRunHandler(handler);
|
||||
}
|
||||
return selector.run(context);
|
||||
};
|
||||
Function<PathInputContext, String> f1 = input.getNext();
|
||||
Function<ComponentContext<?>, Optional<String>> f2 = context -> f1 != null
|
||||
? Optional.ofNullable(f1.apply(selector.getThisContext(context)))
|
||||
: Optional.empty();
|
||||
return OrderedInputOperation.of(input.getId(), input.getOrder(), operation, f2);
|
||||
});
|
||||
}
|
||||
|
||||
private Stream<OrderedInputOperation> confirmationInputsStream() {
|
||||
return confirmationInputs.stream().map(input -> {
|
||||
ConfirmationInput selector = new ConfirmationInput(terminal, input.getName(), input.getDefaultValue());
|
||||
Function<ComponentContext<?>, ComponentContext<?>> operation = (context) -> {
|
||||
selector.setResourceLoader(resourceLoader);
|
||||
selector.setTemplateExecutor(templateExecutor);
|
||||
if (StringUtils.hasText(input.getTemplateLocation())) {
|
||||
selector.setTemplateLocation(input.getTemplateLocation());
|
||||
}
|
||||
if (input.getRenderer() != null) {
|
||||
selector.setRenderer(input.getRenderer());
|
||||
}
|
||||
if (input.isStoreResult()) {
|
||||
selector.addPostRunHandler(c -> {
|
||||
c.put(input.getId(), c.getResultValue());
|
||||
});
|
||||
}
|
||||
for (Consumer<ConfirmationInputContext> handler : input.getPreHandlers()) {
|
||||
selector.addPreRunHandler(handler);
|
||||
}
|
||||
for (Consumer<ConfirmationInputContext> handler : input.getPostHandlers()) {
|
||||
selector.addPostRunHandler(handler);
|
||||
}
|
||||
return selector.run(context);
|
||||
};
|
||||
Function<ConfirmationInputContext, String> f1 = input.getNext();
|
||||
Function<ComponentContext<?>, Optional<String>> f2 = context -> f1 != null
|
||||
? Optional.ofNullable(f1.apply(selector.getThisContext(context)))
|
||||
: Optional.empty();
|
||||
return OrderedInputOperation.of(input.getId(), input.getOrder(), operation, f2);
|
||||
});
|
||||
}
|
||||
|
||||
private Stream<OrderedInputOperation> singleItemSelectorsStream() {
|
||||
return singleInputs.stream().map(input -> {
|
||||
List<SelectorItem<String>> selectorItems = input.getSelectItems().entrySet().stream()
|
||||
.map(e -> SelectorItem.of(e.getKey(), e.getValue()))
|
||||
.collect(Collectors.toList());
|
||||
SingleItemSelector<String, SelectorItem<String>> selector = new SingleItemSelector<>(terminal,
|
||||
selectorItems, input.getName(), input.getComparator());
|
||||
Function<ComponentContext<?>, ComponentContext<?>> operation = (context) -> {
|
||||
if (input.getResultMode() == ResultMode.ACCEPT && input.isStoreResult()
|
||||
&& StringUtils.hasText(input.getResultValue())) {
|
||||
context.put(input.getId(), input.getResultValue());
|
||||
return context;
|
||||
}
|
||||
selector.setResourceLoader(resourceLoader);
|
||||
selector.setTemplateExecutor(templateExecutor);
|
||||
if (StringUtils.hasText(input.getTemplateLocation())) {
|
||||
selector.setTemplateLocation(input.getTemplateLocation());
|
||||
}
|
||||
if (input.getRenderer() != null) {
|
||||
selector.setRenderer(input.getRenderer());
|
||||
}
|
||||
if (input.getMaxItems() != null) {
|
||||
selector.setMaxItems(input.getMaxItems());
|
||||
}
|
||||
if (input.isStoreResult()) {
|
||||
selector.addPostRunHandler(c -> {
|
||||
c.getValue().ifPresent(v -> {
|
||||
c.put(input.getId(), v);
|
||||
});
|
||||
});
|
||||
}
|
||||
for (Consumer<SingleItemSelectorContext<String, SelectorItem<String>>> handler : input.getPreHandlers()) {
|
||||
selector.addPreRunHandler(handler);
|
||||
}
|
||||
for (Consumer<SingleItemSelectorContext<String, SelectorItem<String>>> handler : input.getPostHandlers()) {
|
||||
selector.addPostRunHandler(handler);
|
||||
}
|
||||
return selector.run(context);
|
||||
};
|
||||
Function<SingleItemSelectorContext<String, SelectorItem<String>>, String> f1 = input.getNext();
|
||||
Function<ComponentContext<?>, Optional<String>> f2 = context -> f1 != null
|
||||
? Optional.ofNullable(f1.apply(selector.getThisContext(context)))
|
||||
: Optional.empty();
|
||||
return OrderedInputOperation.of(input.getId(), input.getOrder(), operation, f2);
|
||||
});
|
||||
}
|
||||
|
||||
private Stream<OrderedInputOperation> multiItemSelectorsStream() {
|
||||
return multiInputs.stream().map(input -> {
|
||||
List<SelectorItem<String>> selectorItems = input.getSelectItems().stream()
|
||||
.map(si -> SelectorItem.of(si.name(), si.item(), si.enabled()))
|
||||
.collect(Collectors.toList());
|
||||
MultiItemSelector<String, SelectorItem<String>> selector = new MultiItemSelector<>(terminal,
|
||||
selectorItems, input.getName(), input.getComparator());
|
||||
Function<ComponentContext<?>, ComponentContext<?>> operation = (context) -> {
|
||||
if (input.getResultMode() == ResultMode.ACCEPT && input.isStoreResult()
|
||||
&& !ObjectUtils.isEmpty(input.getResultValues())) {
|
||||
context.put(input.getId(), input.getResultValues());
|
||||
return context;
|
||||
}
|
||||
selector.setResourceLoader(resourceLoader);
|
||||
selector.setTemplateExecutor(templateExecutor);
|
||||
if (StringUtils.hasText(input.getTemplateLocation())) {
|
||||
selector.setTemplateLocation(input.getTemplateLocation());
|
||||
}
|
||||
if (input.getRenderer() != null) {
|
||||
selector.setRenderer(input.getRenderer());
|
||||
}
|
||||
if (input.getMaxItems() != null) {
|
||||
selector.setMaxItems(input.getMaxItems());
|
||||
}
|
||||
if (input.isStoreResult()) {
|
||||
selector.addPostRunHandler(c -> {
|
||||
c.put(input.getId(), c.getValues());
|
||||
});
|
||||
}
|
||||
for (Consumer<MultiItemSelectorContext<String, SelectorItem<String>>> handler : input.getPreHandlers()) {
|
||||
selector.addPreRunHandler(handler);
|
||||
}
|
||||
for (Consumer<MultiItemSelectorContext<String, SelectorItem<String>>> handler : input.getPostHandlers()) {
|
||||
selector.addPostRunHandler(handler);
|
||||
}
|
||||
return selector.run(context);
|
||||
};
|
||||
Function<MultiItemSelectorContext<String, SelectorItem<String>>, String> f1 = input.getNext();
|
||||
Function<ComponentContext<?>, Optional<String>> f2 = context -> f1 != null
|
||||
? Optional.ofNullable(f1.apply(selector.getThisContext(context)))
|
||||
: Optional.empty();
|
||||
return OrderedInputOperation.of(input.getId(), input.getOrder(), operation, f2);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static class OrderedInputOperation implements Ordered {
|
||||
|
||||
private String id;
|
||||
private int order;
|
||||
private Function<ComponentContext<?>, ComponentContext<?>> operation;
|
||||
private Function<ComponentContext<?>, Optional<String>> next;
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Function<ComponentContext<?>, ComponentContext<?>> getOperation() {
|
||||
return operation;
|
||||
}
|
||||
|
||||
public Function<ComponentContext<?>, Optional<String>> getNext() {
|
||||
return next;
|
||||
}
|
||||
|
||||
static OrderedInputOperation of(String id, int order,
|
||||
Function<ComponentContext<?>, ComponentContext<?>> operation,
|
||||
Function<ComponentContext<?>, Optional<String>> next) {
|
||||
OrderedInputOperation oio = new OrderedInputOperation();
|
||||
oio.id = id;
|
||||
oio.order = order;
|
||||
oio.operation = operation;
|
||||
oio.next = next;
|
||||
return oio;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.component.flow;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jline.utils.AttributedString;
|
||||
|
||||
import org.springframework.shell.component.ConfirmationInput.ConfirmationInputContext;
|
||||
import org.springframework.shell.component.context.ComponentContext;
|
||||
import org.springframework.shell.component.flow.ComponentFlow.Builder;
|
||||
|
||||
/**
|
||||
* Interface for string input spec builder.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public interface ConfirmationInputSpec extends BaseInputSpec<ConfirmationInputSpec> {
|
||||
|
||||
/**
|
||||
* Sets a name.
|
||||
*
|
||||
* @param name the name
|
||||
* @return a builder
|
||||
*/
|
||||
ConfirmationInputSpec name(String name);
|
||||
|
||||
/**
|
||||
* Sets a default value.
|
||||
*
|
||||
* @param defaultValue the defult value
|
||||
* @return a builder
|
||||
*/
|
||||
ConfirmationInputSpec defaultValue(Boolean defaultValue);
|
||||
|
||||
/**
|
||||
* Sets a renderer function.
|
||||
*
|
||||
* @param renderer the renderer
|
||||
* @return a builder
|
||||
*/
|
||||
ConfirmationInputSpec renderer(Function<ConfirmationInputContext, List<AttributedString>> renderer);
|
||||
|
||||
/**
|
||||
* Sets a default renderer template location.
|
||||
*
|
||||
* @param location the template location
|
||||
* @return a builder
|
||||
*/
|
||||
ConfirmationInputSpec template(String location);
|
||||
|
||||
/**
|
||||
* Adds a pre-run context handler.
|
||||
*
|
||||
* @param handler the context handler
|
||||
* @return a builder
|
||||
*/
|
||||
ConfirmationInputSpec preHandler(Consumer<ConfirmationInputContext> handler);
|
||||
|
||||
/**
|
||||
* Adds a post-run context handler.
|
||||
*
|
||||
* @param handler the context handler
|
||||
* @return a builder
|
||||
*/
|
||||
ConfirmationInputSpec postHandler(Consumer<ConfirmationInputContext> handler);
|
||||
|
||||
/**
|
||||
* Automatically stores result from a {@link ConfirmationInputContext} into
|
||||
* {@link ComponentContext} with key given to builder. Defaults to {@code true}.
|
||||
*
|
||||
* @param store the flag if storing result
|
||||
* @return a builder
|
||||
*/
|
||||
ConfirmationInputSpec storeResult(boolean store);
|
||||
|
||||
/**
|
||||
* Define a function which may return id of a next component to go.
|
||||
*
|
||||
* @param next next component function
|
||||
* @return a builder
|
||||
*/
|
||||
ConfirmationInputSpec next(Function<ConfirmationInputContext, String> next);
|
||||
|
||||
/**
|
||||
* Build and return parent builder.
|
||||
*
|
||||
* @return the parent builder
|
||||
*/
|
||||
Builder and();
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.component.flow;
|
||||
|
||||
import org.springframework.shell.component.flow.ComponentFlow.BaseBuilder;
|
||||
|
||||
/**
|
||||
* Default impl for {@link BaseConfirmationInput}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public class DefaultConfirmationInputSpec extends BaseConfirmationInput {
|
||||
|
||||
public DefaultConfirmationInputSpec(BaseBuilder builder, String id) {
|
||||
super(builder, id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.component.flow;
|
||||
|
||||
import org.springframework.shell.component.flow.ComponentFlow.BaseBuilder;
|
||||
|
||||
/**
|
||||
* Default impl for {@link BaseMultiItemSelector}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public class DefaultMultiInputSpec extends BaseMultiItemSelector {
|
||||
|
||||
public DefaultMultiInputSpec(BaseBuilder builder, String id) {
|
||||
super(builder, id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.component.flow;
|
||||
|
||||
import org.springframework.shell.component.flow.ComponentFlow.BaseBuilder;
|
||||
|
||||
/**
|
||||
* Default impl for {@link BasePathInput}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public class DefaultPathInputSpec extends BasePathInput {
|
||||
|
||||
public DefaultPathInputSpec(BaseBuilder builder, String id) {
|
||||
super(builder, id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.component.flow;
|
||||
|
||||
/**
|
||||
* Default impl for {@link SelectItem}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public class DefaultSelectItem implements SelectItem {
|
||||
|
||||
private String name;
|
||||
private String item;
|
||||
private boolean enabled;
|
||||
|
||||
public DefaultSelectItem(String name, String item, boolean enabled) {
|
||||
this.name = name;
|
||||
this.item = item;
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String item() {
|
||||
return item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enabled() {
|
||||
return enabled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.component.flow;
|
||||
|
||||
import org.springframework.shell.component.flow.ComponentFlow.BaseBuilder;
|
||||
|
||||
/**
|
||||
* Default impl for {@link BaseSingleItemSelector}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public class DefaultSingleInputSpec extends BaseSingleItemSelector {
|
||||
|
||||
public DefaultSingleInputSpec(BaseBuilder builder, String id) {
|
||||
super(builder, id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.component.flow;
|
||||
|
||||
import org.springframework.shell.component.flow.ComponentFlow.BaseBuilder;
|
||||
|
||||
/**
|
||||
* Default impl for {@link BaseStringInput}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public class DefaultStringInputSpec extends BaseStringInput {
|
||||
|
||||
public DefaultStringInputSpec(BaseBuilder builder, String id) {
|
||||
super(builder, id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.component.flow;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jline.utils.AttributedString;
|
||||
|
||||
import org.springframework.shell.component.MultiItemSelector.MultiItemSelectorContext;
|
||||
import org.springframework.shell.component.context.ComponentContext;
|
||||
import org.springframework.shell.component.flow.ComponentFlow.Builder;
|
||||
import org.springframework.shell.component.support.SelectorItem;
|
||||
|
||||
/**
|
||||
* Interface for multi input spec builder.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public interface MultiItemSelectorSpec extends BaseInputSpec<MultiItemSelectorSpec>{
|
||||
|
||||
/**
|
||||
* Sets a name.
|
||||
*
|
||||
* @param name the name
|
||||
* @return a builder
|
||||
*/
|
||||
MultiItemSelectorSpec name(String name);
|
||||
|
||||
/**
|
||||
* Sets a result values.
|
||||
*
|
||||
* @param resultValues the result values
|
||||
* @return a builder
|
||||
*/
|
||||
MultiItemSelectorSpec resultValues(List<String> resultValues);
|
||||
|
||||
/**
|
||||
* Sets a result mode.
|
||||
*
|
||||
* @param resultMode the result mode
|
||||
* @return a builder
|
||||
*/
|
||||
MultiItemSelectorSpec resultMode(ResultMode resultMode);
|
||||
|
||||
/**
|
||||
* Adds a list of select items.
|
||||
*
|
||||
* @param selectItems the select items
|
||||
* @return a builder
|
||||
*/
|
||||
MultiItemSelectorSpec selectItems(List<SelectItem> selectItems);
|
||||
|
||||
/**
|
||||
* Sets a {@link Comparator} for sorting items.
|
||||
*
|
||||
* @param comparator the item comparator
|
||||
* @return a builder
|
||||
*/
|
||||
MultiItemSelectorSpec sort(Comparator<SelectorItem<String>> comparator);
|
||||
|
||||
/**
|
||||
* Sets a renderer function.
|
||||
*
|
||||
* @param renderer the renderer
|
||||
* @return a builder
|
||||
*/
|
||||
MultiItemSelectorSpec renderer(Function<MultiItemSelectorContext<String, SelectorItem<String>>, List<AttributedString>> renderer);
|
||||
|
||||
/**
|
||||
* Sets a default renderer template location.
|
||||
*
|
||||
* @param location the template location
|
||||
* @return a builder
|
||||
*/
|
||||
MultiItemSelectorSpec template(String location);
|
||||
|
||||
/**
|
||||
* Sets a maximum number of items in a selector list;
|
||||
*
|
||||
* @param max the maximum number of items
|
||||
* @return a builder
|
||||
*/
|
||||
MultiItemSelectorSpec max(int max);
|
||||
|
||||
/**
|
||||
* Adds a pre-run context handler.
|
||||
*
|
||||
* @param handler the context handler
|
||||
* @return a builder
|
||||
*/
|
||||
MultiItemSelectorSpec preHandler(Consumer<MultiItemSelectorContext<String, SelectorItem<String>>> handler);
|
||||
|
||||
/**
|
||||
* Adds a post-run context handler.
|
||||
*
|
||||
* @param handler the context handler
|
||||
* @return a builder
|
||||
*/
|
||||
MultiItemSelectorSpec postHandler(Consumer<MultiItemSelectorContext<String, SelectorItem<String>>> handler);
|
||||
|
||||
/**
|
||||
* Automatically stores result from a {@link MultiItemSelectorContext} into
|
||||
* {@link ComponentContext} with key given to builder. Defaults to {@code true}.
|
||||
*
|
||||
* @param store the flag if storing result
|
||||
* @return a builder
|
||||
*/
|
||||
MultiItemSelectorSpec storeResult(boolean store);
|
||||
|
||||
/**
|
||||
* Define a function which may return id of a next component to go.
|
||||
*
|
||||
* @param next next component function
|
||||
* @return a builder
|
||||
*/
|
||||
MultiItemSelectorSpec next(Function<MultiItemSelectorContext<String, SelectorItem<String>>, String> next);
|
||||
|
||||
/**
|
||||
* Build and return parent builder.
|
||||
*
|
||||
* @return the parent builder
|
||||
*/
|
||||
Builder and();
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.component.flow;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jline.utils.AttributedString;
|
||||
|
||||
import org.springframework.shell.component.PathInput.PathInputContext;
|
||||
import org.springframework.shell.component.StringInput.StringInputContext;
|
||||
import org.springframework.shell.component.context.ComponentContext;
|
||||
import org.springframework.shell.component.flow.ComponentFlow.Builder;
|
||||
|
||||
/**
|
||||
* Interface for path input spec builder.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public interface PathInputSpec extends BaseInputSpec<PathInputSpec> {
|
||||
|
||||
/**
|
||||
* Sets a name.
|
||||
*
|
||||
* @param name the name
|
||||
* @return a builder
|
||||
*/
|
||||
PathInputSpec name(String name);
|
||||
|
||||
/**
|
||||
* Sets a result value.
|
||||
*
|
||||
* @param resultValue the result value
|
||||
* @return a builder
|
||||
*/
|
||||
PathInputSpec resultValue(String resultValue);
|
||||
|
||||
/**
|
||||
* Sets a result mode.
|
||||
*
|
||||
* @param resultMode the result mode
|
||||
* @return a builder
|
||||
*/
|
||||
PathInputSpec resultMode(ResultMode resultMode);
|
||||
|
||||
/**
|
||||
* Sets a default value.
|
||||
*
|
||||
* @param defaultValue the defult value
|
||||
* @return a builder
|
||||
*/
|
||||
PathInputSpec defaultValue(String defaultValue);
|
||||
|
||||
/**
|
||||
* Sets a renderer function.
|
||||
*
|
||||
* @param renderer the renderer
|
||||
* @return a builder
|
||||
*/
|
||||
PathInputSpec renderer(Function<PathInputContext, List<AttributedString>> renderer);
|
||||
|
||||
/**
|
||||
* Sets a default renderer template location.
|
||||
*
|
||||
* @param location the template location
|
||||
* @return a builder
|
||||
*/
|
||||
PathInputSpec template(String location);
|
||||
|
||||
/**
|
||||
* Adds a pre-run context handler.
|
||||
*
|
||||
* @param handler the context handler
|
||||
* @return a builder
|
||||
*/
|
||||
PathInputSpec preHandler(Consumer<PathInputContext> handler);
|
||||
|
||||
/**
|
||||
* Adds a post-run context handler.
|
||||
*
|
||||
* @param handler the context handler
|
||||
* @return a builder
|
||||
*/
|
||||
PathInputSpec postHandler(Consumer<PathInputContext> handler);
|
||||
|
||||
/**
|
||||
* Automatically stores result from a {@link StringInputContext} into
|
||||
* {@link ComponentContext} with key given to builder. Defaults to {@code true}.
|
||||
*
|
||||
* @param store the flag if storing result
|
||||
* @return a builder
|
||||
*/
|
||||
PathInputSpec storeResult(boolean store);
|
||||
|
||||
/**
|
||||
* Define a function which may return id of a next component to go.
|
||||
*
|
||||
* @param next next component function
|
||||
* @return a builder
|
||||
*/
|
||||
PathInputSpec next(Function<PathInputContext, String> next);
|
||||
|
||||
/**
|
||||
* Build and return parent builder.
|
||||
*
|
||||
* @return the parent builder
|
||||
*/
|
||||
Builder and();
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.component.flow;
|
||||
|
||||
/**
|
||||
* Enumeration of a modes instructing how {@code resultValue} is handled.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public enum ResultMode {
|
||||
|
||||
/**
|
||||
* Blindly accept a given {@code resultValue} resulting component run to be
|
||||
* skipped and value set as a result automatically.
|
||||
*/
|
||||
ACCEPT,
|
||||
|
||||
/**
|
||||
* Verify a given {@code resultValue} with a component run. It is up to a
|
||||
* component to define how it's done but usually result value is set as a
|
||||
* default value which allows user to just continue.
|
||||
*/
|
||||
VERIFY
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.component.flow;
|
||||
|
||||
/**
|
||||
* Interface for selectitem contract in selectors.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public interface SelectItem {
|
||||
|
||||
/**
|
||||
* Gets a name.
|
||||
*
|
||||
* @return a name
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* Gets an item
|
||||
*
|
||||
* @return an item
|
||||
*/
|
||||
String item();
|
||||
|
||||
/**
|
||||
* Returns if item is enabled.
|
||||
*
|
||||
* @return if item is enabled
|
||||
*/
|
||||
boolean enabled();
|
||||
|
||||
public static SelectItem of(String name, String item) {
|
||||
return of(name, item, true);
|
||||
}
|
||||
|
||||
public static SelectItem of(String name, String item, boolean enabled) {
|
||||
return new DefaultSelectItem(name, item, enabled);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.component.flow;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jline.utils.AttributedString;
|
||||
|
||||
import org.springframework.shell.component.SingleItemSelector.SingleItemSelectorContext;
|
||||
import org.springframework.shell.component.context.ComponentContext;
|
||||
import org.springframework.shell.component.flow.ComponentFlow.Builder;
|
||||
import org.springframework.shell.component.support.SelectorItem;
|
||||
|
||||
/**
|
||||
* Interface for single item selector spec builder.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public interface SingleItemSelectorSpec extends BaseInputSpec<SingleItemSelectorSpec> {
|
||||
|
||||
/**
|
||||
* Sets a name.
|
||||
*
|
||||
* @param name the name
|
||||
* @return a builder
|
||||
*/
|
||||
SingleItemSelectorSpec name(String name);
|
||||
|
||||
/**
|
||||
* Sets a result value.
|
||||
*
|
||||
* @param resultValue the result value
|
||||
* @return a builder
|
||||
*/
|
||||
SingleItemSelectorSpec resultValue(String resultValue);
|
||||
|
||||
/**
|
||||
* Sets a result mode.
|
||||
*
|
||||
* @param resultMode the result mode
|
||||
* @return a builder
|
||||
*/
|
||||
SingleItemSelectorSpec resultMode(ResultMode resultMode);
|
||||
|
||||
/**
|
||||
* Adds a select item.
|
||||
*
|
||||
* @param name the name
|
||||
* @param item the item
|
||||
* @return a builder
|
||||
*/
|
||||
SingleItemSelectorSpec selectItem(String name, String item);
|
||||
|
||||
/**
|
||||
* Adds a map of select items.
|
||||
*
|
||||
* @param selectItems the select items
|
||||
* @return a builder
|
||||
*/
|
||||
SingleItemSelectorSpec selectItems(Map<String, String> selectItems);
|
||||
|
||||
/**
|
||||
* Sets a {@link Comparator} for sorting items.
|
||||
*
|
||||
* @param comparator the item comparator
|
||||
* @return a builder
|
||||
*/
|
||||
SingleItemSelectorSpec sort(Comparator<SelectorItem<String>> comparator);
|
||||
|
||||
/**
|
||||
* Sets a renderer function.
|
||||
*
|
||||
* @param renderer the renderer
|
||||
* @return a builder
|
||||
*/
|
||||
SingleItemSelectorSpec renderer(Function<SingleItemSelectorContext<String, SelectorItem<String>>, List<AttributedString>> renderer);
|
||||
|
||||
/**
|
||||
* Sets a default renderer template location.
|
||||
*
|
||||
* @param location the template location
|
||||
* @return a builder
|
||||
*/
|
||||
SingleItemSelectorSpec template(String location);
|
||||
|
||||
/**
|
||||
* Sets a maximum number of items in a selector list;
|
||||
*
|
||||
* @param max the maximum number of items
|
||||
* @return a builder
|
||||
*/
|
||||
SingleItemSelectorSpec max(int max);
|
||||
|
||||
/**
|
||||
* Adds a pre-run context handler.
|
||||
*
|
||||
* @param handler the context handler
|
||||
* @return a builder
|
||||
*/
|
||||
SingleItemSelectorSpec preHandler(Consumer<SingleItemSelectorContext<String, SelectorItem<String>>> handler);
|
||||
|
||||
/**
|
||||
* Adds a post-run context handler.
|
||||
*
|
||||
* @param handler the context handler
|
||||
* @return a builder
|
||||
*/
|
||||
SingleItemSelectorSpec postHandler(Consumer<SingleItemSelectorContext<String, SelectorItem<String>>> handler);
|
||||
|
||||
/**
|
||||
* Automatically stores result from a {@link SingleItemSelectorContext} into
|
||||
* {@link ComponentContext} with key given to builder. Defaults to {@code true}.
|
||||
*
|
||||
* @param store the flag if storing result
|
||||
* @return a builder
|
||||
*/
|
||||
SingleItemSelectorSpec storeResult(boolean store);
|
||||
|
||||
/**
|
||||
* Define a function which may return id of a next component to go.
|
||||
*
|
||||
* @param next next component function
|
||||
* @return a builder
|
||||
*/
|
||||
SingleItemSelectorSpec next(Function<SingleItemSelectorContext<String, SelectorItem<String>>, String> next);
|
||||
|
||||
/**
|
||||
* Build and return parent builder.
|
||||
*
|
||||
* @return the parent builder
|
||||
*/
|
||||
Builder and();
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.component.flow;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jline.utils.AttributedString;
|
||||
|
||||
import org.springframework.shell.component.StringInput.StringInputContext;
|
||||
import org.springframework.shell.component.context.ComponentContext;
|
||||
import org.springframework.shell.component.flow.ComponentFlow.Builder;
|
||||
|
||||
/**
|
||||
* Interface for string input spec builder.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public interface StringInputSpec extends BaseInputSpec<StringInputSpec> {
|
||||
|
||||
/**
|
||||
* Sets a name.
|
||||
*
|
||||
* @param name the name
|
||||
* @return a builder
|
||||
*/
|
||||
StringInputSpec name(String name);
|
||||
|
||||
/**
|
||||
* Sets a result value.
|
||||
*
|
||||
* @param resultValue the result value
|
||||
* @return a builder
|
||||
*/
|
||||
StringInputSpec resultValue(String resultValue);
|
||||
|
||||
/**
|
||||
* Sets a result mode.
|
||||
*
|
||||
* @param resultMode the result mode
|
||||
* @return a builder
|
||||
*/
|
||||
StringInputSpec resultMode(ResultMode resultMode);
|
||||
|
||||
/**
|
||||
* Sets a default value.
|
||||
*
|
||||
* @param defaultValue the defult value
|
||||
* @return a builder
|
||||
*/
|
||||
StringInputSpec defaultValue(String defaultValue);
|
||||
|
||||
/**
|
||||
* Sets a mask character.
|
||||
*
|
||||
* @param maskCharacter the mask character
|
||||
* @return a builder
|
||||
*/
|
||||
StringInputSpec maskCharacter(Character maskCharacter);
|
||||
|
||||
/**
|
||||
* Sets a renderer function.
|
||||
*
|
||||
* @param renderer the renderer
|
||||
* @return a builder
|
||||
*/
|
||||
StringInputSpec renderer(Function<StringInputContext, List<AttributedString>> renderer);
|
||||
|
||||
/**
|
||||
* Sets a default renderer template location.
|
||||
*
|
||||
* @param location the template location
|
||||
* @return a builder
|
||||
*/
|
||||
StringInputSpec template(String location);
|
||||
|
||||
/**
|
||||
* Adds a pre-run context handler.
|
||||
*
|
||||
* @param handler the context handler
|
||||
* @return a builder
|
||||
*/
|
||||
StringInputSpec preHandler(Consumer<StringInputContext> handler);
|
||||
|
||||
/**
|
||||
* Adds a post-run context handler.
|
||||
*
|
||||
* @param handler the context handler
|
||||
* @return a builder
|
||||
*/
|
||||
StringInputSpec postHandler(Consumer<StringInputContext> handler);
|
||||
|
||||
/**
|
||||
* Automatically stores result from a {@link StringInputContext} into
|
||||
* {@link ComponentContext} with key given to builder. Defaults to {@code true}.
|
||||
*
|
||||
* @param store the flag if storing result
|
||||
* @return a builder
|
||||
*/
|
||||
StringInputSpec storeResult(boolean store);
|
||||
|
||||
/**
|
||||
* Define a function which may return id of a next component to go.
|
||||
*
|
||||
* @param next next component function
|
||||
* @return a builder
|
||||
*/
|
||||
StringInputSpec next(Function<StringInputContext, String> next);
|
||||
|
||||
/**
|
||||
* Build and return parent builder.
|
||||
*
|
||||
* @return the parent builder
|
||||
*/
|
||||
Builder and();
|
||||
}
|
||||
@@ -219,7 +219,7 @@ public abstract class AbstractComponent<T extends ComponentContext<T>> implement
|
||||
* @param context the context
|
||||
* @return a component context
|
||||
*/
|
||||
protected abstract T getThisContext(ComponentContext<?> context);
|
||||
public abstract T getThisContext(ComponentContext<?> context);
|
||||
|
||||
/**
|
||||
* Read input.
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.component.flow;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import org.jline.keymap.KeyMap;
|
||||
import org.jline.terminal.Terminal;
|
||||
import org.jline.terminal.impl.DumbTerminal;
|
||||
import org.jline.utils.AttributedString;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.shell.style.TemplateExecutor;
|
||||
import org.springframework.shell.style.Theme;
|
||||
import org.springframework.shell.style.ThemeRegistry;
|
||||
import org.springframework.shell.style.ThemeResolver;
|
||||
import org.springframework.shell.style.ThemeSettings;
|
||||
|
||||
import static org.jline.keymap.KeyMap.del;
|
||||
|
||||
public abstract class AbstractShellTests {
|
||||
|
||||
private ExecutorService executorService;
|
||||
private PipedInputStream pipedInputStream;
|
||||
private PipedOutputStream pipedOutputStream;
|
||||
private LinkedBlockingQueue<byte[]> bytesQueue;
|
||||
private ByteArrayOutputStream consoleOut;
|
||||
private Terminal terminal;
|
||||
private TemplateExecutor templateExecutor;
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() throws Exception {
|
||||
executorService = Executors.newFixedThreadPool(1);
|
||||
pipedInputStream = new PipedInputStream();
|
||||
pipedOutputStream = new PipedOutputStream();
|
||||
bytesQueue = new LinkedBlockingQueue<>();
|
||||
consoleOut = new ByteArrayOutputStream();
|
||||
|
||||
ThemeRegistry themeRegistry = new ThemeRegistry();
|
||||
themeRegistry.register(new Theme() {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "default";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThemeSettings getSettings() {
|
||||
return ThemeSettings.themeSettings();
|
||||
}
|
||||
});
|
||||
ThemeResolver themeResolver = new ThemeResolver(themeRegistry, "default");
|
||||
templateExecutor = new TemplateExecutor(themeResolver);
|
||||
|
||||
resourceLoader = new DefaultResourceLoader();
|
||||
|
||||
pipedInputStream.connect(pipedOutputStream);
|
||||
terminal = new DumbTerminal("terminal", "ansi", pipedInputStream, consoleOut, StandardCharsets.UTF_8);
|
||||
|
||||
executorService.execute(() -> {
|
||||
try {
|
||||
while (true) {
|
||||
byte[] take = bytesQueue.take();
|
||||
pipedOutputStream.write(take);
|
||||
pipedOutputStream.flush();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void cleanup() {
|
||||
executorService.shutdown();
|
||||
}
|
||||
|
||||
protected void write(byte[] bytes) {
|
||||
bytesQueue.add(bytes);
|
||||
}
|
||||
|
||||
protected String consoleOut() {
|
||||
return AttributedString.fromAnsi(consoleOut.toString()).toString();
|
||||
}
|
||||
|
||||
protected Terminal getTerminal() {
|
||||
return terminal;
|
||||
}
|
||||
|
||||
protected ResourceLoader getResourceLoader() {
|
||||
return resourceLoader;
|
||||
}
|
||||
|
||||
protected TemplateExecutor getTemplateExecutor() {
|
||||
return templateExecutor;
|
||||
}
|
||||
|
||||
protected class TestBuffer {
|
||||
private final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
|
||||
public TestBuffer() {
|
||||
}
|
||||
|
||||
public TestBuffer(String str) {
|
||||
append(str);
|
||||
}
|
||||
|
||||
public TestBuffer(char[] chars) {
|
||||
append(new String(chars));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
return out.toString(StandardCharsets.UTF_8.name());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public TestBuffer cr() {
|
||||
return append("\r");
|
||||
}
|
||||
|
||||
public TestBuffer backspace() {
|
||||
return append(del());
|
||||
}
|
||||
|
||||
public TestBuffer backspace(int count) {
|
||||
TestBuffer buf = this;
|
||||
for (int i = 0; i < count; i++) {
|
||||
buf = backspace();
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
public TestBuffer down() {
|
||||
return append("\033[B");
|
||||
}
|
||||
|
||||
public TestBuffer ctrl(char let) {
|
||||
return append(KeyMap.ctrl(let));
|
||||
}
|
||||
|
||||
public TestBuffer ctrlE() {
|
||||
return ctrl('E');
|
||||
}
|
||||
|
||||
public TestBuffer ctrlY() {
|
||||
return ctrl('Y');
|
||||
}
|
||||
|
||||
public TestBuffer space() {
|
||||
return append(" ");
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
public TestBuffer append(final String str) {
|
||||
for (byte b : str.getBytes(StandardCharsets.UTF_8)) {
|
||||
append(b);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public TestBuffer append(final int i) {
|
||||
// consoleOut.reset();
|
||||
out.write((byte) i);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.component.flow;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.shell.component.flow.ComponentFlow.ComponentFlowResult;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class ComponentFlowTests extends AbstractShellTests {
|
||||
|
||||
@Test
|
||||
public void testSimpleFlow() throws InterruptedException {
|
||||
Map<String, String> single1SelectItems = new HashMap<>();
|
||||
single1SelectItems.put("key1", "value1");
|
||||
single1SelectItems.put("key2", "value2");
|
||||
List<SelectItem> multi1SelectItems = Arrays.asList(SelectItem.of("key1", "value1"),
|
||||
SelectItem.of("key2", "value2"), SelectItem.of("key3", "value3"));
|
||||
ComponentFlow wizard = ComponentFlow.builder(getTerminal())
|
||||
.resourceLoader(getResourceLoader())
|
||||
.templateExecutor(getTemplateExecutor())
|
||||
.withStringInput("field1")
|
||||
.name("Field1")
|
||||
.defaultValue("defaultField1Value")
|
||||
.and()
|
||||
.withStringInput("field2")
|
||||
.name("Field2")
|
||||
.and()
|
||||
.withPathInput("path1")
|
||||
.name("Path1")
|
||||
.and()
|
||||
.withSingleItemSelector("single1")
|
||||
.name("Single1")
|
||||
.selectItems(single1SelectItems)
|
||||
.and()
|
||||
.withMultiItemSelector("multi1")
|
||||
.name("Multi1")
|
||||
.selectItems(multi1SelectItems)
|
||||
.and()
|
||||
.build();
|
||||
|
||||
ExecutorService service = Executors.newFixedThreadPool(1);
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
AtomicReference<ComponentFlowResult> result = new AtomicReference<>();
|
||||
|
||||
service.execute(() -> {
|
||||
result.set(wizard.run());
|
||||
latch.countDown();
|
||||
});
|
||||
|
||||
// field1
|
||||
TestBuffer testBuffer = new TestBuffer().cr();
|
||||
write(testBuffer.getBytes());
|
||||
// field2
|
||||
testBuffer = new TestBuffer().append("Field2Value").cr();
|
||||
write(testBuffer.getBytes());
|
||||
// path1
|
||||
testBuffer = new TestBuffer().append("fakedir").cr();
|
||||
write(testBuffer.getBytes());
|
||||
// single1
|
||||
testBuffer = new TestBuffer().cr();
|
||||
write(testBuffer.getBytes());
|
||||
// multi1
|
||||
testBuffer = new TestBuffer().ctrlE().space().cr();
|
||||
write(testBuffer.getBytes());
|
||||
|
||||
latch.await(4, TimeUnit.SECONDS);
|
||||
ComponentFlowResult inputWizardResult = result.get();
|
||||
assertThat(inputWizardResult).isNotNull();
|
||||
String field1 = inputWizardResult.getContext().get("field1");
|
||||
String field2 = inputWizardResult.getContext().get("field2");
|
||||
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(path1.toString()).contains("fakedir");
|
||||
assertThat(single1).isEqualTo("value1");
|
||||
assertThat(multi1).containsExactlyInAnyOrder("value2");
|
||||
assertThat(consoleOut()).contains("Field1 defaultField1Value");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipsGivenComponents() throws InterruptedException {
|
||||
ComponentFlow wizard = ComponentFlow.builder(getTerminal())
|
||||
.withStringInput("id1")
|
||||
.name("name")
|
||||
.resultValue("value1")
|
||||
.resultMode(ResultMode.ACCEPT)
|
||||
.and()
|
||||
.withPathInput("id2")
|
||||
.name("name")
|
||||
.resultValue("value2")
|
||||
.resultMode(ResultMode.ACCEPT)
|
||||
.and()
|
||||
.withSingleItemSelector("id3")
|
||||
.resultValue("value3")
|
||||
.resultMode(ResultMode.ACCEPT)
|
||||
.and()
|
||||
.withMultiItemSelector("id4")
|
||||
.resultValues(Arrays.asList("value4"))
|
||||
.resultMode(ResultMode.ACCEPT)
|
||||
.and()
|
||||
.build();
|
||||
|
||||
ExecutorService service = Executors.newFixedThreadPool(1);
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
AtomicReference<ComponentFlowResult> result = new AtomicReference<>();
|
||||
|
||||
service.execute(() -> {
|
||||
result.set(wizard.run());
|
||||
latch.countDown();
|
||||
});
|
||||
|
||||
latch.await(4, TimeUnit.SECONDS);
|
||||
ComponentFlowResult inputWizardResult = result.get();
|
||||
assertThat(inputWizardResult).isNotNull();
|
||||
|
||||
String id1 = inputWizardResult.getContext().get("id1");
|
||||
Path id2 = inputWizardResult.getContext().get("id2");
|
||||
String id3 = inputWizardResult.getContext().get("id3");
|
||||
List<String> id4 = inputWizardResult.getContext().get("id4");
|
||||
|
||||
assertThat(id1).isEqualTo("value1");
|
||||
assertThat(id2.toString()).contains("value2");
|
||||
assertThat(id3).isEqualTo("value3");
|
||||
assertThat(id4).containsExactlyInAnyOrder("value4");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChoosesDynamically() throws InterruptedException {
|
||||
ComponentFlow wizard = ComponentFlow.builder(getTerminal())
|
||||
.resourceLoader(getResourceLoader())
|
||||
.templateExecutor(getTemplateExecutor())
|
||||
.withStringInput("id1")
|
||||
.name("name")
|
||||
.next(ctx -> ctx.get("id1"))
|
||||
.and()
|
||||
.withStringInput("id2")
|
||||
.name("name")
|
||||
.resultValue("value2")
|
||||
.resultMode(ResultMode.ACCEPT)
|
||||
.next(ctx -> null)
|
||||
.and()
|
||||
.withStringInput("id3")
|
||||
.name("name")
|
||||
.resultValue("value3")
|
||||
.resultMode(ResultMode.ACCEPT)
|
||||
.next(ctx -> null)
|
||||
.and()
|
||||
.build();
|
||||
|
||||
ExecutorService service = Executors.newFixedThreadPool(1);
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
AtomicReference<ComponentFlowResult> result = new AtomicReference<>();
|
||||
|
||||
service.execute(() -> {
|
||||
result.set(wizard.run());
|
||||
latch.countDown();
|
||||
});
|
||||
|
||||
// id1
|
||||
TestBuffer testBuffer = new TestBuffer().append("id3").cr();
|
||||
write(testBuffer.getBytes());
|
||||
|
||||
// id3
|
||||
testBuffer = new TestBuffer().cr();
|
||||
write(testBuffer.getBytes());
|
||||
|
||||
latch.await(4, TimeUnit.SECONDS);
|
||||
ComponentFlowResult inputWizardResult = result.get();
|
||||
assertThat(inputWizardResult).isNotNull();
|
||||
String id1 = inputWizardResult.getContext().get("id1");
|
||||
// TODO: should be able to check if variable exists
|
||||
// String id2 = inputWizardResult.getContext().get("id2");
|
||||
String id3 = inputWizardResult.getContext().get("id3");
|
||||
assertThat(id1).isEqualTo("id3");
|
||||
// assertThat(id2).isNull();
|
||||
assertThat(id3).isEqualTo("value3");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.samples.standard;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.shell.component.flow.ComponentFlow;
|
||||
import org.springframework.shell.component.flow.SelectItem;
|
||||
import org.springframework.shell.standard.AbstractShellComponent;
|
||||
import org.springframework.shell.standard.ShellComponent;
|
||||
import org.springframework.shell.standard.ShellMethod;
|
||||
|
||||
@ShellComponent
|
||||
public class ComponentFlowCommands extends AbstractShellComponent {
|
||||
|
||||
@ShellMethod(key = "flow showcase", value = "Showcase", group = "Flow")
|
||||
public void showcase() {
|
||||
Map<String, String> single1SelectItems = new HashMap<>();
|
||||
single1SelectItems.put("key1", "value1");
|
||||
single1SelectItems.put("key2", "value2");
|
||||
List<SelectItem> multi1SelectItems = Arrays.asList(SelectItem.of("key1", "value1"),
|
||||
SelectItem.of("key2", "value2"), SelectItem.of("key3", "value3"));
|
||||
ComponentFlow flow = ComponentFlow.builder(getTerminal())
|
||||
.resourceLoader(getResourceLoader())
|
||||
.templateExecutor(getTemplateExecutor())
|
||||
.withStringInput("field1")
|
||||
.name("Field1")
|
||||
.defaultValue("defaultField1Value")
|
||||
.and()
|
||||
.withStringInput("field2")
|
||||
.name("Field2")
|
||||
.and()
|
||||
.withConfirmationInput("confirmation1")
|
||||
.name("Confirmation1")
|
||||
.and()
|
||||
.withPathInput("path1")
|
||||
.name("Path1")
|
||||
.and()
|
||||
.withSingleItemSelector("single1")
|
||||
.name("Single1")
|
||||
.selectItems(single1SelectItems)
|
||||
.and()
|
||||
.withMultiItemSelector("multi1")
|
||||
.name("Multi1")
|
||||
.selectItems(multi1SelectItems)
|
||||
.and()
|
||||
.build();
|
||||
flow.run();
|
||||
}
|
||||
|
||||
@ShellMethod(key = "flow conditional", value = "Second component based on first", group = "Flow")
|
||||
public void conditional() {
|
||||
Map<String, String> single1SelectItems = new HashMap<>();
|
||||
single1SelectItems.put("Field1", "field1");
|
||||
single1SelectItems.put("Field2", "field2");
|
||||
ComponentFlow flow = ComponentFlow.builder(getTerminal())
|
||||
.resourceLoader(getResourceLoader())
|
||||
.templateExecutor(getTemplateExecutor())
|
||||
.withSingleItemSelector("single1")
|
||||
.name("Single1")
|
||||
.selectItems(single1SelectItems)
|
||||
.next(ctx -> ctx.getResultItem().get().getItem())
|
||||
.and()
|
||||
.withStringInput("field1")
|
||||
.name("Field1")
|
||||
.defaultValue("defaultField1Value")
|
||||
.next(ctx -> null)
|
||||
.and()
|
||||
.withStringInput("field2")
|
||||
.name("Field2")
|
||||
.defaultValue("defaultField2Value")
|
||||
.next(ctx -> null)
|
||||
.and()
|
||||
.build();
|
||||
flow.run();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user