Class Transformation<T extends JobParameters>

java.lang.Object
io.github.douira.glsl_transformer.transform.LifecycleUserImpl<T>
io.github.douira.glsl_transformer.transform.Transformation<T>
All Implemented Interfaces:
LifecycleUser<T>
Direct Known Subclasses:
WrapIdentifier, WrapIdentifierDynamic

public class Transformation<T extends JobParameters> extends LifecycleUserImpl<T>
The transformation holds information about dependencies between transformation phases and nested transformations. It also has a root node and an end node. The root node dependents on all nodes that have no dependents while the end node is depended on by all nodes that have no dependencies. Any directed acyclic graph of dependencies may be created between the nodes within a transformation. A dependency relationship between two nodes consists of a dependent and a dependency node. The dependency node must be executed at some point before the dependent node. As a transformation is a lifecycle user, its internal state can be reset before each transformation job. A stateless (no inter-phase state) transformation can be created by simply making an instance of this class and adding transformations to it. If state between phases is needed, make a subclass and add any state as instance fields. Then phases are created and added within the subclass' constructor. There cannot be any state stored as local variables either in the scope that created the Transformation instance or in a subclass' constructor as it will not be reset if a transformation is run multiple times. In the same vein, state should only be initialized in the LifecycleUser.init() and LifecycleUser.resetState() methods. An intersting effect of the automatic root and end node linking is that it's impossible to create cycles that don't have dangling bits since a bare cycle will be completely disconnected from the rest of the graph. TODO: unclear if sharing phases between transformation managers is problematic since then the compiled paths/patterns in phases have a different parser than the one being used for the transformation. Probably it doesn't matter, and the parser is just used to figure out how the rules of the tree are.
  • Constructor Details

    • Transformation

      public Transformation(LifecycleUser<T> content)
      Creates a stateless transformation and adds a single lifecycle user to it.
      Parameters:
      content - The only lifecycle user to add to this transformation. Typically a transformation phase.
    • Transformation

      public Transformation()
      Creates a stateless transformation with no content, which can be added later.
  • Method Details

    • setupGraph

      protected void setupGraph()
      If conditional dependencies are required for this transformation, all dependencies should be created within this method. Fixed job parameters may be accessed through LifecycleUser.getJobParameters(). Only either dependencies added in this method or statically set dependencies may be used at once. If dependencies are added statically, this method is never run and no conditional dependencies can be created.
    • addDependency

      public void addDependency(LifecycleUser<T> dependent, LifecycleUser<T> dependency)
      Creates a dependency relationship between two nodes. This means the dependency will be run before the dependent. Both of them are added to this transformation if not already present.
      Parameters:
      dependent - The node depending on the dependency to have been run first
      dependency - The node that needs to be run before the dependent
    • addDependent

      public void addDependent(LifecycleUser<T> dependency, LifecycleUser<T> dependent)
      Creates a dependency relationship between two nodes. The meaning of dependent and dependency are the same as in addDependency(LifecycleUser, LifecycleUser) but the positions are switched. This is useful for constructing the dual algorithm in the dependent/dependency structure. Usually the one is just the dependency graph of the other but upside down.
      Parameters:
      dependency - The node being depended on that is executed first
      dependent - The node depending on the dependency that is executed second
      See Also:
    • chainDependency

      public LifecycleUser<T> chainDependency(LifecycleUser<T> dependency)
      Adds a dependency to the last added dependency. If this is the first dependency added to this transformation, this adds it as a dependency of the root node.
      Parameters:
      dependency - The node to add as a further dependency
      Returns:
      The added node
    • chainDependent

      public LifecycleUser<T> chainDependent(LifecycleUser<T> dependent)
      Adds a dependent to the last added dependent. If this is the first dependent added to this transformation, this adds it as a dependent of the end node.
      Parameters:
      dependent - The node to add as a further dependent
      Returns:
      The added node
    • addRootDependency

      public LifecycleUser<T> addRootDependency(LifecycleUser<T> dependency)
      Adds a dependency to the root node. All dependencies added by this method can be run concurrently.
      Parameters:
      dependency - The node to add as a root dependency
      Returns:
      The added node
    • addEndDependent

      public LifecycleUser<T> addEndDependent(LifecycleUser<T> dependent)
      Adds a dependent to the end node. All dependents added by this method can be run concurrently.
      Parameters:
      dependent - The node to add as a end dependent
      Returns:
      The added node
    • appendDependent

      public LifecycleUser<T> appendDependent(LifecycleUser<T> newSoleEndDependent)
      Adds a dependency between the end node and all of its dependents. This replaces the end node with a new end node. This method is called appendDependent because it adds a new node that is the only dependent of the root node after this operation. Furthermore, chaining after this method will see the node with the new content as the dependent and the new end node as the dependency.
      Parameters:
      newSoleEndDependent - The node to place after all present dependencies
      Returns:
      The added node
    • prependDependency

      public LifecycleUser<T> prependDependency(LifecycleUser<T> newSoleRootDependency)
      Adds a dependency between the root node and all of its dependencies. This replaces the root node with a new root node. See appendDependent(LifecycleUser) for why this method is called this way. The argument is the same.
      Parameters:
      newSoleRootDependency - The node to place before all present dependencies
      Returns:
      The added node
    • chainConcurrentDependency

      public LifecycleUser<T> chainConcurrentDependency(LifecycleUser<T> dependency)
      Adds a dependency to the last added dependent. The newly added dependency and the last added dependency can be executed concurrently.
      Parameters:
      dependency - The node to add as a dependency of the last added dependent
      Returns:
      The added node
    • chainConcurrentDependent

      public LifecycleUser<T> chainConcurrentDependent(LifecycleUser<T> dependent)
      Adds a dependent to the last added dependency. The newly added dependent and the last added dependent can be executed concurrently.
      Parameters:
      dependent - The node to add as a dependent of the last added dependency
      Returns:
      The added node
    • chainConcurrentSibling

      public LifecycleUser<T> chainConcurrentSibling(LifecycleUser<T> sibling)
      Adds the same node as a dependent to the last added dependency and as a dependency to the last added dependent. The newly added node must be executed before the last added dependency and after the last added dependent. This is similar to inserting it directly between the two but is less invasive.
      Parameters:
      sibling - The node to add between the last added dependency and dependent without breaking the existing dependency link between them
      Returns:
      The added node