Taxonomy.h header

#include <ew/core/Taxonomy.h>

Namespace ew::core

Taxonomy class

class ew::core::Taxonomy

The user-defined codex taxonomy: the tree of Categories for a project. It owns the categories and answers structural queries (roots, children). Categories are keyed by id in a std::map so on-disk order is stable and diff-friendly. Removing a category promotes its children to its former parent so the tree stays connected. (Field inheritance across the tree is layered on in a later step.)

Members

ew::core::taxonomy::Taxonomy::Taxonomy()

Constructs an empty taxonomy.

ew::core::taxonomy::Taxonomy::~Taxonomy()

Destroys the taxonomy and its categories.

ew::core::taxonomy::Taxonomy::Taxonomy(const Taxonomy &)=delete

Not copyable.

ew::core::taxonomy::Taxonomy::Taxonomy(Taxonomy &&)=delete

Not movable.

Taxonomy & ew::core::taxonomy::Taxonomy::operator=(const Taxonomy &)=delete

Not copyable.

Taxonomy & ew::core::taxonomy::Taxonomy::operator=(Taxonomy &&)=delete

Not movable.

bool ew::core::taxonomy::Taxonomy::add(Category category)

Adds category. Returns false (and changes nothing) if its id already exists.

bool ew::core::taxonomy::Taxonomy::remove(ew::core::foundation::CategoryId id)

Removes the category with id, promoting its children to its former parent. Returns true if a category was removed.

bool ew::core::taxonomy::Taxonomy::contains(ew::core::foundation::CategoryId id) const

Returns true if a category with id exists.

const Category * ew::core::taxonomy::Taxonomy::find(ew::core::foundation::CategoryId id) const

Returns the category with id, or nullptr if none.

Category * ew::core::taxonomy::Taxonomy::find(ew::core::foundation::CategoryId id)

Returns the mutable category with id (for editing), or nullptr if none.

int ew::core::taxonomy::Taxonomy::categoryCount() const

The number of categories in the taxonomy.

std::vector< ew::core::foundation::CategoryId > ew::core::taxonomy::Taxonomy::ids() const

All category ids, in stable (id-sorted) order.

std::vector< const Category * > ew::core::taxonomy::Taxonomy::roots() const

The root categories (those with no parent), in id-sorted order.

std::vector< const Category * > ew::core::taxonomy::Taxonomy::children(ew::core::foundation::CategoryId id) const

The direct children of the category with id, in id-sorted order.

std::vector< FieldDefinition > ew::core::taxonomy::Taxonomy::effectiveFields(ew::core::foundation::CategoryId id) const

The fields that apply to the category with id: its own fields plus every ancestor's, ordered from the root down, with a descendant's field overriding an ancestor's when they share a key. Empty if id is unknown. Cycle-safe.

std::vector< ew::core::foundation::CategoryId > ew::core::taxonomy::Taxonomy::declaringCategories(ew::core::foundation::CategoryId id, QStringView key) const

The categories that DECLARE the field key for the category id: every category in id's chain, ordered from the root down, whose OWN fields include key. Empty when id is unknown or nothing in its chain declares the key. Cycle-safe.

Its first entry is the category that fixes the field's POSITION in effectiveFields: a descendant that re-declares an ancestor's key overrides the definition but keeps the ancestor's place in the order. So a "move this field" must edit the first entry – editing any other reorders a list the reader never sees and looks like a button that does nothing – and a "remove this field" must remove EVERY entry, because leaving one behind leaves the field on the category.

QString ew::core::taxonomy::Taxonomy::fieldMoveNeighbour(ew::core::foundation::CategoryId id, QStringView key, int delta) const

The key of the field that key would trade places with if it moved delta positions (-1 up, +1 down) in id's effectiveFields list, or an empty string when no such move exists. Cycle-safe.

This is the one definition of "can this field move", because the list a reader sees is the effective one and only some moves change it. There is no neighbour when key is at an end of the list, and none when the neighbour's order is fixed by a DIFFERENT category: each category contributes a contiguous block to the effective list, root-down, and no single move can carry a field out of its block. Answering from the effective list also rules out the move that quietly does nothing – swapping a field with a same-category sibling that merely OVERRIDES an ancestor's field, whose place in the list an ancestor owns.

std::optional< std::size_t > ew::core::taxonomy::Taxonomy::fieldMoveTarget(ew::core::foundation::CategoryId id, QStringView key, QStringView beforeKey) const

Where MoveFieldCommand should put key – an index among the fields of the category that DECLARES it – so that key lands immediately before beforeKey in id's effective list, or last in its category's block when beforeKey is empty. Nothing when the move is impossible or pointless: an unknown key, or a beforeKey whose order a different category owns (no single move crosses a block). Cycle-safe.

This is fieldMoveNeighbour's counterpart for a drag, which names a destination rather than a direction. It is here, not in the caller, because translating "before that one" into an index is where the subtleties live: the index is the one AFTER the field has been taken out of the list, and a category's own fields are not all in its block – one that overrides an ancestor's sits elsewhere in the effective list, so it must be stepped over rather than counted.

ew::core::foundation::Result< void > ew::core::taxonomy::Taxonomy::reparent(ew::core::foundation::CategoryId child, ew::core::foundation::CategoryId newParent)

Reparents child under newParent (a null id makes it a root). Fails if child or a non-null newParent is unknown, or if the move would create a cycle (newParent is child or one of its descendants).

std::vector< const Category * > ew::core::taxonomy::Taxonomy::ancestorChain(ew::core::foundation::CategoryId id) const

The category with id together with its ancestors, ordered from the ROOT down – the order in which fields merge, so a descendant's field overrides an ancestor's. Empty if id is unknown. Cycle-safe: a category already seen ends the walk.

Public because "walk from a category to its root" is a question consumers keep asking – which categories a property could move up to, whether an entity sits beneath a given category – and each private answer was another hand-rolled walk that had to remember the cycle guard for itself.