433 lines
13 KiB
Python
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
|