import xml.dom.minidom as mdom from typing import List, Tuple, Dict, Union from abc import abstractmethod from enum import Enum from frame.ReferView import EmptyNode class SliceType(Enum): StoryDefine = 0, TextParagraph = 1, FragmentDefine = 2, FragmentRefer = 3, VolumeDefine = 4, ArticleDefine = 5, class ElementSlice: @abstractmethod def type(self) -> SliceType: raise NotImplementedError("type") @abstractmethod def parent_slice(self) -> Union['ElementSlice', None]: raise NotImplementedError() @abstractmethod def set_prev(self, inst: Union['ElementSlice', None]): raise NotImplementedError() @abstractmethod def prev_sibling(self) -> Union['ElementSlice', None]: raise NotImplementedError() @abstractmethod def set_next(self, inst: Union['ElementSlice', None]): raise NotImplementedError() @abstractmethod def next_sibling(self) -> Union['ElementSlice', None]: raise NotImplementedError() @abstractmethod def index(self) -> int: raise NotImplementedError() class Collection: @abstractmethod def children(self) -> List[ElementSlice]: raise NotImplementedError() class SiblingsImpl(ElementSlice): def __init__(self): self.__prev_sib: Union['StoryDefine', None] = None self.__next_sib: Union['StoryDefine', None] = None pass def parent_slice(self) -> Union['ElementSlice', None]: pass def set_prev(self, inst: Union['ElementSlice', None]): pass def prev_sibling(self) -> Union['ElementSlice', None]: pass def set_next(self, inst: Union['ElementSlice', None]): pass def next_sibling(self) -> Union['ElementSlice', None]: pass def index(self) -> int: pass class StoryDefine(ElementSlice, Collection): def __init__(self, nm: str, idx: int): self.__index = idx self.__name = nm self.__prev_sib: Union['StoryDefine', None] = None self.__next_sib: Union['StoryDefine', None] = None self.__children: List[ElementSlice] = [] pass def type(self) -> SliceType: return SliceType.StoryDefine def parent_slice(self) -> Union['ElementSlice', None]: return None def set_prev(self, inst: Union['ElementSlice', None]): self.__prev_sib = inst pass def prev_sibling(self) -> Union['ElementSlice', None]: return self.__prev_sib pass def set_next(self, inst: Union['ElementSlice', None]): self.__next_sib = inst pass def next_sibling(self) -> Union['ElementSlice', None]: return self.__next_sib pass def index(self) -> int: return self.__index pass def children(self) -> List[ElementSlice]: return self.__children def name(self) -> str: return self.__name def calc_index(inst: ElementSlice) -> int: temp_refer = inst.prev_sibling() index_v = 0 while temp_refer is not None: index_v += 1 temp_refer = temp_refer.prev_sibling() pass return index_v class TextParagraph(ElementSlice): def __init__(self, parent: ElementSlice): self.__parent_node = parent self.__prev_node: Union[ElementSlice, None] = None self.__next_node: Union[ElementSlice, None] = None self.__text_lines: List[str] = [] pass def type(self) -> SliceType: return SliceType.TextParagraph def parent_slice(self) -> Union['ElementSlice', None]: return self.__parent_node def set_prev(self, inst: Union['ElementSlice', None]): self.__prev_node = inst pass def prev_sibling(self) -> Union['ElementSlice', None]: return self.__prev_node def set_next(self, inst: Union['ElementSlice', None]): self.__next_node = inst inst.set_prev(self) pass def next_sibling(self) -> Union['ElementSlice', None]: return self.__next_node def index(self) -> int: return calc_index(self) class FragmentDefine(ElementSlice): def __init__(self, nm:str, parent: ElementSlice): self.__name = nm self.__parent_inst = parent self.__prev_node: Union[ElementSlice, None] = None self.__next_node: Union[ElementSlice, None] = None self.__refer_nodes: List[ElementSlice] = [] self.__description: TextParagraph = TextParagraph(self) pass def type(self) -> SliceType: return SliceType.FragmentDefine def parent_slice(self) -> Union['ElementSlice', None]: return self.__parent_inst def set_prev(self, inst: Union['ElementSlice', None]): self.__prev_node = inst pass def prev_sibling(self) -> Union['ElementSlice', None]: return self.__prev_node def set_next(self, inst: Union['ElementSlice', None]): self.__next_node = inst inst.set_prev(self) pass def next_sibling(self) -> Union['ElementSlice', None]: return self.__next_node def index(self) -> int: return calc_index(self) def name(self) -> str: return self.__name def refer_slices(self) -> List[ElementSlice]: return self.__refer_nodes def refer_append(self, e:ElementSlice): return self.__refer_nodes.append(e) def text_description(self) -> TextParagraph: return self.__description class FragmentRefer(ElementSlice): def __init__(self, parent: ElementSlice): pass class FragmentSlice(EmptyNode): def __init__(self, def_mark: bool = False, name: str = ""): self.is_define_node = (def_mark, name) self.parent_story = "" self.story_refer = "" self.fragm_refer = "" self.fragm_sort_i = 0 self.prev_node: Union['FragmentSlice', None] = None # 上游节点 self.next_node: Union['FragmentSlice', None] = None # 下游节点 self.refers_slice: Dict[str, 'FragmentSlice'] = {} # 引用切面 self.text_sections: List[str] = [] # 文本段落 pass def get_from_memory(self) -> str: return "\n".join(self.text_sections) pass def set_to_memory(self, content: str): self.text_sections = content.split("\n") pass class ArticleSlice(EmptyNode): def __init__(self, a_name: str, v_name: str): self.article_name = a_name self.volume_blongs = v_name self.article_fullname = f"{v_name}@{a_name}" self.refer_target: List[str] = [] # 引用切面 self.text_sections: List[str] = [] # 文本段落 pass def get_from_memory(self) -> str: return "\n".join(self.text_sections) def set_to_memory(self, content: str): self.text_sections = content.split("\n") pass class StoryMap: def __init__(self, name: str): self.story_name = name self.sort_index = 0 self.empty_head = FragmentSlice() self.empty_head.set_to_memory("<空节点>") self.slice_list: List[FragmentSlice] = [self.empty_head] pass def append_fragment_slice(self, node: FragmentSlice): self.slice_list[-1].next_node = node node.parent_story = self.story_name node.prev_node = self.slice_list[-1] node.fragm_sort_i = len(self.slice_list) self.slice_list.append(node) pass def get_fragment_defined(self, name: str) -> Union[FragmentSlice, None]: for fit in self.slice_list: if fit.is_define_node[0]: if fit.is_define_node[1] == name: return fit pass pass return None class XAST_ParseTool: def __init__(self, ast_path: str): self.dom_tree = mdom.parse(ast_path) self.story_list: List[StoryMap] = [] names = self.story_names() for nm in names: vinst = self.__extract_empty_storymap(nm) self.__storynode_initfrags(vinst[0], vinst[1]) self.story_list.append(vinst[0]) pass def story_names(self) -> List[str]: """ 获取所有故事名称 :return: List[故事名] """ storys = self.dom_tree.getElementsByTagName("story") values = [] for n in storys: values.append(n.getAttribute("name")) pass return values def __extract_empty_storymap(self, name: str) -> Union[Tuple[StoryMap, mdom.Element], None]: storys = self.dom_tree.getElementsByTagName("story") for node in storys: if node.getAttribute("name") == name: mem_node = StoryMap(name) mem_node.sort_index = int(node.getAttribute("sort")) return mem_node, node pass return None def __storynode_initfrags(self, node: StoryMap, e: mdom.Element): first_elem: mdom.Element = e.firstChild while first_elem is not None: if hasattr(first_elem, "tagName"): dom_tag = first_elem.tagName if first_elem.tagName == "fragment": fnode = FragmentSlice(True, first_elem.getAttribute("name")) self.__fragmnode_init_desc(fnode, first_elem) node.append_fragment_slice(fnode) elif first_elem.tagName == "refer": fnode = FragmentSlice() fnode.story_refer = first_elem.getAttribute("story") fnode.fragm_refer = first_elem.getAttribute("fragment") self.__fragmnode_init_desc(fnode, first_elem) node.append_fragment_slice(fnode) pass pass first_elem = first_elem.nextSibling pass def __fragmnode_init_desc(self, node: FragmentSlice, e: mdom.Element): node_elem: mdom.Element = e.firstChild while node_elem is not None: if hasattr(node_elem, "tagName") and node_elem.tagName == "text-section": line = node_elem.getAttribute("text") node.text_sections.append(line) pass node_elem = node_elem.nextSibling pass pass def storylines_plait(self, storys: Dict[str, StoryMap]): for snode in storys.values(): self.__storyline_plait_within(snode, storys) pass def __storyline_plait_within(self, story: StoryMap, storys_map: Dict[str, StoryMap]): for fnode in story.slice_list[1:]: if not fnode.is_define_node[0]: sname = fnode.story_refer fname = fnode.fragm_refer target_story = storys_map[sname] target_fragment = target_story.get_fragment_defined(fname) target_fragment.refers_slice[story.story_name] = fnode pass pass pass def get_story_graph(self) -> Dict[str, StoryMap]: story_dict = storyline_list2map(self.story_list) self.storylines_plait(story_dict) return story_dict def get_article_nodes(self) -> List[ArticleSlice]: retvalues = [] hangout_nodes = ArticleSlice("悬空节点", "") retvalues.append(hangout_nodes) fragments = self.__get_all_fragment_names() volumes = self.dom_tree.getElementsByTagName("volume") for vnode in volumes: child_articles = self.__volume_node_parse(vnode) retvalues.extend(child_articles) for child in child_articles: for refn in child.refer_target: if refn in fragments: fragments.remove(refn) pass pass pass pass hangout_nodes.refer_target.extend(fragments) return retvalues def __get_all_fragment_names(self) -> List[str]: values = [] frags = self.dom_tree.getElementsByTagName("fragment") for frag in frags: story: mdom.Element = frag.parentNode values.append(f"{story.getAttribute("name")}&{frag.getAttribute("name")}") pass return values def __volume_node_parse(self, vnode: mdom.Element) -> List[ArticleSlice]: retvalues = [] vname = vnode.getAttribute("name") articles = vnode.getElementsByTagName("article") for anode in articles: aname = anode.getAttribute("name") node_inst = ArticleSlice(aname, vname) retvalues.append(node_inst) refsnode = anode.getElementsByTagName("refer") for refnode in refsnode: ref_story = refnode.getAttribute("story") ref_fragment = refnode.getAttribute("fragment") node_inst.refer_target.append(f"{ref_story}&{ref_fragment}") pass pass return retvalues pass def storyline_list2map(storylines: List[StoryMap]) -> Dict[str, StoryMap]: retv = {} for linst in storylines: retv[linst.story_name] = linst pass return retv