Why the planner placed a task where it did. It's read from Mutation.reason, which the planner fills only when the call ran with reasons = true in SolveOptions or PreviewOptions. The planner creates the values; caller code only reads them.
The five nested classes are the variants. Match a value with instanceof patterns, or with a switch over type patterns that ends in a default case. Variants compare by field values through equals and hashCode. copy() returns a shallow copy of the same variant.
importjava.util.List;importorg.dhtmlx.plen.*;publicclassReasonExample{publicstaticvoidmain(String[]args)throwsException{Moment monday =Moment.fromYmdHm(2026,1,5,8,0);ProjectData data =newProjectData().withTasks(List.of(newTaskData(Id.str("design")).withStartDate(monday).withDuration(2.0),newTaskData(Id.str("build")).withStartDate(monday).withDuration(3.0))).withLinks(List.of(newLinkData().withId(Id.str("l1")).withSource(Id.str("design")).withTarget(Id.str("build"))));ScheduleResult result =Engine.schedule(data,newSolveOptions().withReasons(true));for(Mutation update : result.updates){String why;if(update.reason instanceofReason.Asap r){
why ="pushed by "+ r.sourceId +" through link "+ r.linkId;}elseif(update.reason instanceofReason.Alap r){
why ="pulled by "+ r.targetId +" through link "+ r.linkId;}elseif(update.reason instanceofReason.SummaryTranslation r){
why ="moved with summary "+ r.summaryId;}elseif(update.reason instanceofReason.Constraint){
why ="placed by its constraint";}elseif(update.reason instanceofReason.Preferred){
why ="kept at its own date";}else{
why ="no reason recorded";}System.out.println(update.id +": "+ why);// build: pushed by design through link l1
}}}