def delete_or_update_node(self, node, new_node): ''' Delete or update a node from the XML DOM. If the node was shadowing an inherited node, the inherited node is (re-)inserted into the DOM (after merging with new_node in the case of an update) and returned. Calling delete_or_update_node on an inherited node has no effect. @node (Element) node to remove @new_node (Element) node to insert instead of the removed element; None to remove only @return the (re-inserted) node (Element) or None ''' # The three cases of deleting/updating a node: # The node is local (simplest case -- just remove it) # - if updating, simply add the new node # The node is inherited (no, wait, this is the simplest case -- do nothing) # The node is shadowing an inherited node (remove the node) # - if removing, reinsert the inherited node # - if updating, merge the new node with the inherited node and insert # helper function to clean out all child nodes from shadowing_nodes def clean_shadownodes(node): for child_node in node: clean_shadownodes(child_node) if node in self._shadowing_nodes: del self._shadowing_nodes[node] assert node not in self._shadowing_nodes parent = node.getparent() node_index = parent.index(node) inherited_node = None reinserted_node = new_node if node in self._shadowing_nodes: inherited_node = copy.deepcopy(self._shadowing_nodes[node]) if new_node is None: reinserted_node = inherited_node inherited_node = None elif node.get('inherited'): return else: pass clean_shadownodes(node) node_id = node_identity_string(node) parent.remove(node) if inherited_node is not None: assert new_node is not None XMLConfiguration._merge_nodes(inherited_node, new_node) if new_node is not None: self._add_shadowing_nodes(new_node, node_id) if reinserted_node is not None: parent.insert(node_index, reinserted_node) return reinserted_node
def delete_or_update_node(self, node, new_node): ''' Delete or update a node from the XML DOM. If the node was shadowing an inherited node, the inherited node is (re-)inserted into the DOM (after merging with new_node in the case of an update) and returned. Calling delete_or_update_node on an inherited node has no effect. @node (Element) node to remove @new_node (Element) node to insert instead of the removed element; None to remove only @return the (re-inserted) node (Element) or None ''' # The three cases of deleting/updating a node: # The node is local (simplest case -- just remove it) # - if updating, simply add the new node # The node is inherited (no, wait, this is the simplest case -- do nothing) # The node is shadowing an inherited node (remove the node) # - if removing, reinsert the inherited node # - if updating, merge the new node with the inherited node and insert # helper function to clean out all child nodes from shadowing_nodes def clean_shadownodes(node): for child_node in node: clean_shadownodes(child_node) if node in self._shadowing_nodes: del self._shadowing_nodes[node] assert node not in self._shadowing_nodes parent = node.getparent() node_index = parent.index(node) inherited_node = None reinserted_node = new_node if node in self._shadowing_nodes: inherited_node = copy.deepcopy(self._shadowing_nodes[node]) if new_node is None: reinserted_node = inherited_node inherited_node = None elif node.get('inherited'): return else: pass clean_shadownodes(node) node_id = node_identity_string(node) parent.remove(node) if inherited_node is not None: assert new_node is not None XMLConfiguration._merge_nodes(inherited_node, new_node) if new_node is not None: self._add_shadowing_nodes(new_node, node_id) if reinserted_node is not None: parent.insert(node_index, reinserted_node) return reinserted_node
def copy_to_parent(self, node): ''' Copies a local node to the parent configuration without deleting it. @node (Element) to copy to parent ''' # Helper routines: # Never copy description and parent nodes to parent def delete_immutable(): id_strings = self.IMMUTABLE_NODE_IDS for id_string in id_strings: for n in self.find_all_by_id_string(id_string, clone): n.getparent().remove(n) # Find deepest parent node of to-be-inserted node that is also in the parent config def get_insert_node(merge_node, nodes): nodes.append(merge_node) ins_node = self.find_by_id_string(node_identity_string(merge_node), parent_root) if ins_node is not None: return ins_node merge_node = merge_node.getparent() return get_insert_node(merge_node, nodes) # Remove all children for all nodes in the nodes list def strip_children(nodes): for n in nodes[:-1]: for subelement in n.getparent().getchildren(): if subelement is not n: n.getparent().remove(subelement) # Remove "inherited" attribute from all nodes below tree_node that were # introduced by the immediate parent. Remove all inherited nodes # that were introduced by a grandparent -- copying them to the parent # leads to incorrect results. def clear_inherited_attribute_or_delete_inherited_nodes(tree_node): nodes_to_delete = [] for node in tree_node.iterchildren(): if node.get('inherited') is not None: if node.get('inherited') == parent_name: del node.attrib['inherited'] clear_inherited_attribute_or_delete_inherited_nodes( node) else: nodes_to_delete.append(node) else: clear_inherited_attribute_or_delete_inherited_nodes(node) for node in nodes_to_delete: tree_node.remove(node) #work on clone_node id_string = node_identity_string(node) clone = copy.deepcopy(self.root_node()) node = self.find_by_id_string(id_string, clone) #get parent project parent_file = self.get_last_writable_parent_file() parent_project = OpusProject() parent_project.open(parent_file) parent_name = parent_project.xml_config.name parent_root = parent_project.xml_config.tree.getroot() delete_immutable() parents_to_insert = [] if node is not clone: insert_node = get_insert_node(node, parents_to_insert) node = parents_to_insert[-1] strip_children(parents_to_insert) clear_inherited_attribute_or_delete_inherited_nodes(node) XMLConfiguration._merge_nodes(insert_node, node) insert_parent = insert_node.getparent() insert_parent.replace(insert_node, node) # using parent_project.save() adds unnecessary attributes for some reason. parent_project.xml_config.save_as(parent_file)
def copy_to_parent(self, node): ''' Copies a local node to the parent configuration without deleting it. @node (Element) to copy to parent ''' # Helper routines: # Never copy description and parent nodes to parent def delete_immutable(): id_strings = self.IMMUTABLE_NODE_IDS for id_string in id_strings: for n in self.find_all_by_id_string(id_string, clone): n.getparent().remove(n) # Find deepest parent node of to-be-inserted node that is also in the parent config def get_insert_node(merge_node, nodes): nodes.append(merge_node) ins_node = self.find_by_id_string(node_identity_string(merge_node), parent_root) if ins_node is not None: return ins_node merge_node = merge_node.getparent() return get_insert_node(merge_node, nodes) # Remove all children for all nodes in the nodes list def strip_children(nodes): for n in nodes[:-1]: for subelement in n.getparent().getchildren(): if subelement is not n: n.getparent().remove(subelement) # Remove "inherited" attribute from all nodes below tree_node that were # introduced by the immediate parent. Remove all inherited nodes # that were introduced by a grandparent -- copying them to the parent # leads to incorrect results. def clear_inherited_attribute_or_delete_inherited_nodes(tree_node): nodes_to_delete = [] for node in tree_node.iterchildren(): if node.get('inherited') is not None: if node.get('inherited') == parent_name: del node.attrib['inherited'] clear_inherited_attribute_or_delete_inherited_nodes(node) else: nodes_to_delete.append(node) else: clear_inherited_attribute_or_delete_inherited_nodes(node) for node in nodes_to_delete: tree_node.remove(node) #work on clone_node id_string = node_identity_string(node) clone = copy.deepcopy(self.root_node()) node = self.find_by_id_string(id_string, clone) #get parent project parent_file = self.get_last_writable_parent_file() parent_project = OpusProject() parent_project.open(parent_file) parent_name = parent_project.xml_config.name parent_root = parent_project.xml_config.tree.getroot() delete_immutable() parents_to_insert = [] if node is not clone: insert_node = get_insert_node(node, parents_to_insert) node = parents_to_insert[-1] strip_children(parents_to_insert) clear_inherited_attribute_or_delete_inherited_nodes(node) XMLConfiguration._merge_nodes(insert_node, node) insert_parent = insert_node.getparent() insert_parent.replace(insert_node, node) # using parent_project.save() adds unnecessary attributes for some reason. parent_project.xml_config.save_as(parent_file)