Initial commit.

This commit is contained in:
Florian THIERRY
2021-07-30 18:36:02 +02:00
commit 6d6c56518c
9 changed files with 448 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
package org.takiguchi.workflowvalidator.workflowvalidator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class WorkflowValidatorApplication {
public static void main(String[] args) {
SpringApplication.run(WorkflowValidatorApplication.class, args);
}
}

View File

@@ -0,0 +1,122 @@
package org.takiguchi.workflowvalidator.workflowvalidator.model;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import static org.takiguchi.workflowvalidator.workflowvalidator.model.StepType.FINAL;
import static org.takiguchi.workflowvalidator.workflowvalidator.model.StepType.INITIAL;
public class Step {
private String name;
private StepType type;
private List<Step> previousSteps;
private List<Step> nextSteps;
public Step(String name, StepType type, List<Step> previousSteps, List<Step> nextSteps) {
this.name = name;
this.type = type;
this.previousSteps = previousSteps;
this.nextSteps = nextSteps;
}
public static Builder oneStep() {
return new Builder();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public StepType getType() {
return type;
}
public void setType(StepType type) {
this.type = type;
}
public List<Step> getPreviousSteps() {
return previousSteps;
}
public void setPreviousSteps(List<Step> previousSteps) {
this.previousSteps = previousSteps;
}
public List<Step> getNextSteps() {
return nextSteps;
}
public void setNextSteps(List<Step> nextSteps) {
this.nextSteps = nextSteps;
}
public boolean hasAtLeastOneNextStep() {
return !Optional.ofNullable(nextSteps)
.map(List::isEmpty)
.orElse(false);
}
public boolean hasAtLeastOnePreviousStep() {
return !Optional.ofNullable(previousSteps)
.map(List::isEmpty)
.orElse(false);
}
public boolean isInitialStep() {
return type == INITIAL;
}
public boolean isFinalStep() {
return type == FINAL;
}
public void addPreviousStep(Step step) {
previousSteps.add(step);
}
public void addNextStep(Step step) {
nextSteps.add(step);
}
@Override
public String toString() {
return "Step{" + name + '}';
}
public static class Builder {
private String name;
private StepType type;
private List<Step> previousSteps = new ArrayList<>();
private List<Step> nextSteps = new ArrayList<>();
public Builder name(String name) {
this.name = name;
return this;
}
public Builder type(StepType type) {
this.type = type;
return this;
}
public Builder previousSteps(List<Step> previousSteps) {
this.previousSteps = previousSteps;
return this;
}
public Builder nextSteps(List<Step> nextSteps) {
this.nextSteps = nextSteps;
return this;
}
public Step build() {
return new Step(name, type, previousSteps, nextSteps);
}
}
}

View File

@@ -0,0 +1,5 @@
package org.takiguchi.workflowvalidator.workflowvalidator.model;
public enum StepType {
INITIAL, MIDDLE, FINAL
}

View File

@@ -0,0 +1,15 @@
package org.takiguchi.workflowvalidator.workflowvalidator.model;
import java.util.List;
public class Workflow {
private final List<Step> steps;
public Workflow(List<Step> steps) {
this.steps = steps;
}
public List<Step> getSteps() {
return steps;
}
}

View File

@@ -0,0 +1,71 @@
package org.takiguchi.workflowvalidator.workflowvalidator.service;
import org.springframework.util.CollectionUtils;
import org.takiguchi.workflowvalidator.workflowvalidator.model.Step;
import org.takiguchi.workflowvalidator.workflowvalidator.model.Workflow;
public class WorkflowValidator {
public void validate(Workflow workflow) {
if (CollectionUtils.isEmpty(workflow.getSteps())) {
throw new IllegalStateException("Workflow should have at least one step.");
}
workflow.getSteps().forEach(this::validateStep);
}
private void validateStep(Step step) {
if (step.hasAtLeastOneNextStep()) {
if (step.isFinalStep()) {
throw new IllegalStateException(String.format("The step %s is final, but it has at least one next step, but it shouldn't.", step));
}
// Non final step, that has at least one next step.
validatePreviousStepsOf(step);
} else {
if (!step.isFinalStep()) {
throw new IllegalStateException(String.format("The step %s should have at least one next step.", step));
}
// Final Step, that hasn't any next step.
validatePreviousStepsOf(step);
}
}
private void validatePreviousStepsOf(Step step) {
if (step.hasAtLeastOnePreviousStep()) {
if (step.isInitialStep()) {
throw new IllegalStateException(String.format("The step %s is initial, but is has at least one previous step, but it shouldn't.", step));
}
// Non initial step, that has at least one previous step.
if (canPreviousStepsReachAnInitialStep(step)) {
// Ok
} else {
throw new IllegalStateException(String.format("The step %s hasn't any previous step that can reach an initial step.", step));
}
} else {
if (!step.isInitialStep()) {
throw new IllegalStateException(String.format("The step %s han't any previous step while it is not an initial step.", step));
}
// Initial step, that hasn't any previous step.
}
}
private boolean canPreviousStepsReachAnInitialStep(Step step) {
return step.getPreviousSteps().stream()
.anyMatch(this::canStepReachAnInitialStep);
}
private boolean canStepReachAnInitialStep(Step step) {
boolean result;
if (step.hasAtLeastOnePreviousStep()) {
result = canPreviousStepsReachAnInitialStep(step);
} else {
result = step.isInitialStep();
}
return result;
}
}

View File

@@ -0,0 +1 @@