<commit_before>from uuid import uuid4
import warnings
import re
import copy
from . import _elementAccess
import types
# see http://freeplane.sourceforge.net/wiki/index.php/Current_Freeplane_File_Format for file specifications
# terminology: elem, element = MindMap Elements (no etree elements allowed! Use a mmFactory to convert those


class ExampleElement:  # example element showing minimum of things to define to inherit from BaseElement and create one.
    tag = ''  # [REQUIRED]. Only way for a Factory (which you must code) to
        # convert the element of type 'tag'. Examples include 'node' or 'cloud'
    attrib = {}  # [OPTIONAL] pre-define default element attributes
    specs = {}  # [OPTIONAL] pre-define attribute types (like bool, str, int, float).
    _descriptors = []  # [OPTIONAL] list of attribs that are used when constructing string representation

   

class BaseElement:
    """ pymm's Base Element. All other elements inherit from BaseElement, which represents an element in a similar style
    to xml.etree.ElementTree. with enhancements aimed at providing faster mindmap manipulation. Each element has a
    specific identifier, a tag, that specifies what type of element it is. If a specific xml element type does not have
    a corresponding pymm element, it will become a BaseElement, but the corresponding "BaseElement" tag will be replaced
    with the actual xml element's tag.

    It stores all its children in a list that you can access and modify @ BaseElement.children.
    BaseElement stores xml attributes in a dictionary, which can be accessed as if the element sub-classed Dict.
    e.g.: BaseE['TEXT'] or BaseE['SIZE'] can access BaseElement's attributes TEXT and SIZE, respectively. xml
    attributes are those key=value declarations that exist within an xml-elements opening (<) and closing (>) tags.
    The functions items(), keys(), and update() are used to access BaseElement's attributes.

    :param tag: the tag specifying type of element
    :param parent: a link to the element's parent element
    :param specs: a dictionary of expected xml attributes and their expected types (i.e. int, list, string, etc.).When
                changing an element's attribute, the new attribute will be checked against the specs, and an warning
                generated if the new attribute does not match the specs. Can be modified to allow additional specs. I.E.
                elem['TEXT'] = 'HI 5' sets the element's TEXT attribute to 'HI 5'. If specs contains the key 'TEXT', the
                type of value 'HI 5' (str) will be checked against the value of specs['TEXT'], and allowed if they match
    """
    tag = 'BaseElement'  # must set to cloud, hook, edge, etc.
    parent = None
    _text = ''    # equivalent to ElementTree's .text  -- text between start tag and next element   # keep this for
    _tail = ''    # equivalent to ElementTree's .tail  -- text after end tag                        # .mm compatibility
    children = []  # all child elements including nodes
    attrib = {}  # pre-define these (outside of init like this) in other classes to define default element attribs
    _descriptors = []  # list of attribs that can be used to better describe instance. Used in str(self) construction
    specs = {}  # list all possible attributes of an element and valid entries / types in a list or standalone:
                    # [str, int, 'thin', etc.], str, int, 'thin', etc.

    def __new__(cls, *args, **kwargs):
        """ DO NOT OVERRIDE W/O super. override __init__ for most stuff. Copy
        all the mutable class attributes such as children, attrib, specs, and 
        _descriptors into the class instance to prevent user from accidentally 
        adding to class-wide attributes if he overrides __init__ and forgets to
        call super().__init__ first. It is still possible for user to change 
        class-wide variables, but this will make it less likely he will do so 
        with an instance of the class. We do not use *args or **kwargs in 
        __new__ because developer may want to override behavior of arguments
        """
        self = super().__new__(cls)
        self.children = copy.deepcopy(self.children)
        self.attrib = copy.deepcopy(self.attrib)
        self._descriptors = copy.deepcopy(self._descriptors)
        self.specs = copy.deepcopy(self.specs)
        self._init_all_preconstructed_element_accessors()
        return self

    def _init_all_preconstructed_element_accessors(self):
        """ locate all preconstructed child access functions and run them.
        get back child access object, and set using same attribute name.
        things like self.nodes. Makes sure that this is a new, separate
        instance from the class itself. This feels computationally heavy tho...
        """
        for varName in dir(self):  # looking for a .nodes or .clouds function
            func = getattr(self, varName)  # idk why it's <class 'method'>
                                           # instead of <class 'function'>
            if type(func) == types.MethodType and func.__name__ == 'this_function_gets_automatically_run_inside_elements__new__':
                childAccessor = func()  # run function, get back child
                setattr(self, varName, childAccessor)      # access object

    def __init__(self, **kwargs):  # used to be: (self, attrib={}, **kwargs)  ## self.attrib.update(attribs)
        for k, v in kwargs.items():  # make this call different than updating attrib directly because it's more likely
            self.attrib[k] = v                       # that a developer specifically typed this out. This will error check it

    def findall(self, tag):
        """ Return all child elements with matching tag. Return all children if '*' passed in.

        :param tag: child element tag (ex. 'node', 'hook', 'map', etc.)
        :return: list of matching children. Return empty list if none found.
        """
        if tag == '*':
            return self.children
        matching = []
        for element in self.children:
            if element.tag == tag:
                matching.append(element)
        return matching

    def getchildren(self):
        return self.children


class Node(BaseElement):
    """ The most common element in a mindmap. The Node is the visual group, with an expandable branch of children.
    Nodes contain the text you type, contains links to other nodes or urls, contains pictures, and can be made visually
    unique through clouds, edge-line colors, or rich-text formatting. A Node contains an ID and text by default
    """
    tag = 'node'
    nodes = _elementAccess.ChildSubset.class_preconstructor(tag_regex=r'node')
    attrib = {'ID': 'random#', 'TEXT': ''}
    specs = {'BACKGROUND_COLOR': str, 'COLOR': str, 'FOLDED': bool, 'ID': str, 'LINK': str,
            'POSITION': ['left', 'right'], 'STYLE': str, 'TEXT': str, 'LOCALIZED_TEXT': str, 'TYPE': str,
            'CREATED': int, 'MODIFIED': int, 'HGAP': int, 'VGAP': int, 'VSHIFT': int,
            'ENCRYPTED_CONTENT': str, 'OBJECT': str, 'MIN_WIDTH': int, 'MAX_WIDTH': int}

    def __init__(self, **kwargs):
        self.attrib['ID'] = 'ID_' + str(uuid4().time).replace('L', '')
        super().__init__(**kwargs)

    def __str__(self):
        return self.tag + ': ' + self.attrib['TEXT'].replace('\n', '')

    def set_text(self, text):
        self['TEXT'] = text

    def get_text(self):
        return self['TEXT']<commit_msg>Node inherits from ImplicitNodeAttributes. implicit attributes are excel-like tables underneath a node<commit_after>from uuid import uuid4
import warnings
import re
import copy
from . import _elementAccess
import types
# see http://freeplane.sourceforge.net/wiki/index.php/Current_Freeplane_File_Format for file specifications
# terminology: elem, element = MindMap Elements (no etree elements allowed! Use a mmFactory to convert those


class ExampleElement:  # example element showing minimum of things to define to inherit from BaseElement and create one.
    tag = ''  # [REQUIRED]. Only way for a Factory (which you must code) to
        # convert the element of type 'tag'. Examples include 'node' or 'cloud'
    attrib = {}  # [OPTIONAL] pre-define default element attributes
    specs = {}  # [OPTIONAL] pre-define attribute types (like bool, str, int, float).
    _descriptors = []  # [OPTIONAL] list of attribs that are used when constructing string representation

   

class BaseElement:
    """ pymm's Base Element. All other elements inherit from BaseElement, which represents an element in a similar style
    to xml.etree.ElementTree. with enhancements aimed at providing faster mindmap manipulation. Each element has a
    specific identifier, a tag, that specifies what type of element it is. If a specific xml element type does not have
    a corresponding pymm element, it will become a BaseElement, but the corresponding "BaseElement" tag will be replaced
    with the actual xml element's tag.

    It stores all its children in a list that you can access and modify @ BaseElement.children.
    BaseElement stores xml attributes in a dictionary, which can be accessed as if the element sub-classed Dict.
    e.g.: BaseE['TEXT'] or BaseE['SIZE'] can access BaseElement's attributes TEXT and SIZE, respectively. xml
    attributes are those key=value declarations that exist within an xml-elements opening (<) and closing (>) tags.
    The functions items(), keys(), and update() are used to access BaseElement's attributes.

    :param tag: the tag specifying type of element
    :param parent: a link to the element's parent element
    :param specs: a dictionary of expected xml attributes and their expected types (i.e. int, list, string, etc.).When
                changing an element's attribute, the new attribute will be checked against the specs, and an warning
                generated if the new attribute does not match the specs. Can be modified to allow additional specs. I.E.
                elem['TEXT'] = 'HI 5' sets the element's TEXT attribute to 'HI 5'. If specs contains the key 'TEXT', the
                type of value 'HI 5' (str) will be checked against the value of specs['TEXT'], and allowed if they match
    """
    tag = 'BaseElement'  # must set to cloud, hook, edge, etc.
    parent = None
    _text = ''    # equivalent to ElementTree's .text  -- text between start tag and next element   # keep this for
    _tail = ''    # equivalent to ElementTree's .tail  -- text after end tag                        # .mm compatibility
    children = []  # all child elements including nodes
    attrib = {}  # pre-define these (outside of init like this) in other classes to define default element attribs
    _descriptors = []  # list of attribs that can be used to better describe instance. Used in str(self) construction
    specs = {}  # list all possible attributes of an element and valid entries / types in a list or standalone:
                    # [str, int, 'thin', etc.], str, int, 'thin', etc.

    def __new__(cls, *args, **kwargs):
        """ DO NOT OVERRIDE W/O super. override __init__ for most stuff. Copy
        all the mutable class attributes such as children, attrib, specs, and 
        _descriptors into the class instance to prevent user from accidentally 
        adding to class-wide attributes if he overrides __init__ and forgets to
        call super().__init__ first. It is still possible for user to change 
        class-wide variables, but this will make it less likely he will do so 
        with an instance of the class. We do not use *args or **kwargs in 
        __new__ because developer may want to override behavior of arguments
        """
        self = super().__new__(cls)
        self.children = copy.deepcopy(self.children)
        self.attrib = copy.deepcopy(self.attrib)
        self._descriptors = copy.deepcopy(self._descriptors)
        self.specs = copy.deepcopy(self.specs)
        self._init_all_preconstructed_element_accessors()
        return self

    def _init_all_preconstructed_element_accessors(self):
        """ locate all preconstructed child access functions and run them.
        get back child access object, and set using same attribute name.
        things like self.nodes. Makes sure that this is a new, separate
        instance from the class itself. This feels computationally heavy tho...
        """
        for varName in dir(self):  # looking for a .nodes or .clouds function
            func = getattr(self, varName)  # idk why it's <class 'method'>
                                           # instead of <class 'function'>
            if type(func) == types.MethodType and func.__name__ == 'this_function_gets_automatically_run_inside_elements__new__':
                childAccessor = func()  # run function, get back child
                setattr(self, varName, childAccessor)      # access object

    def __init__(self, **kwargs):  # used to be: (self, attrib={}, **kwargs)  ## self.attrib.update(attribs)
        for k, v in kwargs.items():  # make this call different than updating attrib directly because it's more likely
            self.attrib[k] = v                       # that a developer specifically typed this out. This will error check it

    def findall(self, tag):
        """ Return all child elements with matching tag. Return all children if '*' passed in.

        :param tag: child element tag (ex. 'node', 'hook', 'map', etc.)
        :return: list of matching children. Return empty list if none found.
        """
        if tag == '*':
            return self.children
        matching = []
        for element in self.children:
            if element.tag == tag:
                matching.append(element)
        return matching

    def getchildren(self):
        return self.children


class ImplicitNodeAttributes:
    """ attributes are excel like tables (2-cells wide each) that define a key-value pair
    they are attached (visually) beneath a Node in Freeplane. This class allows the user to define
    attributes on a node with implicit indexing like a dictionary BUT iteration is not allowed.
    Instead you should use the keys() or values() method to iterate over current attributes.
    """

    def __setitem__(self, key, val):
        self._attribute[key] = val

    def __getitem__(self, key, val):
        return self._attribute[key]

    def __iter__(self):
        raise NotImplemented('Cannot iterate node attributes. Use .values() or .keys()')

    def __delitem__(self, key):
        del self._attribute[key]

    def keys(self):
        return self._attribute.keys()

    def values(self):
        return self._attribute.values()


class Node(ImplicitNodeAttributes, BaseElement):
    """ The most common element in a mindmap. The Node is the visual group, with an expandable branch of children.
    Nodes contain the text you type, contains links to other nodes or urls, contains pictures, and can be made visually
    unique through clouds, edge-line colors, or rich-text formatting. A Node contains an ID and text by default
    """
    tag = 'node'
    nodes = _elementAccess.ChildSubset.class_preconstructor(tag_regex=r'node')
    attrib = {'ID': 'random#', 'TEXT': ''}
    # _attribute is node-specific, excel-like tables underneath a node that have key/value pairs
    _attribute = {}
    specs = {'BACKGROUND_COLOR': str, 'COLOR': str, 'FOLDED': bool, 'ID': str, 'LINK': str,
            'POSITION': ['left', 'right'], 'STYLE': str, 'TEXT': str, 'LOCALIZED_TEXT': str, 'TYPE': str,
            'CREATED': int, 'MODIFIED': int, 'HGAP': int, 'VGAP': int, 'VSHIFT': int,
            'ENCRYPTED_CONTENT': str, 'OBJECT': str, 'MIN_WIDTH': int, 'MAX_WIDTH': int}

    def __init__(self, **kwargs):
        self.attrib['ID'] = 'ID_' + str(uuid4().time).replace('L', '')
        super().__init__(**kwargs)

    def __str__(self):
        return self.tag + ': ' + self.attrib['TEXT'].replace('\n', '')

    def set_text(self, text):
        self['TEXT'] = text

    def get_text(self):
        return self['TEXT']
{'fraction_non_alphanumeric': 0.08784581908610507, 'fraction_numerical': 0.0006838669567920423, 'mean_word_length': 2.893030009680542, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 14}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1727327', 'n_tokens_mistral': 4284, 'n_tokens_neox': 4129, 'n_words': 1864}