ArchitectureEditorProjection.java
package com.taxonomy.editor;
import com.taxonomy.diagram.DiagramEdge;
import com.taxonomy.diagram.DiagramLayout;
import com.taxonomy.diagram.DiagramModel;
import com.taxonomy.diagram.DiagramNode;
import com.taxonomy.diagram.DiagramScene;
import com.taxonomy.dsl.command.ArchitectureDslCommands;
import com.taxonomy.dsl.model.CanonicalArchitectureModel;
import com.taxonomy.dsl.model.TaxonomyRootTypes;
import com.taxonomy.dsl.validation.DslValidator;
import com.taxonomy.export.LayeredDiagramLayoutService;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
/** Graph, tree, forms and source are projections of the same canonical workspace revision or Git checkpoint. */
@Component
public class ArchitectureEditorProjection {
private final LayeredDiagramLayoutService layout;
public ArchitectureEditorProjection(LayeredDiagramLayoutService layout) { this.layout = layout; }
public record Schema(List<String> elementTypes, List<String> elementProperties,
List<String> relationTypes, List<String> relationStatuses,
Map<String, Map<String, java.util.Set<String>>> relationRules,
Map<String, String> typeRoots, Map<String, PropertyRule> properties, String layoutMode) {}
public record PropertyRule(boolean required, boolean readOnly, boolean derived,
List<String> values, String labelKey) {}
public record SearchEntry(String id, String text) {}
public record View(ArchitectureEditorService.Document document, CanonicalArchitectureModel model,
DiagramScene scene, Schema schema, boolean mayEdit, List<SearchEntry> searchIndex) {}
public View project(ArchitectureEditorService.Document document, boolean mayEdit) {
CanonicalArchitectureModel model = new ArchitectureDslCommands().model(document.dsl());
Map<String, String> parents = new java.util.LinkedHashMap<>();
model.getRelations().stream().filter(relation -> "CONTAINS".equals(relation.getRelationType()))
.forEach(relation -> parents.putIfAbsent(relation.getTargetId(), relation.getSourceId()));
List<String> elementTypes = types();
Map<String, Integer> typeRanks = new java.util.HashMap<>();
for (int rank = 0; rank < elementTypes.size(); rank++) typeRanks.put(elementTypes.get(rank), rank);
List<DiagramNode> nodes = model.getElements().stream().map(element -> new DiagramNode(
element.getId(), element.getTitle(), element.getType(), 0, false,
typeRanks.getOrDefault(element.getType(), 0), 0, false,
parents.get(element.getId()), false)).toList();
List<DiagramEdge> edges = model.getRelations().stream().map(relation -> new DiagramEdge(
relation.getSourceId() + " " + relation.getRelationType() + " " + relation.getTargetId(),
relation.getSourceId(), relation.getTargetId(), relation.getRelationType(), 0)).toList();
DiagramScene scene = layout.layout(new DiagramModel("Architecture ยท " + document.context().branch(), nodes, edges, new DiagramLayout("LR", true)));
Schema schema = new Schema(types(), ArchitectureDslCommands.ELEMENT_PROPERTIES.stream().sorted().toList(),
DslValidator.relationTypes().stream().sorted().toList(), DslValidator.relationStatuses().stream().sorted().toList(),
DslValidator.relationTypeRules(), TaxonomyRootTypes.TYPE_TO_ROOT, properties(), "DERIVED_SERVER_LAYOUT");
return new View(document, model, scene, schema, mayEdit && "PRIVATE_WORKSPACE".equals(document.context().writeMode())
&& "READY".equals(document.projectionState()),
model.getElements().stream().map(element -> new SearchEntry(element.getId(),
(element.getId() + " " + element.getTitle() + " " + element.getType()).toLowerCase(java.util.Locale.ROOT))).toList());
}
private static List<String> types() { return TaxonomyRootTypes.TYPE_TO_ROOT.keySet().stream().sorted().toList(); }
private static Map<String, PropertyRule> properties() {
Map<String, PropertyRule> properties = new java.util.TreeMap<>();
Map<String, String> labels = Map.of("title", "name", "description", "description", "x-editor-status", "elementStatus",
"x-owner", "owner", "x-responsible-organization", "organization", "x-product-reference", "product", "x-system-reference", "system");
ArchitectureDslCommands.ELEMENT_PROPERTIES.forEach(key -> properties.put(key,
new PropertyRule("title".equals(key), false, false,
"x-editor-status".equals(key) ? List.of("draft", "active", "retired") : List.of(), "editor." + labels.get(key))));
properties.put("id", new PropertyRule(true, true, true, List.of(), "editor.identity"));
properties.put("taxonomy", new PropertyRule(false, true, false, List.of(), "editor.taxonomy"));
properties.put("type", new PropertyRule(true, false, false, types(), "editor.type"));
return java.util.Collections.unmodifiableMap(properties);
}
}