NGMTask.java

/* ========================================================================
 * PlantUML : a free UML diagram generator
 * ========================================================================
 *
 * (C) Copyright 2009-2024, Arnaud Roques
 *
 * Project Info:  https://plantuml.com
 * 
 * If you like this project or if you find it useful, you can support us at:
 * 
 * https://plantuml.com/patreon (only 1$ per month!)
 * https://plantuml.com/paypal
 * 
 * This file is part of PlantUML.
 *
 * PlantUML is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * PlantUML distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
 * USA.
 *
 *
 * Original Author:  Arnaud Roques
 * 
 *
 */
package net.sourceforge.plantuml.project.ngm;

import java.time.Duration;
import java.time.LocalDateTime;

/**
 * Represents an abstract scheduled task in the New Gantt Model (NGM).
 *
 * <p>
 * This model is built around three independent notions:
 * </p>
 *
 * <ul>
 *   <li><b>Total effort</b> — an {@link NGMTotalEffort} expressing the total amount of
 *       work associated with the task, typically in person-time
 *       (for example, person-seconds or person-hours).</li>
 *
 *   <li><b>Allocation</b> — an {@link NGMAllocation} representing the effective
 *       full-time-equivalent (FTE) assigned to the task
 *       (e.g., 1 = 100%, 1/2 = 50%, 5/7 = weekdays only, 2 = two persons).</li>
 *
 *   <li><b>Duration</b> — a {@link Duration} representing the calendar span
 *       between the start and end instants.</li>
 * </ul>
 *
 * <p>
 * These three quantities must remain conceptually independent. Confusing them leads to
 * incorrect scheduling behaviour. The goal of NGM is to provide a clean and unambiguous
 * task model using {@code java.time} and explicit resource-allocation logic.
 * </p>
 *
 *
 * <h3>Task behaviour</h3>
 *
 * <p>
 * At the scheduling level, a task can behave in one of two ways:
 * </p>
 *
 * <ul>
 *   <li><b>Fixed-total-effort task</b>: The total effort is intrinsic and does not change.
 *       The scheduled duration is computed from the total effort and the allocation.
 *       Example: “this task requires 80 hours of work”.</li>
 *
 *   <li><b>Fixed-duration task</b>: The calendar span is intrinsic and does not change.
 *       The total effort becomes a derived quantity and depends on the allocation.
 *       Example: “crossing the Atlantic takes 7 days regardless of crew size”.</li>
 * </ul>
 *
 * <p>
 * This distinction is crucial: without it, the scheduler cannot make consistent
 * decisions about resource allocation, overlapping tasks, or long-running schedules.
 * </p>
 *
 *
 * <h3>Why allocation is final</h3>
 *
 * <p>
 * The allocation represents the <em>structural capacity</em> assigned to the task.
 * It may influence start date, end date, duration, or total effort (depending on the
 * task type), but it is not modified by them.
 * </p>
 *
 * <p>
 * By contrast, the temporal attributes (<code>start</code>, <code>end</code>,
 * <code>duration</code>) as well as the total effort (for fixed-duration tasks) may
 * vary depending on scheduling decisions, calendars, dependencies, or external constraints.
 * </p>
 *
 *
 * <h3>Factory methods</h3>
 *
 * <p>
 * The static factory methods
 * {@link #withFixedDuration(NGMAllocation, Duration)} and
 * {@link #withFixedTotalEffort(NGMAllocation, NGMTotalEffort)}
 * create concrete implementations representing these two behaviours.
 * </p>
 */
public abstract class NGMTask {


	protected final NGMAllocation allocation;

	/**
	 * Creates a new task with a fixed workload allocation.
	 *
	 * @param workload the constant full-time-equivalent allocation applied to this
	 *                 task
	 */
	protected NGMTask(NGMAllocation allocation) {
		this.allocation = allocation;
	}

	/**
	 * Returns the current start instant of the task.
	 *
	 * <p>
	 * The start and end instants are not independent values in the NGM model.
	 * They form a coherent scheduled window derived from the task type
	 * (fixed-duration vs fixed-total-effort), the allocation, and the
	 * scheduling constraints.
	 * </p>
	 *
	 * @return the scheduled start instant
	 */
	public abstract LocalDateTime getStart();

	/**
	 * Sets (anchors) the start instant of the task.
	 *
	 * <p>
	 * This method is a scheduling input. Calling it triggers a recomputation of
	 * the task's scheduled window. As a consequence, after this call:
	 * </p>
	 *
	 * <ul>
	 *   <li>{@link #getStart()} will reflect the newly anchored start, and</li>
	 *   <li>{@link #getEnd()} will be recalculated to maintain a consistent task model.</li>
	 * </ul>
	 *
	 * <p>
	 * The exact recomputation rules depend on the concrete task type and may take into
	 * account the intrinsic duration or total effort, the constant allocation, and
	 * the scheduling calendar.
	 * </p>
	 *
	 * @param start the start instant to anchor for scheduling
	 */
	public abstract void setStart(LocalDateTime start);

	/**
	 * Returns the current end instant of the task.
	 *
	 * <p>
	 * The start and end instants are not independent values in the NGM model.
	 * They form a coherent scheduled window derived from the task type
	 * (fixed-duration vs fixed-total-effort), the allocation, and the
	 * scheduling constraints.
	 * </p>
	 *
	 * @return the scheduled end instant
	 */
	public abstract LocalDateTime getEnd();

	/**
	 * Sets (anchors) the end instant of the task.
	 *
	 * <p>
	 * This method is a scheduling input. Calling it triggers a recomputation of
	 * the task's scheduled window. As a consequence, after this call:
	 * </p>
	 *
	 * <ul>
	 *   <li>{@link #getEnd()} will reflect the newly anchored end, and</li>
	 *   <li>{@link #getStart()} will be recalculated to maintain a consistent task model.</li>
	 * </ul>
	 *
	 * <p>
	 * The exact recomputation rules depend on the concrete task type and may take into
	 * account the intrinsic duration or total effort, the constant allocation, and
	 * the scheduling calendar.
	 * </p>
	 *
	 * @param end the end instant to anchor for scheduling
	 */
	public abstract void setEnd(LocalDateTime end);


	/**
	 * Returns the effective scheduled duration of the task.
	 *
	 * <p>
	 * This value is not always equal to <code>end - start</code>. In practice, the
	 * duration depends on:
	 * </p>
	 *
	 * <ul>
	 * <li>the start and end instants,</li>
	 * <li>the working calendar (non-working days, holidays, weekends),</li>
	 * <li>whether the task is fixed-load or fixed-duration.</li>
	 * </ul>
	 *
	 * <p>
	 * For a fixed-duration task, the duration is intrinsic and constant even if the
	 * start or end dates shift due to calendar constraints. For a fixed-load task,
	 * the duration must be computed from the intrinsic load, the assigned workload
	 * (FTE), and the working calendar.
	 * </p>
	 *
	 * <p>
	 * Because of these factors, the duration may represent the scheduled "active
	 * working time" rather than a simple chronological difference.
	 * </p>
	 *
	 * @return the computed scheduled duration of the task
	 */
	public abstract Duration getDuration();

	/**
	 * Returns the total effort associated with this task.
	 *
	 * <p>
	 * The term <b>total effort</b> refers to the overall amount of work required to
	 * complete the task, independent of the calendar span. It is typically
	 * expressed in person-time (for example, person-seconds or person-hours).
	 * </p>
	 *
	 * <p>
	 * Depending on the concrete task type, this value may be:
	 * </p>
	 * <ul>
	 * <li><b>intrinsic (fixed)</b> — the effort is defined directly by the user and
	 * does not change when dates or allocation change (e.g., “this task requires 80
	 * hours of work”).</li>
	 * <li><b>computed (derived)</b> — the effort is calculated from other defining
	 * properties such as the task’s duration, the effective allocation, and any
	 * scheduling constraints.</li>
	 * </ul>
	 *
	 * <p>
	 * In other words, this method provides a single, consistent access point to the
	 * task’s “work quantity”, whether that quantity is a primary input or a
	 * secondary result of the scheduling model.
	 * </p>
	 *
	 * @return the intrinsic or computed total effort of the task
	 */
	public abstract NGMTotalEffort getTotalEffort();

	/**
	 * Returns the constant allocation applied to the task.
	 *
	 * <p>
	 * The <b>allocation</b> represents the effective full-time-equivalent (FTE)
	 * assigned to the task. It describes the resource intensity available for
	 * executing the work, and therefore influences derived scheduling properties.
	 * </p>
	 *
	 * <p>
	 * Examples of typical meanings:
	 * </p>
	 * <ul>
	 * <li><code>1</code> — one full-time equivalent (100%).</li>
	 * <li><code>1/2</code> — half-time allocation (50%).</li>
	 * <li><code>2</code> — two full-time equivalents (two people at 100%).</li>
	 * <li><code>5/7</code> — an allocation constrained to weekdays only, if your
	 * model uses such fractions to represent availability patterns.</li>
	 * </ul>
	 *
	 * <p>
	 * This method returns the constant allocation associated with the task
	 * instance. Task types that support variable or time-sliced allocations may
	 * override or complement this behaviour with more advanced APIs as the model
	 * evolves.
	 * </p>
	 *
	 * @return the constant FTE allocation applied to the task
	 */
	public NGMAllocation getAllocation() {
		return allocation;
	}

	/**
	 * Creates a task whose duration is intrinsic (fixed) and does not depend on the
	 * assigned allocation.
	 *
	 * <p>
	 * In this type of task, the duration is the defining property: it remains
	 * constant regardless of how many resources are allocated.
	 * </p>
	 *
	 * <p>
	 * When a scheduling calendar is applied, the <em>start</em> and/or <em>end</em>
	 * instants may shift to satisfy availability constraints (for example,
	 * when certain days are closed or non-working). However, the overall scheduled
	 * duration represented by this task remains unchanged.
	 * </p>
	 *
	 * <p>
	 * The total effort is then derived from the allocation applied over this
	 * fixed-duration window.
	 * </p>
	 *
	 * <p>
	 * Example: a ship crossing the Atlantic takes a fixed number of days; assigning
	 * more or fewer crew members does not shorten or extend the trip, it only
	 * changes the total effort performed during that period.
	 * </p>
	 *
	 * @param allocation the constant full-time-equivalent allocation applied to the task
	 * @param duration   the intrinsic fixed calendar duration of the task
	 * @return a new fixed-duration task
	 */
	public static NGMTask withFixedDuration(NGMAllocation allocation, Duration duration) {
		return new NGMTaskFixedDuration(allocation, duration);
	}

	/**
	 * Creates a task whose total effort is intrinsic (fixed) and does not depend on the
	 * assigned allocation.
	 *
	 * <p>
	 * In this type of task, the total amount of work is the defining property:
	 * regardless of the calendar duration, the task requires a fixed quantity of
	 * person-time (for example, person-seconds or person-hours).
	 * </p>
	 *
	 * <p>
	 * The actual scheduled duration will be derived from the available allocation
	 * (FTE) and the scheduling constraints.
	 * </p>
	 *
	 * <p>
	 * Example: implementing a feature requires “80 hours of work”. Allocating
	 * additional resources reduces the duration, while reducing resources increases
	 * it. The intrinsic total effort itself does not change.
	 * </p>
	 *
	 * <p>
	 * This method will eventually return a concrete {@code NGMTask} implementation
	 * representing this behaviour. For now, it throws
	 * {@link UnsupportedOperationException} because the model is still under
	 * construction.
	 * </p>
	 *
	 * @param allocation  the constant full-time-equivalent allocation applied to the task
	 * @param totalEffort the intrinsic amount of work required for this task
	 * @return a new fixed-total-effort task (when implemented)
	 */
	public static NGMTask withFixedTotalEffort(NGMAllocation allocation, NGMTotalEffort totalEffort) {
		return new NGMTaskFixedTotalEffort(allocation, totalEffort);
	}

}