poussecafe.doc.DomainProcessStepsFactory Maven / Gradle / Ivy
The newest version!
package poussecafe.doc;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import poussecafe.discovery.DefaultProcess;
import poussecafe.doc.model.DocumentationItem;
import poussecafe.doc.model.Domain;
import poussecafe.doc.model.DomainProcessGraphNodes;
import poussecafe.doc.model.MessageListener;
import poussecafe.doc.model.MessageListenersPerEvent;
import poussecafe.doc.model.domainprocessdoc.DomainProcessGraphNode;
import poussecafe.doc.model.domainprocessdoc.DomainProcessGraphNodeName;
import poussecafe.doc.model.domainprocessdoc.ToStep;
import poussecafe.doc.model.processstepdoc.NameRequired;
import poussecafe.doc.model.processstepdoc.StepMethodSignature;
import poussecafe.source.analysis.ClassName;
import poussecafe.source.model.Documentation;
import static java.util.stream.Collectors.toList;
public class DomainProcessStepsFactory {
public static DomainProcessGraphNodes buildDomainProcessGraphNodes(DocumentationItem domainProcessDoc, Domain domain) {
DomainProcessGraphNodes.Builder stepsBuilder = new DomainProcessGraphNodes.Builder();
var moduleComponentDoc = domainProcessDoc;
var moduleDocId = moduleComponentDoc.moduleName();
String processName = moduleComponentDoc.name();
List listeners = domain.listeners(moduleDocId, processName);
MessageListenersPerEvent eventToConsumingStepsMap = buildConsumingStepsPerEvent(listeners);
Set otherProcesses = new HashSet<>();
for(MessageListener listener : listeners) {
var currentStepToSteps = new ArrayList();
var toInternals = eventToConsumingStepsMap.locateToInternals(listener);
currentStepToSteps.addAll(toDirectSteps(toInternals));
var toExternals = locateToExternals(listener);
stepsBuilder.merge(toExternalStepsMap(toExternals));
currentStepToSteps.addAll(toExternals);
var toDomainProcesses = locateToDomainProcesses(domainProcessDoc, listener, domain);
otherProcesses.addAll(toDomainProcesses.stream().map(ToStep::name).collect(toList()));
stepsBuilder.merge(toExternalStepsMap(toDomainProcesses));
currentStepToSteps.addAll(toDomainProcesses);
var processStepComponentDoc = listener.documentation();
var currentStep = new DomainProcessGraphNode.Builder()
.componentDoc(processStepComponentDoc)
.tos(currentStepToSteps)
.build();
stepsBuilder.add(currentStep);
DomainProcessGraphNodeName currentStepName = new DomainProcessGraphNodeName(processStepComponentDoc.name());
ToStep toCurrentStep = directStep(currentStepName);
List fromExternals = locateFromExternals(listener);
stepsBuilder.merge(fromExternalStepsMap(fromExternals, toCurrentStep));
List fromDomainProcesses = fromDomainProcesses(domainProcessDoc, listener, domain);
otherProcesses.addAll(fromDomainProcesses);
stepsBuilder.merge(fromExternalStepsMap(fromDomainProcesses, toCurrentStep));
}
Map interprocessSteps = buildInterprocessSteps(moduleDocId, otherProcesses, domain);
stepsBuilder.merge(interprocessSteps);
return stepsBuilder.build();
}
private static List locateFromExternals(MessageListener processStepDoc) {
return processStepDoc.fromExternals().stream().map(DomainProcessGraphNodeName::new).collect(toList());
}
private static Map fromExternalStepsMap(List fromExternals, ToStep toCurrentStep) {
Map fromExternalSteps = new HashMap<>();
for(DomainProcessGraphNodeName fromExternal : fromExternals) {
var fromExternalStep = new DomainProcessGraphNode.Builder()
.componentDoc(new DocumentationItem.Builder()
.id("from" + fromExternal)
.name(fromExternal.stringValue())
.description(Documentation.empty())
.build())
.external(true)
.to(toCurrentStep)
.build();
fromExternalSteps.put(fromExternalStep.stepName(), fromExternalStep);
}
return fromExternalSteps;
}
private static Map toExternalStepsMap(Collection externalStepsNames) {
Map steps = new HashMap<>();
for(ToStep externalToStep : externalStepsNames) {
DomainProcessGraphNodeName externalStepName = externalToStep.name();
steps.computeIfAbsent(externalStepName, key -> new DomainProcessGraphNode.Builder()
.componentDoc(new DocumentationItem.Builder()
.id("to" + externalStepName)
.name(externalStepName.stringValue())
.description(Documentation.empty())
.build())
.external(true)
.build());
}
return steps;
}
private static MessageListenersPerEvent buildConsumingStepsPerEvent(List processStepDocs) {
var builder = new MessageListenersPerEvent.Builder();
for(MessageListener processStepDoc : processStepDocs) {
builder.withMessageListener(processStepDoc);
}
return builder.build();
}
private static List toDirectSteps(Collection tos) {
List toSteps = new ArrayList<>();
for(DomainProcessGraphNodeName to : tos) {
toSteps.add(directStep(to));
}
return toSteps;
}
private static ToStep directStep(DomainProcessGraphNodeName to) {
return new ToStep.Builder()
.name(to)
.directly(true)
.build();
}
private static Set locateToExternals(MessageListener processStepDoc) {
Set toExternals = new HashSet<>();
toExternals.addAll(processStepDoc.toExternals().stream().map(DomainProcessGraphNodeName::new).map(DomainProcessStepsFactory::directStep).collect(toList()));
for(Entry> entry : processStepDoc.toExternalsByEvent().entrySet()) {
boolean required = entry.getKey().required();
toExternals.addAll(entry.getValue().stream().map(name -> toStep(name, required)).collect(toList()));
}
return toExternals;
}
private static ToStep toStep(String name, boolean required) {
return new ToStep.Builder()
.name(new DomainProcessGraphNodeName(name))
.directly(required)
.build();
}
private static List locateToDomainProcesses(
DocumentationItem processDoc,
MessageListener listener,
Domain domain) {
Set producedEvents = listener.producedEvents();
String domainProcessName = processDoc.name();
String moduleDocId = processDoc.moduleName();
Set toDomainProcesses = new HashSet<>();
for(NameRequired producedEvent : producedEvents) {
for(MessageListener nextListener : domain.findConsuming(moduleDocId, producedEvent.name())) {
Set processNames = nextListener.processNames();
for(String processName : processNames) {
if(!processName.equals(domainProcessName)) {
toDomainProcesses.add(new ToStep.Builder()
.name(new DomainProcessGraphNodeName(processName))
.directly(producedEvent.required())
.build());
}
}
}
}
return toDomainProcesses.stream().collect(toList());
}
private static List fromDomainProcesses(
DocumentationItem domainProcessDoc,
MessageListener processStepDoc,
Domain domain) {
Optional stepMethodSignature = processStepDoc.stepMethodSignature();
Optional consumedEvent = Optional.empty();
if(stepMethodSignature.isPresent()) {
consumedEvent = stepMethodSignature.get().consumedEventName();
}
String domainProcessName = domainProcessDoc.name();
Set otherDomainProcesses = new HashSet<>();
if(consumedEvent.isPresent()) {
String moduleDocId = domainProcessDoc.moduleName();
List stepsProducingEvent = domain.findProducing(moduleDocId, consumedEvent.get());
for(MessageListener stepDoc : stepsProducingEvent) {
Set processNames = stepDoc.processNames();
for(String processName : processNames) {
if(!processName.equals(domainProcessName)) {
otherDomainProcesses.add(processName);
}
}
}
}
return otherDomainProcesses.stream()
.map(DomainProcessGraphNodeName::new)
.collect(toList());
}
private static Map buildInterprocessSteps(
String moduleName,
Set otherProcesses,
Domain domain) {
Map interprocessSteps = new HashMap<>();
List otherProcessesList = new ArrayList<>(otherProcesses);
Map> producedEventsPerProcess = new HashMap<>();
Map> consumedEventsPerProcess = new HashMap<>();
for(int i = 0; i < otherProcessesList.size(); ++i) {
var processName1 = otherProcessesList.get(i);
var process1ClassName = processClassName(moduleName, domain, processName1);
var producedEventsOf1 = producedEventsPerProcess.computeIfAbsent(processName1, name -> producedEventsOfProcess(moduleName, name, domain));
var consumedEventsOf1 = consumedEventsPerProcess.computeIfAbsent(processName1, name -> consumedEventsOfProcess(moduleName, name, domain));
for(int j = i + 1; j < otherProcessesList.size(); ++j) {
var processName2 = otherProcessesList.get(j);
var process2ClassName = processClassName(moduleName, domain, processName2);
var producedEventsOf2 = producedEventsPerProcess.computeIfAbsent(processName2, name -> producedEventsOfProcess(moduleName, name, domain));
var consumedEventsOf2 = consumedEventsPerProcess.computeIfAbsent(processName2, name -> consumedEventsOfProcess(moduleName, name, domain));
Set producedBy1AndConsumedBy2 = intersect(producedEventsOf1, consumedEventsOf2);
if(!producedBy1AndConsumedBy2.isEmpty()) {
interprocessSteps.put(processName1, new DomainProcessGraphNode.Builder()
.componentDoc(new DocumentationItem.Builder()
.id(processName1 + "_" + processName2)
.className(process1ClassName)
.name(processName1.stringValue())
.description(Documentation.empty())
.moduleName(moduleName)
.build())
.to(directStep(processName2))
.build());
}
Set producedBy2AndConsumedBy1 = intersect(producedEventsOf2, consumedEventsOf1);
if(!producedBy2AndConsumedBy1.isEmpty()) {
interprocessSteps.put(processName2, new DomainProcessGraphNode.Builder()
.componentDoc(new DocumentationItem.Builder()
.id(processName2 + "_" + processName1)
.className(process2ClassName)
.name(processName2.stringValue())
.description(Documentation.empty())
.moduleName(moduleName)
.build())
.to(directStep(processName1))
.build());
}
}
}
return interprocessSteps;
}
private static Optional processClassName(String moduleName, Domain domain, DomainProcessGraphNodeName processName) {
if(processName.stringValue().equals(DefaultProcess.class.getSimpleName())) {
return Optional.empty();
} else {
return domain.module(moduleName).orElseThrow().processes().stream()
.filter(process -> process.name().equals(processName.stringValue()))
.findFirst()
.map(DocumentationItem::className)
.orElse(Optional.empty());
}
}
private static Set producedEventsOfProcess(
String moduleDocId,
DomainProcessGraphNodeName processName,
Domain domain) {
Set producedEvents = new HashSet<>();
List processStepDocs = domain.listeners(moduleDocId, processName.stringValue());
for(MessageListener stepDoc : processStepDocs) {
producedEvents.addAll(stepDoc.producedEvents().stream().map(NameRequired::name).collect(toList()));
}
return producedEvents;
}
private static Set consumedEventsOfProcess(String moduleDocId, DomainProcessGraphNodeName processName, Domain domain) {
Set consumedEvents = new HashSet<>();
List processStepDocs = domain.listeners(moduleDocId, processName.stringValue());
for(MessageListener stepDoc : processStepDocs) {
Optional signature = stepDoc.stepMethodSignature();
if(signature.isPresent()) {
Optional consumedEvent = signature.get().consumedEventName();
if(consumedEvent.isPresent()) {
consumedEvents.add(consumedEvent.get());
}
}
}
return consumedEvents;
}
private static Set intersect(Set set1, Set set2) {
Set intersection = new HashSet<>(set1);
intersection.retainAll(set2);
return intersection;
}
private DomainProcessStepsFactory() {
}
}