Conflict#

A scheduling conflict found while placing tasks. Instances come from ScheduleResult.conflicts, which lists every conflict of the run. A conflict is data in the result, not an exception. The run still succeeds and returns dates.

Declaration#

package org.dhtmlx.plen;

public abstract class Conflict extends ValueRecord {
  public static class ConstraintViolation extends Conflict { /* ... */ }
  public static class CalendarNonWorking extends Conflict { /* ... */ }
}

The library creates only these two nested subclasses. Read them with instanceof patterns or switch pattern matching. The hierarchy isn't sealed, so a switch over a Conflict needs a default arm.

Code refers to the variant as Conflict.ConstraintViolation. The package also has a top-level enum org.dhtmlx.plen.ConstraintViolation, and with import org.dhtmlx.plen.*; the bare name ConstraintViolation refers to that enum.

Variants#

Variant When it occurs
Conflict.ConstraintViolation A task's constraint placed it at a date its driving dependency link doesn't allow. The constraint wins, and the task keeps the constraint's date. Reported only when SchedulingConfig.applyConstraints is true.
Conflict.CalendarNonWorking A summary moved as one block, and a descendant task landed on non-working time of its calendar. Reported only when SchedulingConfig.strictCalendar is true.

ConstraintViolation#

public ConstraintViolation(
    Id task, ConstraintType constraint, Violation violation, Moment required, Moment actual)
Field Type Description
task Id The task or summary whose constraint won.
constraint ConstraintType The constraint that placed the task: the task's own, or one inherited from a summary.
violation Violation Which bound the link contradicts.
required Moment The date the constraint gave the task. The task has this date in the result.
actual Moment The date the driving link would have given the task.

CalendarNonWorking#

public CalendarNonWorking(Id task, Moment proposed, Moment snapped)
Field Type Description
task Id The descendant task that landed on non-working time.
proposed Moment The task's new start, or its new end when the project is scheduled from the end. The date is on non-working time.
snapped Moment The date the task keeps. Currently it's always equal to proposed: the task isn't moved to working time.

Methods#

copy#

public Conflict copy()

Returns a shallow copy. Each subclass overrides it with its own return type, such as Conflict.ConstraintViolation copy(). All fields are immutable values, so the copy is independent of the original.

Returns: a new instance of the same variant.

equals, hashCode, toString#

public boolean equals(Object other)
public int hashCode()
public String toString()

Inherited from ValueRecord. Two conflicts are equal when they're the same variant and all their fields are equal. toString() prints the variant and its fields, e.g. ConstraintViolation{task=demo, constraint=SNLT, violation=LATEST_START, required=2026-03-03 00:00, actual=2026-03-05 00:00}.

Violation#

Which bound of a constraint the driving link contradicts, in Conflict.ConstraintViolation.violation. EARLIEST_START and LATEST_START compare start dates. EARLIEST_END and LATEST_END compare end dates.

package org.dhtmlx.plen;

public enum Violation {
  EARLIEST_START,
  LATEST_START,
  EARLIEST_END,
  LATEST_END
}
Value Description
EARLIEST_START The link wants an earlier start than the constraint allows.
LATEST_START The link wants a later start than the constraint allows.
EARLIEST_END The link wants an earlier end than the constraint allows.
LATEST_END The link wants a later end than the constraint allows.

Example#

demo must start no later than Tuesday, but its predecessor build ends on Wednesday. The constraint wins, and the result reports the conflict:

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

public class ConflictExample {
  public static void main(String[] args) throws Exception {
    Moment monday = Moment.fromYmdHm(2026, 3, 2, 0, 0);
    Moment tuesday = Moment.fromYmdHm(2026, 3, 3, 0, 0);

    ProjectData data = new ProjectData()
        .withConfig(new SchedulingConfig().withApplyConstraints(true))
        .withTasks(List.of(
            new TaskData(Id.str("build")).withStartDate(monday).withDuration(3.0),
            new TaskData(Id.str("demo"))
                .withStartDate(tuesday)
                .withDuration(1.0)
                .withConstraintType(ConstraintType.SNLT)
                .withConstraintDate(tuesday)))
        .withLinks(List.of(
            new LinkData().withId(Id.num(1)).withSource(Id.str("build")).withTarget(Id.str("demo"))));

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

    for (Conflict conflict : result.conflicts) {
      String text = switch (conflict) {
        case Conflict.ConstraintViolation v -> v.task + ": " + v.constraint + " " + v.violation
            + ", constraint gives " + v.required + ", link wants " + v.actual;
        case Conflict.CalendarNonWorking n -> n.task + ": on non-working time at " + n.proposed;
        default -> throw new IllegalStateException("unknown conflict: " + conflict);
      };
      System.out.println(text);
    }
  }
}

Output:

demo: SNLT LATEST_START, constraint gives 2026-03-03 00:00, link wants 2026-03-05 00:00

The default arm is there only because the compiler requires it. The library returns no other variant.