StoryCheckTools/parse/StoryMap.py

433 lines
13 KiB
Python

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