[Canonical page](<https://docs.dhtmlx.com/planning-engine/java/api/results/analytics/>)
[Full documentation index](<https://docs.dhtmlx.com/planning-engine/llms.txt>)


# Analytics

The critical path and slack of a schedule. It's read from [`ScheduleResult.analytics`](<https://docs.dhtmlx.com/planning-engine/java/api/results/schedule-result.md#analytics>), which is set only when the call requested at least one analytic through [`AnalyticsOptions`](<https://docs.dhtmlx.com/planning-engine/java/api/project/analytics-options.md>). The engine creates the values; caller code only reads them.

## Declaration

```java
package org.dhtmlx.plen;

public class Analytics extends ValueRecord implements DeepCloneable<Analytics>
```

Every field is public and mutable, and has a `withX(value)` setter that assigns it and returns `this`. `equals`, `hashCode` and `toString` come from [`ValueRecord`](<https://docs.dhtmlx.com/planning-engine/java/api/support/value-record.md>) and compare `status`, `criticalPath` and `slack`.

## Fields

| Field | Type | Description |
|---|---|---|
| `status` | [`AnalyticsStatus`](<https://docs.dhtmlx.com/planning-engine/java/api/results/analytics.md#analyticsstatus>) | Whether the analytics could be computed. Check it before reading the other fields. |
| `criticalPath` | [`Critical`](<https://docs.dhtmlx.com/planning-engine/java/api/results/analytics.md#critical>) | The critical tasks and links. Set when `status` is `AVAILABLE` and `AnalyticsOptions.criticalPath` is `true`, otherwise `null`. |
| `slack` | [`Vec<TaskSlack>`](<https://docs.dhtmlx.com/planning-engine/java/api/support/vec.md>) | One entry per active leaf task and milestone. Set when `status` is `AVAILABLE` and `AnalyticsOptions.slack` is `true`, otherwise `null`. |

### status

```java
public AnalyticsStatus status = AnalyticsStatus.AVAILABLE;
public Analytics withStatus(AnalyticsStatus value)
```

`INVALID` when the dependency graph has a cycle. Both `criticalPath` and `slack` are then `null`, whatever was requested. The cycles themselves are in [`ScheduleResult.cycles`](<https://docs.dhtmlx.com/planning-engine/java/api/results/schedule-result.md#cycle>).

### criticalPath

```java
public Critical criticalPath = null;
public Analytics withCriticalPath(Critical value)
```

### slack

```java
public Vec<TaskSlack> slack = null;
public Analytics withSlack(Vec<TaskSlack> value)
public Analytics withSlack(java.util.Collection<? extends TaskSlack> value)
```

The `Collection` overload copies the argument into a new `Vec`, or stores `null` for a `null` argument.

## Methods

### deepClone

```java
public Analytics deepClone()
```

Returns a recursive copy: a new `Critical` and a new list of new `TaskSlack` entries. A `null` field stays `null`.

**Returns** - a new `Analytics`.

### copy

```java
public Analytics copy()
```

Returns a shallow copy that shares `criticalPath` and the `slack` list with this instance.

**Returns** - a new `Analytics`.

## AnalyticsStatus

Whether the analytics in an `Analytics` value could be computed.

```java
public enum AnalyticsStatus {
  AVAILABLE,
  INVALID
}
```

| Value | Description |
|---|---|
| `AVAILABLE` | The requested fields are filled. |
| `INVALID` | The dependency graph is cyclic. `criticalPath` and `slack` are `null`. |

## Critical

The tasks and links on the critical path.

```java
public class Critical extends ValueRecord implements DeepCloneable<Critical>
```

| Field | Type | Default | Description |
|---|---|---|---|
| `tasks` | [`Vec<Id>`](<https://docs.dhtmlx.com/planning-engine/java/api/support/vec.md>) | empty | Critical tasks: every leaf task or milestone with total slack of zero or less, plus every summary with at least one critical leaf below it. Completed tasks are never critical. |
| `links` | [`Vec<Id>`](<https://docs.dhtmlx.com/planning-engine/java/api/support/vec.md>) | empty | Links whose source and target are both critical and that leave no margin between them. Each link appears once. |

Both fields have `withTasks` / `withLinks` setters that take a `Vec<Id>` or any `java.util.Collection<? extends Id>`. `deepClone()` copies both lists. `copy()` returns a shallow copy that shares them.

## TaskSlack

The slack of one task.

```java
public class TaskSlack extends ValueRecord implements DeepCloneable<TaskSlack>
```

| Field | Type | Default | Description |
|---|---|---|---|
| `task` | [`Id`](<https://docs.dhtmlx.com/planning-engine/java/api/data/id.md>) | `null` | The task. |
| `total` | `double` | `0.0` | How far the task can move later without moving the project end. Negative when the task already runs past its latest date. |
| `free` | `double` | `0.0` | How far the task can move later without moving any successor. Never larger than `total`. |

Both values are in the project's [`durationUnit`](<https://docs.dhtmlx.com/planning-engine/java/api/data/scheduling-config.md#durationunit>). A completed task (with [`useProgress`](<https://docs.dhtmlx.com/planning-engine/java/api/data/scheduling-config.md#useprogress>) on and `progress == 1`) has `total` and `free` of `0.0`. Each field has a `withX` setter. `deepClone()` and `copy()` both return a new `TaskSlack` with the same values.

[Critical path and slack](<https://docs.dhtmlx.com/planning-engine/java/scheduling/critical-path-and-slack.md>) covers how the engine computes both.

## Example

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

public class AnalyticsExample {
  public static void main(String[] args) throws Exception {
    Moment monday = Moment.fromYmdHm(2026, 1, 5, 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("docs")).withStartDate(monday).withDuration(1.0)))
        .withLinks(List.of(
            new LinkData().withId(Id.str("l1"))
                .withSource(Id.str("design"))
                .withTarget(Id.str("build"))));

    SolveOptions options = new SolveOptions()
        .withAnalytics(new AnalyticsOptions().withCriticalPath(true).withSlack(true));
    Analytics analytics = Engine.schedule(data, options).analytics;

    if (analytics.status == AnalyticsStatus.INVALID) {
      System.out.println("dependency cycle, no analytics");
      return;
    }

    System.out.println("critical tasks: " + analytics.criticalPath.tasks);
    System.out.println("critical links: " + analytics.criticalPath.links);
    for (TaskSlack s : analytics.slack) {
      System.out.println(s.task + ": total " + s.total + ", free " + s.free);
    }
    // critical tasks: [design, build]
    // critical links: [l1]
    // design: total 0.0, free 0.0
    // build: total 0.0, free 0.0
    // docs: total 4.0, free 4.0
  }
}
```

## Related

- [`AnalyticsOptions`](<https://docs.dhtmlx.com/planning-engine/java/api/project/analytics-options.md>) - requesting critical path and slack
- [`ScheduleResult`](<https://docs.dhtmlx.com/planning-engine/java/api/results/schedule-result.md>) - the result that carries `analytics`
- [`Project#analyze`](<https://docs.dhtmlx.com/planning-engine/java/api/project-analyze.md>) - analytics without scheduling
- [Critical path and slack](<https://docs.dhtmlx.com/planning-engine/java/scheduling/critical-path-and-slack.md>) - how critical path and slack are computed
