LinkData#

A dependency between two tasks. It says which end of the source task drives which end of the target task, and how much working time lies between them. Links go in ProjectData.links. Create one with new LinkData() and the fluent withX setters.

Declaration#

public class LinkData extends ValueRecord implements DeepCloneable<LinkData>

Constructors#

LinkData()#

public LinkData()

Creates a link with id, source and target set to null. Set all three before the link goes into a snapshot.

Fields#

Field Type Default Description
id Id null Link id, unique among the project's links. Required.
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. JSON field type.
lag double 0.0 Working time between the two ends, in lagUnit. Negative values are allowed.
lagUnit TimeUnit null Unit of lag. null uses config.durationUnit.

id#

The key results use to refer to this link: in Note.DroppedLink, in a cycle's links and in the critical path of Analytics. Build it with Id.str("a") or Id.num(1).

source, target#

Ids of the two tasks the link connects. An id that names no task, or names an unscheduled task, doesn't fail validation. The link is dropped and reported as a Note.DroppedLink with the reason. See Links that get dropped for every case.

linkType#

FS (finish-to-start), SS, FF or SF. See LinkType below.

lag#

Working time added between the source end and the target end, in lagUnit. A negative value lets the target overlap the source. Must be a finite number. Lag is an absolute amount, not a percentage of the predecessor's duration.

lagUnit#

Unit for this link's lag. null uses SchedulingConfig.durationUnit.

Note

Without a lagUnit, lag is measured in config.durationUnit.

Methods#

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

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

Each setter assigns its field and returns this instance.

Returns - this LinkData.

deepClone#

public LinkData deepClone()

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

Returns - a new LinkData.

copy#

public LinkData copy()

Returns a shallow copy with the same field values.

Returns - a new LinkData.

equals, hashCode, toString#

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

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

LinkType#

The dependency type of a link: which end of the source task drives which end of the target task.

public enum LinkType { FS, SS, FF, SF }
Value Description
FS Finish-to-start: the target starts after the source finishes. The default.
SS Start-to-start: the target starts after the source starts.
FF Finish-to-finish: the target finishes after the source finishes.
SF Start-to-finish: the target finishes after the source starts.

lag is added for every type. LinkAdd and LinkUpdate use the same enum. In JSON, Decode reads the values from the type field.

Errors#

The snapshot is validated when it's passed to Engine.schedule, Project#solve(data, …) or Project#analyze(data, …). With strict validation, a link makes the call throw InputException when:

  • its id repeats the id of an earlier link: path links[i].id
  • its lag is NaN or infinite: path links[i].lag

A null id, source or target throws NullPointerException.

Validation.REPAIR handles a repeated id without throwing. The later link replaces the earlier one at the earlier position, and a Note.Repaired records it.

Links added or edited after the first solve go through LinkAdd and LinkUpdate.

Example#

Task build starts when design finishes. Task test starts four working hours after build starts, while the project counts durations in days:

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

public class LinkDataExample {
  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),
            new TaskData(Id.str("test")).withStartDate(monday).withDuration(1.0)))
        .withLinks(List.of(
            new LinkData()
                .withId(Id.str("a"))
                .withSource(Id.str("design"))
                .withTarget(Id.str("build")),
            new LinkData()
                .withId(Id.str("b"))
                .withSource(Id.str("build"))
                .withTarget(Id.str("test"))
                .withLinkType(LinkType.SS)
                .withLag(4.0)
                .withLagUnit(TimeUnit.HOUR)));

    ScheduleResult result = Engine.schedule(data, new SolveOptions());
    for (Mutation update : result.updates) {
      System.out.println(update.id + ": " + update.startDate + " - " + update.endDate);
    }
  }
}

Output:

build: 2026-04-08 08:00 - 2026-04-13 08:00
test: 2026-04-08 12:00 - 2026-04-09 12:00

design takes Monday and Tuesday, so build starts on Wednesday morning. test starts four working hours later, at 12:00 on Wednesday.