LinkUpdate#

A partial edit of one existing link. Only the fields set to a patch other than Patch.missing() change; the rest keep their current values. Add it to changes.links.update of a SchedulingChanges and pass the change set to Project#apply, Project#preview or Project#evaluate. Create one with new LinkUpdate(id) and the fluent withX setters.

Declaration#

public class LinkUpdate extends ValueRecord implements DeepCloneable<LinkUpdate>

Constructors#

LinkUpdate(Id)#

public LinkUpdate(Id id)

Creates an update for the link with this id. Every other field is Patch.missing(), so the update changes nothing until a field is set.

Parameters

Name Type Description
id Id Id of the link to edit. Build it with Id.str("a") or Id.num(1).

The class has no parameterless constructor.

Fields#

Every field except id is a Patch<T> and starts as Patch.missing(). Patch.of(value) sets the field. The last column shows what Patch.nullValue() does. The fields mean the same as on LinkData.

Field Type Patch.nullValue()
id Id Not a patch. Selects the link to edit.
source Patch<Id> Rejected: a link always has a source.
target Patch<Id> Rejected: a link always has a target.
linkType Patch<LinkType> Resets to LinkType.FS.
lag Patch<Double> Resets to 0.0.
lagUnit Patch<TimeUnit> Clears the unit, so lag is read in SchedulingConfig.durationUnit.

id#

public Id id

Id of an existing link. The update edits that link and can't change its id.

source, target#

public Patch<Id> source = Patch.missing();
public Patch<Id> target = Patch.missing();

A value moves that end of the link to another task. If the new task doesn't exist or is unscheduled, the update is still applied, but the link is dropped from scheduling and its id is listed in ApplyReport.effects.droppedLinks (see ApplyReport).

Patch.of(null) on either field throws NullPointerException when the change set is applied.

linkType#

public Patch<LinkType> linkType = Patch.missing();

Which end of source drives which end of target.

lag#

public Patch<Double> lag = Patch.missing();

Working time between the two ends, in the link's lag unit. Negative values are allowed. The value must be finite. Patch.of(null) throws NullPointerException when the change set is applied.

lagUnit#

public Patch<TimeUnit> lagUnit = Patch.missing();

Unit of lag for this link only.

Methods#

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

public LinkUpdate withId(Id value)
public LinkUpdate withSource(Patch<Id> value)
public LinkUpdate withTarget(Patch<Id> value)
public LinkUpdate withLinkType(Patch<LinkType> value)
public LinkUpdate withLag(Patch<Double> value)
public LinkUpdate withLagUnit(Patch<TimeUnit> value)

Each setter assigns its field and returns this instance. The patch setters take a Patch, not a plain value: write withLag(Patch.of(2.0)).

Returns - this LinkUpdate.

deepClone#

public LinkUpdate deepClone()

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

Returns - a new LinkUpdate.

copy#

public LinkUpdate copy()

Returns a shallow copy with the same field values.

Returns - a new LinkUpdate.

equals, hashCode, toString#

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

Inherited from ValueRecord. Two LinkUpdate are equal when all six fields are equal; patches compare by kind and value. toString() lists the fields, for logging.

Errors#

An invalid update doesn't throw. Project#apply lists it in ApplyReport.rejected as a RejectedOperation with path "links.update[i]", and the link stays as it was. An update is rejected when:

  • id names no link in the project: unknown link id
  • source or target is Patch.nullValue(): required field cannot be null
  • lag is NaN or infinite: lag must be finite

A rejection sets ApplyReport.needsResync. See Project#apply for what that means for later calls.

Example#

Turn link "a" into a start-to-start link with a two-hour lag and leave its tasks as they are:

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

public class EditLink {
  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.num(1)).withStartDate(monday).withDuration(2.0),
            new TaskData(Id.num(2)).withStartDate(monday).withDuration(1.0)))
        .withLinks(List.of(
            new LinkData().withId(Id.str("a")).withSource(Id.num(1)).withTarget(Id.num(2))));

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

    SchedulingChanges changes = new SchedulingChanges().withBaselineId(solved.baselineId);
    changes.links.update.add(new LinkUpdate(Id.str("a"))
        .withLinkType(Patch.of(LinkType.SS))
        .withLag(Patch.of(2.0))
        .withLagUnit(Patch.of(TimeUnit.HOUR)));

    ApplyReport report = project.apply(changes);
    for (RejectedOperation rejected : report.rejected) {
      System.out.println(rejected.path + ": " + rejected.message);
    }

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