Mutation#

The new values of one task after a scheduling call. Each entry in ScheduleResult.updates is a Mutation. A date or leveling field holds a value only when the result reports that field; the others are null.

Which tasks get an entry is described on ScheduleResult. An entry belongs to a result that's read-only by contract, so call deepClone() before changing one.

Declaration#

package org.dhtmlx.plen;

public class Mutation extends ValueRecord implements DeepCloneable<Mutation>

Fields#

The date and leveling fields have the same names and units as on TaskData. Each field has a fluent setter of the same name, withX(value), that returns this entry.

Field Type Set when Description
id Id always Id of the task.
kind MutationKind always Whether the task is a leaf task, a summary or a milestone.
startDate Moment the start changed, or the task is reported in full New start.
endDate Moment the end changed, or the task is reported in full New end.
duration Double the duration changed, or the task is reported in full New working time in the task's durationUnit.
levelingDelay Double the leveling delay changed New leveling delay, as elapsed time in the task's durationUnit.
pauses Vec<Pause> the pause list changed The complete new pause list. An empty list when every pause was removed. A task that had no pauses and still has none never gets this field.
reason Reason the call ran with reasons = true What placed the task.

A task is reported in full when includeUnchanged is set on SolveOptions, or when a Project#apply touched it since the last solve. Such an entry has startDate, endDate and duration all set, even if none of them changed.

Methods#

deepClone#

public Mutation deepClone()

Returns a copy that has its own pauses list with cloned Pause entries. The copy shares the reason object with this entry.

Returns: a new Mutation with the same values.

copy#

public Mutation copy()

Returns a shallow copy. The copy shares the pauses list and the reason object with this entry.

Returns: a new Mutation with the same field values.

equals / hashCode / toString#

Inherited from ValueRecord. equals and hashCode compare all public fields by value, with pauses compared element by element. toString returns a field dump, e.g. Mutation{id=design, kind=TASK, …}.

Writing back#

The field names match TaskUpdate: each non-null value goes into the TaskUpdate field of the same name, wrapped in Patch.of(...). kind and reason have no counterpart there.

TaskUpdate update = new TaskUpdate(mutation.id);
if (mutation.startDate != null) update.withStartDate(Patch.of(mutation.startDate));
if (mutation.endDate != null) update.withEndDate(Patch.of(mutation.endDate));
if (mutation.duration != null) update.withDuration(Patch.of(mutation.duration));
if (mutation.levelingDelay != null) update.withLevelingDelay(Patch.of(mutation.levelingDelay));
if (mutation.pauses != null) update.withPauses(Patch.of(mutation.pauses));

MutationKind#

The kind of task an entry describes. It matches the task's TaskType in the input data.

package org.dhtmlx.plen;

public enum MutationKind { TASK, SUMMARY, MILESTONE }
Value Description
TASK A leaf task (TaskType.TASK). The field default.
SUMMARY A summary task (TaskType.SUMMARY).
MILESTONE A milestone (TaskType.MILESTONE).

Example#

import java.util.List;
import org.dhtmlx.plen.*;

public class MutationExample {
  public static void main(String[] args) throws Exception {
    Moment monday = Moment.fromYmdHm(2026, 4, 6, 8, 0);

    ProjectData data = new ProjectData()
        .withTasks(List.of(
            new TaskData(Id.str("design")).withStartDate(monday).withDuration(2.0),
            new TaskData(Id.str("build")).withStartDate(monday).withDuration(3.0)))
        .withLinks(List.of(
            new LinkData().withId(Id.str("l1"))
                .withSource(Id.str("design"))
                .withTarget(Id.str("build"))));

    ScheduleResult result = Engine.schedule(data, new SolveOptions().withReasons(true));

    for (Mutation update : result.updates) {
      if (update.startDate != null) {
        System.out.println(update.id + " (" + update.kind + ") starts " + update.startDate
            + ": " + update.reason);
      }
    }
  }
}