All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.archifacts.integration.c4.model.C4ModelBuilder Maven / Gradle / Ivy

The newest version!
package org.archifacts.integration.c4.model;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;

import org.archifacts.core.model.Archifact;
import org.archifacts.core.model.Artifact;
import org.archifacts.core.model.ArtifactContainer;
import org.archifacts.core.model.ArtifactRelationship;
import org.archifacts.core.model.BuildingBlock;
import org.archifacts.core.model.ExternalArtifact;
import org.archifacts.core.model.MiscArtifact;

import com.structurizr.Workspace;
import com.structurizr.model.Component;
import com.structurizr.model.Container;
import com.structurizr.model.CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy;
import com.structurizr.model.Model;
import com.structurizr.model.ModelItem;
import com.structurizr.model.Relationship;
import com.structurizr.model.SoftwareSystem;

public class C4ModelBuilder {

	private final List containers = new ArrayList<>();
	private final List artifacts = new ArrayList<>();
	private final List relationships = new ArrayList<>();

	private final Lookup lookup = new Lookup();
	private final C4ModelComputer containerComputer = new C4ModelComputer<>(lookup, this::defaultTransformation);
	private final C4ModelComputer artifactComputer = new C4ModelComputer<>(lookup, this::defaultTransformation);
	private final C4ModelComputer relationshipComputer = new C4ModelComputer<>(lookup, this::defaultTransformation);

	private final Workspace workspace;
	private final SoftwareSystem softwareSystem;

	private final Set defaultTransformation(final Artifact artifact, final C4ModelLookup lookup) {
		return artifact.getContainer().map(
				container -> Collections.singleton(lookup.container(container).addComponent(artifact.getName(), artifact.getJavaClass().getName(), null,
						getTypeName(artifact))))
				.orElseThrow(() -> new IllegalStateException("No container present for " + artifact));
	}

	private final Set defaultTransformation(final ArtifactRelationship relationship, final C4ModelLookup lookup) {
		return Collections.singleton(lookup.component(relationship.getSource())
				.uses(lookup.component(relationship.getTarget()), relationship.getRole().getName()));
	}

	private final Set defaultTransformation(final ArtifactContainer container, final C4ModelLookup lookup) {
		return Collections.singleton(lookup.softwareSystem().addContainer(container.getName(),
				null, container.getType().getName()));
	}

	C4ModelBuilder(final Workspace workspace) {
		this.workspace = workspace;
		this.softwareSystem = initSoftwareSystem(workspace);
	}

	private SoftwareSystem initSoftwareSystem(final Workspace workspace) {
		final Model model = workspace.getModel();
		model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy());
		return model.addSoftwareSystem(workspace.getName());
	}

	public void container(final ArtifactContainer artifactContainer) {
		containers.add(artifactContainer);
	}

	public void artifact(final Artifact artifact) {
		artifacts.add(artifact);
	}

	public void relationship(final ArtifactRelationship relationship) {
		relationships.add(relationship);
	}

	public ComputationRuleBuilderPredicateStage containerRule() {
		return new ComputationRuleBuilder<>(containerComputer);
	}

	public ComputationRuleBuilderPredicateStage artifactRule() {
		return new ComputationRuleBuilder<>(artifactComputer);
	}

	public ComputationRuleBuilderPredicateStage relartionshipRule() {
		return new ComputationRuleBuilder<>(relationshipComputer);
	}

	public C4Model build() {
		containers.forEach(containerComputer::compute);
		artifacts.forEach(artifactComputer::compute);
		relationships.forEach(relationshipComputer::compute);

		final Map> archifactMap = new HashMap<>();
		archifactMap.putAll(containerComputer.getMappings());
		archifactMap.putAll(artifactComputer.getMappings());
		archifactMap.putAll(relationshipComputer.getMappings());
		return new C4Model(workspace, softwareSystem, archifactMap);
	}

	public interface ComputationRuleBuilderPredicateStage {
		ComputationRuleBuilderBuildStage predicate(Predicate predicate);
	}

	public interface ComputationRuleBuilderBuildStage {
		C4ModelBuilder computation(ComputationFunction computation);
	}

	public final class ComputationRuleBuilder implements ComputationRuleBuilderPredicateStage, ComputationRuleBuilderBuildStage {
		private final C4ModelComputer c4ModelComputer;
		private Predicate predicate;

		private ComputationRuleBuilder(final C4ModelComputer c4ModelComputer) {
			this.c4ModelComputer = c4ModelComputer;
		}

		@Override
		public ComputationRuleBuilderBuildStage predicate(final Predicate predicate) {
			this.predicate = predicate;
			return this;
		}

		@Override
		public C4ModelBuilder computation(final ComputationFunction computation) {
			c4ModelComputer.addComputationRule(new ModelItemComputationRule<>(predicate, computation));
			return C4ModelBuilder.this;
		}

	}

	private class Lookup implements C4ModelLookup {

		@Override
		public SoftwareSystem softwareSystem() {
			return softwareSystem;
		}

		private  R lookup(final T archifact, final C4ModelComputer computer, final Class returnType) {
			final Set modelItems = computer.compute(archifact);
			if (modelItems.isEmpty()) {
				throw new IllegalStateException("No model item found for " + archifact);
			}
			if (modelItems.size() > 1) {
				throw new IllegalStateException("Too many model items found for " + archifact);
			}
			final ModelItem modelItem = modelItems.iterator().next();
			if (returnType.isInstance(modelItem)) {
				return returnType.cast(modelItem);
			}
			throw new IllegalStateException("Element is not of expected type: " + archifact);
		}

		@Override
		public Component component(final Artifact artifact) {
			return lookup(artifact, artifactComputer, Component.class);
		}

		@Override
		public Container container(final ArtifactContainer artifactContainer) {
			return lookup(artifactContainer, containerComputer, Container.class);
		}

		@Override
		public Relationship relationship(final ArtifactRelationship artifactRelationship) {
			return lookup(artifactRelationship, relationshipComputer, Relationship.class);
		}

	}

	private static String getTypeName(final Artifact artifact) {

		if (artifact instanceof BuildingBlock) {
			return ((BuildingBlock) artifact).getType().getName();
		}
		if (artifact instanceof ExternalArtifact) {
			return "External";
		}
		if (artifact instanceof MiscArtifact) {
			return "Misc";
		}
		throw new IllegalArgumentException("Unexpected type: " + artifact.getClass().getName());
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy