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

com.structurizr.Workspace Maven / Gradle / Ivy

There is a newer version: 2.2.0
Show newest version
package com.structurizr;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.structurizr.documentation.Documentable;
import com.structurizr.documentation.Documentation;
import com.structurizr.model.*;
import com.structurizr.view.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.annotation.Nonnull;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * Represents a Structurizr workspace, which is a wrapper for a
 * software architecture model, views and documentation.
 */
public final class Workspace extends AbstractWorkspace implements Documentable {

    private static final Log log = LogFactory.getLog(Workspace.class);

    private Model model = createModel();
    private ViewSet viewSet;
    private Documentation documentation;

    Workspace() {
    }

    /**
     * Creates a new workspace.
     *
     * @param name          the name of the workspace
     * @param description   a short description
     */
    public Workspace(String name, String description) {
        super(name, description);

        model = createModel();
        viewSet = createViewSet();
        documentation = new Documentation();
    }

    /**
     * Gets the software architecture model.
     *
     * @return  a Model instance
     */
    public Model getModel() {
        return model;
    }

    void setModel(Model model) {
        this.model = model;
    }

    /**
     * Gets the set of views onto a software architecture model.
     *
     * @return  a ViewSet instance
     */
    public ViewSet getViews() {
        return viewSet;
    }

    void setViews(ViewSet viewSet) {
        this.viewSet = viewSet;
    }

    private Model createModel() {
        try {
            Constructor constructor = Model.class.getDeclaredConstructor();
            constructor.setAccessible(true);
            return (Model)constructor.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private ViewSet createViewSet() {
        try {
            Constructor constructor = ViewSet.class.getDeclaredConstructor(Model.class);
            constructor.setAccessible(true);
            return (ViewSet)constructor.newInstance(model);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Called when deserialising JSON, to re-create the object graph
     * based upon element/relationship IDs.
     */
    public void hydrate() {
        if (viewSet == null) {
            viewSet = createViewSet();
        }

        hydrateModel();
        hydrateViewSet();
    }

    private void hydrateModel() {
        try {
            Method hydrateMethod = Model.class.getDeclaredMethod("hydrate");
            hydrateMethod.setAccessible(true);
            hydrateMethod.invoke(model);
        } catch (InvocationTargetException ite) {
            if (ite.getCause() != null && ite.getCause() instanceof WorkspaceValidationException) {
                throw (WorkspaceValidationException)ite.getCause();
            } else {
                throw new RuntimeException(ite.getCause());
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void hydrateViewSet() {
        try {
            Method hydrateMethod = ViewSet.class.getDeclaredMethod("hydrate", Model.class);
            hydrateMethod.setAccessible(true);
            hydrateMethod.invoke(viewSet, model);
        } catch (InvocationTargetException ite) {
            if (ite.getCause() != null && ite.getCause() instanceof WorkspaceValidationException) {
                throw (WorkspaceValidationException)ite.getCause();
            } else {
                throw new RuntimeException(ite.getCause());
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Gets the documentation associated with this workspace.
     *
     * @return  a Documentation object
     */
    public Documentation getDocumentation() {
        return documentation;
    }

    /**
     * Sets the documentation associated with this workspace.
     *
     * @param documentation a Documentation object
     */
    void setDocumentation(@Nonnull Documentation documentation) {
        this.documentation = documentation;
    }

    /**
     * Determines whether this model is empty.
     *
     * @return  true if the model has no elements, views or documentation; false otherwise
     */
    @JsonIgnore
    public boolean isEmpty() {
        return model.isEmpty() && viewSet.isEmpty() && documentation.isEmpty();
    }

    /**
     * Trims the workspace by removing all unused elements.
     */
    public void trim() {
        for (CustomElement element : model.getCustomElements()) {
            remove(element);
        }

        for (Person person : model.getPeople()) {
            remove(person);
        }

        for (SoftwareSystem softwareSystem : model.getSoftwareSystems()) {
            remove(softwareSystem);
        }

        for (DeploymentNode deploymentNode : model.getDeploymentNodes()) {
            remove(deploymentNode);
        }
    }

    void remove(CustomElement element) {
        if (!isElementAssociatedWithAnyViews(element)) {
            try {
                Method method = Model.class.getDeclaredMethod("remove", CustomElement.class);
                method.setAccessible(true);
                method.invoke(model, element);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    void remove(Person person) {
        if (!isElementAssociatedWithAnyViews(person)) {
            try {
                Method method = Model.class.getDeclaredMethod("remove", Person.class);
                method.setAccessible(true);
                method.invoke(model, person);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    void remove(SoftwareSystem softwareSystem) {
        Set softwareSystemInstances = model.getElements().stream().filter(e -> e instanceof SoftwareSystemInstance && ((SoftwareSystemInstance)e).getSoftwareSystem() == softwareSystem).map(e -> (SoftwareSystemInstance)e).collect(Collectors.toSet());
        for (SoftwareSystemInstance softwareSystemInstance : softwareSystemInstances) {
            remove(softwareSystemInstance);
        }

        for (Container container : softwareSystem.getContainers()) {
            remove(container);
        }

        boolean hasContainers = softwareSystem.hasContainers();
        boolean hasSoftwareSystemInstances = model.getElements().stream().anyMatch(e -> e instanceof SoftwareSystemInstance && ((SoftwareSystemInstance)e).getSoftwareSystem() == softwareSystem);
        if (!hasContainers && !hasSoftwareSystemInstances && !isElementAssociatedWithAnyViews(softwareSystem)) {
            try {
                Method method = Model.class.getDeclaredMethod("remove", SoftwareSystem.class);
                method.setAccessible(true);
                method.invoke(model, softwareSystem);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    void remove(Container container) {
        for (Component component : container.getComponents()) {
            remove(component);
        }

        if (!isElementAssociatedWithAnyViews(container)) {
            Set containerInstances = model.getElements().stream().filter(e -> e instanceof ContainerInstance && ((ContainerInstance)e).getContainer() == container).map(e -> (ContainerInstance)e).collect(Collectors.toSet());
            for (ContainerInstance containerInstance : containerInstances) {
                remove(containerInstance);
            }

            boolean hasComponents = container.hasComponents();
            boolean hasContainerInstances = model.getElements().stream().anyMatch(e -> e instanceof ContainerInstance && ((ContainerInstance)e).getContainer() == container);
            if (!hasComponents && !hasContainerInstances && !isElementAssociatedWithAnyViews(container)) {
                try {
                    Method method = Model.class.getDeclaredMethod("remove", Container.class);
                    method.setAccessible(true);
                    method.invoke(model, container);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    void remove(Component component) {
        if (!isElementAssociatedWithAnyViews(component)) {
            try {
                Method method = Model.class.getDeclaredMethod("remove", Component.class);
                method.setAccessible(true);
                method.invoke(model, component);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    void remove(SoftwareSystemInstance softwareSystemInstance) {
        if (!isElementAssociatedWithAnyViews(softwareSystemInstance)) {
            try {
                Method method = Model.class.getDeclaredMethod("remove", SoftwareSystemInstance.class);
                method.setAccessible(true);
                method.invoke(model, softwareSystemInstance);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    void remove(ContainerInstance containerInstance) {
        if (!isElementAssociatedWithAnyViews(containerInstance)) {
            try {
                Method method = Model.class.getDeclaredMethod("remove", ContainerInstance.class);
                method.setAccessible(true);
                method.invoke(model, containerInstance);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    void remove(DeploymentNode deploymentNode) {
        if (deploymentNode.hasChildren()) {
            for (DeploymentNode child : deploymentNode.getChildren()) {
                remove(child);
            }
        }

        if (!deploymentNode.hasChildren() && !deploymentNode.hasSoftwareSystemInstances() && !deploymentNode.hasContainerInstances()) {
            try {
                Method method = Model.class.getDeclaredMethod("remove", DeploymentNode.class);
                method.setAccessible(true);
                method.invoke(model, deploymentNode);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    private boolean isElementAssociatedWithAnyViews(Element element) {
        boolean result = false;

        // is the element used in any views
        for (View view : viewSet.getViews()) {
            if (view instanceof ModelView) {
                ModelView modelView = (ModelView)view;
                result = result | modelView.isElementInView(element);
            }
        }

        // is the element the scope of any views?
        for (SystemContextView view : viewSet.getSystemContextViews()) {
            result = result | view.getSoftwareSystem() == element;
        }

        for (ContainerView view : viewSet.getContainerViews()) {
            result = result | view.getSoftwareSystem() == element;
        }

        for (ComponentView view : viewSet.getComponentViews()) {
            result = result | view.getContainer() == element;
        }

        for (DynamicView view : viewSet.getDynamicViews()) {
            result = result | view.getElement() == element;
        }

        for (DeploymentView view : viewSet.getDeploymentViews()) {
            result = result | view.getSoftwareSystem() == element;
        }

        for (ImageView view : viewSet.getImageViews()) {
            result = result | view.getElement() == element;
        }

        return result;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy