Esempio n. 1
0
    def _read_xml_from_config(self, xml_config):
        ''' Extract XML information from the XML Configuration object '''
        self.xml_config = xml_config
        self._root_node = xml_config.full_tree.getroot()

        if xml_config.inherited_tree is None:
            self._inherited_root = XMLConfiguration().full_tree.getroot()
        else:
            self._inherited_root = xml_config.inherited_tree
        # map id's to nodes for the inherited and the local nodes
        inherited_ids_to_nodes = dict(
            (node_identity_string(n), n)
            for n in self._inherited_root.getiterator())
        local_ids_to_nodes = dict((node_identity_string(n), n)
                                  for n in self._root_node.getiterator()
                                  if not n.get('inherited'))

        self._shadowing_nodes = {}
        # join the local and inherited nodes on id-match
        for id_, node in local_ids_to_nodes.items():
            if id_ in inherited_ids_to_nodes:
                self._shadowing_nodes[node] = inherited_ids_to_nodes[id_]

        if self.find('./general/project_name') is not None:
            self.name = self.find('./general/project_name').text
        else:
            self.name = 'unnamed_project'
        os.environ['OPUSPROJECTNAME'] = self.name
        self.dirty = False
    def _read_xml_from_config(self, xml_config):
        ''' Extract XML information from the XML Configuration object '''
        self.xml_config = xml_config
        self._root_node = xml_config.full_tree.getroot()

        if xml_config.inherited_tree is None:
            self._inherited_root = XMLConfiguration().full_tree.getroot()
        else:
            self._inherited_root = xml_config.inherited_tree
        # map id's to nodes for the inherited and the local nodes
        inherited_ids_to_nodes = dict((node_identity_string(n), n) for
                                      n in self._inherited_root.getiterator())
        local_ids_to_nodes = dict((node_identity_string(n), n) for
                                  n in self._root_node.getiterator() if not n.get('inherited'))

        self._shadowing_nodes = {}
        # join the local and inherited nodes on id-match
        for id_, node in local_ids_to_nodes.items():
            if id_ in inherited_ids_to_nodes:
                self._shadowing_nodes[node] = inherited_ids_to_nodes[id_]

        if self.find('./general/project_name') is not None:
            self.name = self.find('./general/project_name').text
        else:
            self.name = 'unnamed_project'
        os.environ['OPUSPROJECTNAME'] = self.name
        self.dirty = False
Esempio n. 3
0
 def _add_shadowing_nodes(self, root_node, root_node_id):
     # map id's to nodes for the inherited and the local nodes
     inherited_ids_to_nodes = dict((node_identity_string(n), n) for n in self._inherited_root.getiterator())
     local_ids_to_nodes = dict((root_node_id + node_identity_string(n), n) for 
         n in root_node.getiterator() if not n.get('inherited'))
     # join the local and inherited nodes on id-match
     for id_, node in local_ids_to_nodes.items():
         if id_ in inherited_ids_to_nodes:
             shadowing_node = inherited_ids_to_nodes[id_]
             assert node.tag == shadowing_node.tag
             self._shadowing_nodes[node] = shadowing_node
Esempio n. 4
0
 def _add_shadowing_nodes(self, root_node, root_node_id):
     # map id's to nodes for the inherited and the local nodes
     inherited_ids_to_nodes = dict(
         (node_identity_string(n), n)
         for n in self._inherited_root.getiterator())
     local_ids_to_nodes = dict((root_node_id + node_identity_string(n), n)
                               for n in root_node.getiterator()
                               if not n.get('inherited'))
     # join the local and inherited nodes on id-match
     for id_, node in local_ids_to_nodes.items():
         if id_ in inherited_ids_to_nodes:
             shadowing_node = inherited_ids_to_nodes[id_]
             assert node.tag == shadowing_node.tag
             self._shadowing_nodes[node] = shadowing_node
Esempio n. 5
0
 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)
Esempio n. 6
0
 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)
Esempio n. 7
0
 def can_copy_to_parent(self, node):
     if not self.get_last_writable_parent_file():
         return False
     if node.get('inherited'):
         return False
     if node_identity_string(node) in self.IMMUTABLE_NODE_IDS:
         return False
     return True
Esempio n. 8
0
 def can_copy_to_parent(self, node):
     if not self.get_last_writable_parent_file():
         return False
     if node.get('inherited'):
         return False
     if node_identity_string(node) in self.IMMUTABLE_NODE_IDS:
         return False
     return True
Esempio n. 9
0
 def _add_to_shadowing_nodes(self, node):
     '''
     Convenience method to add a node to the shadow map with error checking
     @param node (Element) node to add to shadow map
     '''
     id_ = node_identity_string(node)
     inherited_node = self.find_by_id_string(id_, self._inherited_root)
     if inherited_node is not None:
         self._shadowing_nodes[node] = inherited_node
Esempio n. 10
0
 def _add_to_shadowing_nodes(self, node):
     '''
     Convenience method to add a node to the shadow map with error checking
     @param node (Element) node to add to shadow map
     '''
     id_ = node_identity_string(node)
     inherited_node = self.find_by_id_string(id_, self._inherited_root)
     if inherited_node is not None:
         self._shadowing_nodes[node] = inherited_node
Esempio n. 11
0
    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
Esempio n. 12
0
    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 merge_templated_nodes_with_project(templated_nodes, new_project):
    ''' Merge a set of user configured template nodes into a new project.
    
    @param templated_nodes ( [xml_node:Element, ...] ) - list of nodes to merge
    @param new_project (OpusProject) the project in which new nodes are created
    
    Created nodes in the new_project are guaranteed to have the same id-path as in the parent tree of the 
    templated node and thus will overwrite any parents configuration in those places. 
    When the node path doesn't exist in the new project, it is created with empty nodes all the way down to 
    the templated node. This causes the new project to inherit attributes and values from the parent project
    when loaded.
     
    '''
    
    for templated_node in templated_nodes:
        node_id_string = node_identity_string(templated_node)
        
        # try to fetch the existing node in the new project
        node_to_edit = new_project.find_by_id_string(node_id_string)
       
        # create it if it isn't there
        if node_to_edit is None:
            # get a list of all the nodes between the project root and the templated node. This list can then
            # be traversed to figure out which nodes that we need to create in the new project in order for 
            # an inserted node to have the same id-path in the templated project and in the new project 
            nodes_to_parent = []
            walker_node = templated_node.getparent()
            while walker_node.getparent() is not None: # walker.parent == None -> walker is project root
                nodes_to_parent.append(walker_node)
                walker_node = walker_node.getparent()
            del walker_node
            
            # nodes_to_parent.reverse() # we want to traverse from closest parent to furthest
            
            # fetches a reference to the created or existing parent node that we want to attach to            
            node_to_edit_parent_node = _create_or_get_parent(nodes_to_parent, new_project)
            
            # attach the node to the parent
            node_to_edit = SubElement(node_to_edit_parent_node, 
                                       templated_node.tag, 
                                       {'name': templated_node.get('name') or ''})
        
        # copy the text from the templated node to the node in the new project
        node_to_edit.text = templated_node.text
def merge_templated_nodes_with_project(templated_nodes, new_project):
    ''' Merge a set of user configured template nodes into a new project.
    
    @param templated_nodes ( [xml_node:Element, ...] ) - list of nodes to merge
    @param new_project (OpusProject) the project in which new nodes are created
    
    Created nodes in the new_project are guaranteed to have the same id-path as in the parent tree of the 
    templated node and thus will overwrite any parents configuration in those places. 
    When the node path doesn't exist in the new project, it is created with empty nodes all the way down to 
    the templated node. This causes the new project to inherit attributes and values from the parent project
    when loaded.
     
    '''
    
    for templated_node in templated_nodes:
        node_id_string = node_identity_string(templated_node)
        
        # try to fetch the existing node in the new project
        node_to_edit = new_project.find_by_id_string(node_id_string)
       
        # create it if it isn't there
        if node_to_edit is None:
            # get a list of all the nodes between the project root and the templated node. This list can then
            # be traversed to figure out which nodes that we need to create in the new project in order for 
            # an inserted node to have the same id-path in the templated project and in the new project 
            nodes_to_parent = []
            walker_node = templated_node.getparent()
            while walker_node.getparent() is not None: # walker.parent == None -> walker is project root
                nodes_to_parent.append(walker_node)
                walker_node = walker_node.getparent()
            del walker_node
            
            # nodes_to_parent.reverse() # we want to traverse from closest parent to furthest
            
            # fetches a reference to the created or existing parent node that we want to attach to            
            node_to_edit_parent_node = _create_or_get_parent(nodes_to_parent, new_project)
            
            # attach the node to the parent
            node_to_edit = SubElement(node_to_edit_parent_node, 
                                       templated_node.tag, 
                                       {'name': templated_node.get('name') or ''})
        
        # copy the text from the templated node to the node in the new project
        node_to_edit.text = templated_node.text
def _create_or_get_parent(nodes_to_parent, new_project):
    '''helper method to create a node path in the new project like the one of the templated node'''
    if nodes_to_parent == []: # reached the project root
        return new_project.root_node()
    
    # check if the new project contains a node at the current position
    current_node_id_string = node_identity_string(nodes_to_parent[0])
    current_node = new_project.find_by_id_string(current_node_id_string) 
    if current_node is not None:
        return current_node # this was an existing node so we can build down from here
    
    # the current_node doesn't existing in the new project. Extract the missing node's tag and name from the 
    # templated node, get the (created or existing) parent node, and create the current node with the missing
    # tag and name
    missing_node = nodes_to_parent.pop(0) # consume the closest node
    missing_node_tag, missing_node_name = missing_node.tag, missing_node.get('name', None)
    parent_node = _create_or_get_parent(nodes_to_parent, new_project) # continue up the tree
    if missing_node_name is not None:
        current_node = SubElement(parent_node, missing_node_tag, {'name': missing_node_name})
    else: # parent node did not have a name attribute so this node shouldn't either
        current_node = SubElement(parent_node, missing_node_tag)
    return current_node
def _create_or_get_parent(nodes_to_parent, new_project):
    '''helper method to create a node path in the new project like the one of the templated node'''
    if nodes_to_parent == []: # reached the project root
        return new_project.root_node()
    
    # check if the new project contains a node at the current position
    current_node_id_string = node_identity_string(nodes_to_parent[0])
    current_node = new_project.find_by_id_string(current_node_id_string) 
    if current_node is not None:
        return current_node # this was an existing node so we can build down from here
    
    # the current_node doesn't existing in the new project. Extract the missing node's tag and name from the 
    # templated node, get the (created or existing) parent node, and create the current node with the missing
    # tag and name
    missing_node = nodes_to_parent.pop(0) # consume the closest node
    missing_node_tag, missing_node_name = missing_node.tag, missing_node.get('name', None)
    parent_node = _create_or_get_parent(nodes_to_parent, new_project) # continue up the tree
    if missing_node_name is not None:
        current_node = SubElement(parent_node, missing_node_tag, {'name': missing_node_name})
    else: # parent node did not have a name attribute so this node shouldn't either
        current_node = SubElement(parent_node, missing_node_tag)
    return current_node
Esempio n. 17
0
 def insert_node(self, node, parent_node, row = 0):
     '''
     Insert a node into the XML DOM tree. Can only insert nodes that are not already in the
     tree (identified by their id string).
     @param node (Element) node to insert (all child nodes are also inserted)
     @param parent_node (Element) the parent to attach the node to
     @param row (int) insert the new node as row:th child of parent_node
     @return the inserted node (Element) or None if the node was not inserted (already exists)
     '''
     # check if there are any nodes with this nodes id string
     future_id = node_identity_string(parent_node) + element_id(node)
     try:
         existing_node = self.find_by_id_string(future_id, parent_node.getroottree().getroot())
     except LookupError: # nodes with this id already exists
         print "LookupError"
         return None
     if existing_node is not None:
         return None # don't allow overwriting existing nodes
     # Insert the node
     parent_node.insert(row, node)
     self.make_local(node)
     return node
Esempio n. 18
0
 def insert_node(self, node, parent_node, row=0):
     '''
     Insert a node into the XML DOM tree. Can only insert nodes that are not already in the
     tree (identified by their id string).
     @param node (Element) node to insert (all child nodes are also inserted)
     @param parent_node (Element) the parent to attach the node to
     @param row (int) insert the new node as row:th child of parent_node
     @return the inserted node (Element) or None if the node was not inserted (already exists)
     '''
     # check if there are any nodes with this nodes id string
     future_id = node_identity_string(parent_node) + element_id(node)
     try:
         existing_node = self.find_by_id_string(
             future_id,
             parent_node.getroottree().getroot())
     except LookupError:  # nodes with this id already exists
         print "LookupError"
         return None
     if existing_node is not None:
         return None  # don't allow overwriting existing nodes
     # Insert the node
     parent_node.insert(row, node)
     self.make_local(node)
     return node
Esempio n. 19
0
    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)
Esempio n. 20
0
    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)