# Purpose: database module # Created: 11.03.2011 # Copyright (c) 2011-2018, Manfred Moitzi # License: MIT License from typing import Optional, Iterable, Tuple from ezdxf.tools.handle import HandleGenerator from ezdxf.lldxf.const import DXFValueError from ezdxf.lldxf.tags import DXFTag from ezdxf.lldxf.extendedtags import ExtendedTags from ezdxf.dxfentity import DXFEntity class EntityDB: """ A simple key/value database a.k.a. dict(), but can be replaced by other classes that implements all of the methods of `EntityDB`. The entities have no order. The Data Model Every entity/object, except tables and sections, are represented as tag-list (see ExtendedTags Class), this lists are stored in the drawing-associated database, database-key is the 'handle' tag (code == 5 or 105). For the entity/object manipulation this tag-list will be wrapped into separated classes, which are generated by the dxffactory-object. The dxffactory-object generates DXF-Version specific wrapper classes. """ def __init__(self): self._database = {} self.handles = HandleGenerator() def __delitem__(self, handle: str) -> None: del self._database[handle] def __getitem__(self, handle: str) -> ExtendedTags: return self._database[handle] def get(self, handle: str) -> Optional[ExtendedTags]: try: return self.__getitem__(handle) except KeyError: # internal exception return None def __setitem__(self, handle: str, tags: ExtendedTags) -> None: self._database[handle] = tags def __contains__(self, handle: str) -> bool: """ Database contains handle? """ return handle in self._database def __len__(self) -> int: """ Count of database items. """ return len(self._database) def __iter__(self) -> Iterable[str]: """ Iterate over all handles. """ return iter(self._database.keys()) def keys(self) -> Iterable[str]: """ Iterate over all handles. """ return self._database.keys() def values(self) -> Iterable[ExtendedTags]: """ Iterate over all entities. """ return self._database.values() def items(self) -> Iterable[Tuple[str, ExtendedTags]]: """ Iterate over all (handle, entities) pairs. """ return self._database.items() def add_tags(self, tags: ExtendedTags) -> str: try: handle = tags.get_handle() except DXFValueError: # create new handle handle = self.get_unique_handle() handle_code = 105 if tags.dxftype() == 'DIMSTYLE' else 5 # legacy shit!!! tags.noclass.insert(1, DXFTag(handle_code, handle)) # handle should be the 2. tag self.__setitem__(handle, tags) return handle def delete_entity(self, entity: DXFEntity) -> None: entity.destroy() self.delete_handle(entity.dxf.handle) def delete_handle(self, handle: str) -> None: del self._database[handle] def get_unique_handle(self)-> str: while True: handle = self.handles.next() if handle not in self._database: # you can not trust $HANDSEED value return handle def duplicate_tags(self, tags: ExtendedTags) -> ExtendedTags: """ Deep copy of tags with new handle and duplicated linked entities (VERTEX, ATTRIB, SEQEND) with also new handles. An existing owner tag is not changed because this is not the domain of the EntityDB() class. The new entity tags are added to the drawing database. This is not a deep copy in the meaning of Python, because handle and link is changed. """ new_tags = tags.clone() new_tags.noclass.replace_handle(self.get_unique_handle()) # set new handle self.add_tags(new_tags) # add new tags to database source_link = tags.link # follow link structure of original entity parent_copy = new_tags while source_link is not None: # duplicate linked entities (VERTEX, ATTRIB, SEQEND) source_linked_entity = self.get(source_link) # extended tags linked_entity_copy = source_linked_entity.clone() new_handle = self.get_unique_handle() linked_entity_copy.noclass.replace_handle(new_handle) # set new handle self.add_tags(linked_entity_copy) # add new tags to database parent_copy.link = new_handle source_link = source_linked_entity.link # follow link structure of original entity parent_copy = linked_entity_copy return new_tags