Requirement#

A staffing need on one task: count workers who satisfy every SkillMatch in matches. Create one with new Requirement(id, task, matches) and pass a list of requirements to SkillLayer.slots together with a Pool. Each requirement becomes count slots.

Declaration#

public class Requirement extends ValueRecord implements DeepCloneable<Requirement>

Constructors#

Requirement(Id, Id, Vec)#

public Requirement(Id id, Id task, Vec<SkillMatch> matches)
public Requirement(Id id, Id task, java.util.Collection<? extends SkillMatch> matches)

Creates a requirement with count 1, units 1.0 and assignments set to null.

Name Type Description
id Id Requirement id, unique among the requirements passed to one call.
task Id The task that needs the workers.
matches Vec<SkillMatch> Skill conditions a worker must meet.

The Vec overload stores the given list itself. The Collection overload copies the elements into a new Vec, so List.of(...) works; null stays null.

Fields#

Field Type Default Description
id Id set by constructor Requirement id; the base of its slot ids.
task Id set by constructor The task the slots belong to.
matches Vec<SkillMatch> set by constructor Conditions that all must hold for a worker to be allowed.
count int 1 Number of slots, one per worker needed.
units double 1.0 Value of an assignment that fills one of the slots.
assignments Vec<Id> null Existing assignments bound to the slots explicitly; null = automatic binding.

SkillLayer.slots checks each requirement and throws MatchException.Invalid with a requirements[i]… path when a field is wrong; see Errors.

id#

public Id id = null;

Slot ids are "<id>#<n>" with n from 1 to count (see SkillLayer.slotId). A slot id becomes the id of the assignment that fills the slot, so an id stays tied to its task: after a commit, reusing it for another task makes the call fail.

matches#

public Vec<SkillMatch> matches = null;

A worker is allowed on the slots only when every match holds. An empty list allows every worker in the pool. The field must not be null when the requirement is used.

count#

public int count = 1;

Must be 1 or more.

units#

public double units = 1.0;

Becomes the value of each slot, which is the units of the new assignment that fills it. Must be finite and greater than 0.

assignments#

public Vec<Id> assignments = null;

With null, the task's existing assignments are bound to these slots automatically. A list replaces automatic binding for this requirement: listed assignments take the slots in order, and slots left over stay empty. An empty list leaves all slots empty. An assignment whose id is one of this requirement's slot ids always keeps its slot and must be in the list. The list can't hold more entries than count. SkillLayer.slots describes binding in full.

Methods#

withId, withTask, withCount, withUnits#

public Requirement withId(Id value)
public Requirement withTask(Id value)
public Requirement withCount(int value)
public Requirement withUnits(double value)

Set the field and return this requirement.

withMatches, withAssignments#

public Requirement withMatches(Vec<SkillMatch> value)
public Requirement withMatches(java.util.Collection<? extends SkillMatch> value)
public Requirement withAssignments(Vec<Id> value)
public Requirement withAssignments(java.util.Collection<? extends Id> value)

Set the field and return this requirement. The Vec overloads store the given list itself. The Collection overloads copy the elements into a new Vec; null sets the field to null.

deepClone#

public Requirement deepClone()

Returns a deep copy. The matches list, each SkillMatch in it and the assignments list are copied. Throws NullPointerException when matches is null.

copy#

public Requirement copy()

Returns a shallow copy. The copy holds the same matches and assignments lists as the original.

equals, hashCode, toString#

Inherited from ValueRecord. Two requirements are equal when all fields are equal; lists compare element by element in order. toString() returns Requirement{id=…, task=…, matches=[…], count=…, units=…, assignments=…}.

SkillMatch#

One condition on a worker's level in one skill. Create one with the static factories.

public class SkillMatch extends ValueRecord implements DeepCloneable<SkillMatch>
Field Type Default Description
skill Id null The skill to check.
op SkillOp SkillOp.AT_LEAST How the worker's level compares to value.
value double 1.0 The level to compare with. Must be finite.

new SkillMatch() with only skill set means "has the skill" (level 1 or more). Each field has a fluent setter: withSkill, withOp, withValue.

atLeast, noMore, equal#

public static SkillMatch atLeast(Id skill, double value)
public static SkillMatch noMore(Id skill, double value)
public static SkillMatch equal(Id skill, double value)

Return a match with op set to AT_LEAST, NO_MORE or EQUAL.

holds#

public boolean holds(Worker worker)

Returns true when the Worker's level in skill satisfies op and value. A skill the worker doesn't list counts as level 0.

deepClone, copy#

public SkillMatch deepClone()
public SkillMatch copy()

Return a copy. SkillMatch holds no lists, so both copies are independent of the original.

SkillOp#

The comparison a SkillMatch applies. All three are inclusive.

public enum SkillOp
Value Description
AT_LEAST Worker level >= value. The default.
NO_MORE Worker level <= value.
EQUAL Worker level == value.

Example#

Task api needs two backend developers at level 2 or higher who aren't juniors.

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

public class RequirementExample {
  public static void main(String[] args) throws Exception {
    Requirement backend = new Requirement(
        Id.str("api-dev"),
        Id.str("api"),
        List.of(
            SkillMatch.atLeast(Id.str("backend"), 2),
            SkillMatch.noMore(Id.str("junior"), 0)))
        .withCount(2);

    Worker ann = new Worker().withResource(Id.str("ann"))
        .withSkills(List.of(new Skill().withId(Id.str("backend")).withValue(3)));

    boolean allowed = backend.matches.stream().allMatch(m -> m.holds(ann));
    System.out.println(allowed); // true
  }
}

Passed to SkillLayer.slots, this requirement produces the slots api-dev#1 and api-dev#2.