LinkAdd#

A new dependency between two tasks, added to a live project through SchedulingChanges.links.add. It has the same fields as LinkData, but id is optional. Create one with new LinkAdd() and the fluent withX setters.

Declaration#

package org.dhtmlx.plen;

public class LinkAdd extends ValueRecord implements DeepCloneable<LinkAdd>

Constructors#

LinkAdd()#

public LinkAdd()

Creates an add with id, source, target and lagUnit set to null, linkType set to LinkType.FS and lag set to 0.0. source and target must be set before the add goes to apply.

Fields#

Field Type Default Description
id Id null Id of the new link. null makes apply generate one.
source Id null Id of the predecessor task. Required.
target Id null Id of the successor task. Required.
linkType LinkType LinkType.FS Which end of source drives which end of target.
lag double 0.0 Working time between the two ends, in lagUnit. Must be finite.
lagUnit TimeUnit null Unit of lag. null uses SchedulingConfig.durationUnit.

source, target, linkType, lag and lagUnit mean the same as on LinkData. That includes what happens when an endpoint names no task.

id#

public Id id = null;

A non-null id stores the link under that id. It must not repeat the id of a link already in the project.

With null, Project#apply picks the first free id of the form $generated_link_1, $generated_link_2, and so on. The new id is listed in ApplyReport.effects.generatedLinks. Later updates and removals of the link need that id.

Methods#

withId, withSource, withTarget, withLinkType, withLag, withLagUnit#

public LinkAdd withId(Id value)
public LinkAdd withSource(Id value)
public LinkAdd withTarget(Id value)
public LinkAdd withLinkType(LinkType value)
public LinkAdd withLag(double value)
public LinkAdd withLagUnit(TimeUnit value)

Each setter assigns its field and returns this instance.

Returns: this LinkAdd.

deepClone#

public LinkAdd deepClone()

Returns a new LinkAdd with the same field values. Id, LinkType and TimeUnit are immutable, so the copy shares nothing that can change.

Returns: a new LinkAdd.

copy#

public LinkAdd copy()

Returns a shallow copy with the same field values.

Returns: a new LinkAdd.

equals, hashCode, toString#

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

Inherited from ValueRecord. Two LinkAdd instances are equal when all six fields are equal. toString() lists the fields, for logging.

Errors#

Each LinkAdd is one operation, with the path links.add[i]. apply doesn't throw for a rejected operation. It lists the operation as a RejectedOperation in ApplyReport.rejected and moves on to the next one. An add is rejected when:

Message When
duplicate link id id is non-null and the project already has a link with that id
lag must be finite lag is NaN or infinite

A null source or target makes apply throw NullPointerException.

An endpoint that names no task, or names an unscheduled task, doesn't get the operation rejected. See ChangeEffects.droppedLinks.

Link removals run before link adds. So a LinkAdd can reuse the id of a link removed in the same change set. See change sets.

Example#

Link design to build in a solved project without giving an id, then read back the generated one:

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

public class LinkAddExample {
  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)));

    Project project = new Project();
    SolveOptions options = new SolveOptions();
    project.solve(data, options);

    SchedulingChanges changes = new SchedulingChanges();
    changes.links.add.add(new LinkAdd()
        .withSource(Id.str("design"))
        .withTarget(Id.str("build")));

    ApplyReport report = project.apply(changes);
    System.out.println("new link ids: " + report.effects.generatedLinks);

    ScheduleResult result = project.solve(null, options);
    for (Mutation update : result.updates) {
      System.out.println(update.id + ": " + update.startDate + " - " + update.endDate);
    }
  }
}

Output:

new link ids: [$generated_link_1]
build: 2026-04-08 08:00 - 2026-04-13 08:00

design takes Monday and Tuesday, so build now starts on Wednesday morning.