Source code for flexicon.code.Grammar.affix_template_collection

#
#   affix_template_collection.py
#
#   Class: AffixTemplateCollection
#          Smart collection for affix templates with type-aware filtering
#          and unified access across templates.
#
#   Platform: Python.NET
#             FieldWorks Version 9+
#
#   Copyright 2025
#

"""
Smart collection class for affix templates.

This module provides AffixTemplateCollection, a smart collection that manages
affix templates while supporting unified operations and convenient filtering.

Problem:
    GetAll() returns multiple templates from different parts of speech.
    Users need to:
    - See how many templates are in the collection
    - Filter by common criteria (name, stratum, slot availability)
    - Work with templates without manual casting or checking

Solution:
    AffixTemplateCollection provides:
    - __str__() showing template summary
    - filter() for common criteria (name_contains, stratum)
    - Convenience methods (with_prefix_slots, with_suffix_slots, etc.)
    - Chainable filtering: templates.with_prefix_slots().filter(name_contains='Verb')
    - where() for custom predicates

Example::

    from flexlibs2.code.Grammar.affix_template_collection import AffixTemplateCollection

    # GetAll() now returns AffixTemplateCollection
    templates = morphRuleOps.GetAllAffixTemplates()
    print(templates)  # Shows collection summary

    # Filter by slots
    prefix_templates = templates.with_prefix_slots()
    print(f"Templates with prefix slots: {len(prefix_templates)}")

    # Filter by name
    verb_templates = templates.filter(name_contains='Verb')

    # Chain filters
    verb_prefix = templates.with_prefix_slots().filter(name_contains='Verb')

    # Iterate
    for template in templates:
        print(template.name)
"""

from ..Shared.smart_collection import SmartCollection
from .affix_template import AffixTemplate


[docs] class AffixTemplateCollection(SmartCollection): """ Smart collection for affix templates with filtering capabilities. Manages collections of affix templates (AffixTemplate wrapper objects) with type-aware display and filtering capabilities. Supports filtering by common properties (name, stratum, slot availability). Attributes: _items: List of AffixTemplate wrapper objects Example:: templates = morphRuleOps.GetAllAffixTemplates() print(templates) # Shows collection summary prefix = templates.with_prefix_slots() # Filter to templates with prefixes verb = templates.filter(name_contains='Verb') # Name filter both = templates.with_prefix_slots().filter(name_contains='Verb') # Chain """ def __init__(self, items=None): """ Initialize an AffixTemplateCollection. Args: items: Iterable of AffixTemplate objects, or None for empty. Example:: collection = AffixTemplateCollection() collection = AffixTemplateCollection([template1, template2, template3]) """ super().__init__(items)
[docs] def filter(self, name_contains=None, stratum=None, where=None): """ Filter the collection by common template properties. Supports filtering by properties that work across all templates (name, stratum). For complex filtering, use where(). Args: name_contains (str, optional): Filter to templates whose name contains this string (case-sensitive). stratum (str, optional): Filter by stratum name or reference. where (callable, optional): Custom predicate function. If provided, other criteria are ignored. Returns: AffixTemplateCollection: New collection with filtered items. Example:: # Filter by name verb_templates = templates.filter(name_contains='Verb') # Filter by stratum stratum1 = templates.filter(stratum='Stratum1') # Custom filtering with_slots = templates.where(lambda t: t.has_any_slots) # Chain filters verb_stratum = templates.filter(name_contains='Verb').filter(stratum='Stratum1') Notes: - name_contains is case-sensitive - All criteria are AND-ed together unless only one is provided - where() takes precedence over other criteria - Returns new collection (doesn't modify original) - Use where() for complex custom filtering """ # If custom predicate provided, use it if where is not None: filtered = [item for item in self._items if where(item)] return AffixTemplateCollection(filtered) # Apply criteria filters filtered = self._items if name_contains is not None: filtered = [template for template in filtered if name_contains in (template.name or "")] if stratum is not None: filtered = [ template for template in filtered if template.stratum and (stratum in (template.stratum.Name or "") or str(stratum) == str(template.stratum)) ] return AffixTemplateCollection(filtered)
[docs] def where(self, predicate): """ Filter using a custom predicate function. For filtering by complex criteria or properties not supported by filter(). Args: predicate (callable): Function that takes an AffixTemplate and returns True to include it in the result. Returns: AffixTemplateCollection: New collection with items matching the predicate. Example:: # Complex predicate: templates with both prefix and suffix slots full_templates = templates.where( lambda t: t.has_prefix_slots and t.has_suffix_slots ) # Combining conditions complex_filter = templates.where( lambda t: t.has_any_slots and (t.name or "").startswith('Verb') ) # Capability checking prefix_templates = templates.where(lambda t: t.has_prefix_slots) Notes: - Predicate receives each AffixTemplate object - Return True to include in result, False to exclude - For simple filters (name, stratum), use filter() instead """ filtered = [template for template in self._items if predicate(template)] return AffixTemplateCollection(filtered)
[docs] def with_prefix_slots(self): """ Get only the templates that have prefix slots. Convenience method for filtering to templates with prefix slots. Returns: AffixTemplateCollection: New collection with templates that have prefix slots. Example:: prefix_templates = templates.with_prefix_slots() print(f"Found {len(prefix_templates)} templates with prefix slots") # Chain with other filters verb_prefix = templates.with_prefix_slots().filter(name_contains='Verb') Notes: - Equivalent to where(lambda t: t.has_prefix_slots) - Use has_prefix_slots on individual templates to check """ return self.where(lambda t: t.has_prefix_slots)
[docs] def with_suffix_slots(self): """ Get only the templates that have suffix slots. Convenience method for filtering to templates with suffix slots. Returns: AffixTemplateCollection: New collection with templates that have suffix slots. Example:: suffix_templates = templates.with_suffix_slots() print(f"Found {len(suffix_templates)} templates with suffix slots") # Chain with other filters verb_suffix = templates.with_suffix_slots().filter(name_contains='Verb') Notes: - Equivalent to where(lambda t: t.has_suffix_slots) - Use has_suffix_slots on individual templates to check """ return self.where(lambda t: t.has_suffix_slots)
[docs] def with_proclitic_slots(self): """ Get only the templates that have proclitic slots. Convenience method for filtering to templates with proclitic slots. Returns: AffixTemplateCollection: New collection with templates that have proclitic slots. Example:: proclitic_templates = templates.with_proclitic_slots() print(f"Found {len(proclitic_templates)} templates with proclitic slots") Notes: - Equivalent to where(lambda t: t.has_proclitic_slots) - Use has_proclitic_slots on individual templates to check """ return self.where(lambda t: t.has_proclitic_slots)
[docs] def with_enclitic_slots(self): """ Get only the templates that have enclitic slots. Convenience method for filtering to templates with enclitic slots. Returns: AffixTemplateCollection: New collection with templates that have enclitic slots. Example:: enclitic_templates = templates.with_enclitic_slots() print(f"Found {len(enclitic_templates)} templates with enclitic slots") Notes: - Equivalent to where(lambda t: t.has_enclitic_slots) - Use has_enclitic_slots on individual templates to check """ return self.where(lambda t: t.has_enclitic_slots)
[docs] def with_slots(self, slot_type): """ Get only the templates that have slots of a specific type. Convenience method for filtering to specific slot types. Args: slot_type (str): Type of slots to filter by: 'prefix', 'suffix', 'proclitic', or 'enclitic'. Returns: AffixTemplateCollection: New collection with templates that have the specified slots. Example:: # Get templates with prefix slots prefix_templates = templates.with_slots('prefix') # Get templates with suffix slots suffix_templates = templates.with_slots('suffix') # Get templates with proclitic slots proclitic_templates = templates.with_slots('proclitic') Notes: - slot_type must be one of: 'prefix', 'suffix', 'proclitic', 'enclitic' - Returns empty collection if slot_type is not recognized """ slot_type = (slot_type or "").lower() if slot_type == "prefix": return self.with_prefix_slots() elif slot_type == "suffix": return self.with_suffix_slots() elif slot_type == "proclitic": return self.with_proclitic_slots() elif slot_type == "enclitic": return self.with_enclitic_slots() else: # Unrecognized slot type, return empty collection return AffixTemplateCollection([])
[docs] def with_any_slots(self): """ Get only the templates that have any slots. Convenience method for filtering to templates with at least one slot. Returns: AffixTemplateCollection: New collection with templates that have slots. Example:: templates_with_slots = templates.with_any_slots() print(f"Found {len(templates_with_slots)} templates with slots") Notes: - Equivalent to where(lambda t: t.has_any_slots) """ return self.where(lambda t: t.has_any_slots)
[docs] def full_templates(self): """ Get only the templates that have all four slot types. Convenience method for filtering to templates with prefix, suffix, proclitic, and enclitic slots. Returns: AffixTemplateCollection: New collection with full-featured templates. Example:: full_templates = templates.full_templates() print(f"Found {len(full_templates)} templates with all slot types") Notes: - Equivalent to:: where(lambda t: all([ t.has_prefix_slots, t.has_suffix_slots, t.has_proclitic_slots, t.has_enclitic_slots ])) """ return self.where( lambda t: (t.has_prefix_slots and t.has_suffix_slots and t.has_proclitic_slots and t.has_enclitic_slots) )
[docs] def for_pos(self, pos_name_or_object): """ Get only the templates for a specific part of speech. Convenience method for filtering templates by their owner POS. Args: pos_name_or_object: Either a POS name (str) or a POS object. Returns: AffixTemplateCollection: New collection with templates for the specified POS. Example:: # Filter by POS name verb_templates = templates.for_pos('Verb') # Filter by POS object verb_pos = posOps.Find('Verb') verb_templates = templates.for_pos(verb_pos) Notes: - If pos_name_or_object is a string, looks for POS names containing the string - Returns empty collection if no templates found for the POS """ if isinstance(pos_name_or_object, str): pos_name = pos_name_or_object return self.where(lambda t: t.owner_pos and pos_name in (t.owner_pos.Name or "")) else: pos_obj = pos_name_or_object return self.where(lambda t: t.owner_pos and t.owner_pos == pos_obj)
def __repr__(self): """Technical representation.""" return f"AffixTemplateCollection({len(self._items)} templates)"