Пример #1
0
 def __init__(self, type, level=1):
     # xml type being parsed
     self.type = type
     # indent level
     self.level = level
     # dict of names-tuples
     self.attrnames = {}
     # sets of (tag, id) preserve order to avoid dangling references during loading
     self.ids_deleted = OrderedMultiSet()
     self.ids_created = OrderedMultiSet()
     # dict from (tag, id) to (names, values, children)
     self.id_attrs = {}
     # dict from tag to (names, values)-sets, need to preserve order (CAVEAT5)
     self.idless_deleted = defaultdict(lambda:OrderedMultiSet())
     self.idless_created = defaultdict(lambda:OrderedMultiSet())
Пример #2
0
 def __init__(self, type, copy_tags, level=1):
     # xml type being parsed
     self.type = type
     # tag names to copy even if unchanged
     self.copy_tags = copy_tags
     # indent level
     self.level = level
     # dict of names-tuples
     self.attrnames = {}
     # sets of (tag, id) preserve order to avoid dangling references during
     # loading
     self.ids_deleted = OrderedMultiSet()
     self.ids_created = OrderedMultiSet()
     self.ids_copied = OrderedMultiSet()
     # dict from (tag, id) to (names, values, children)
     self.id_attrs = {}
     # dict from tag to (names, values)-sets, need to preserve order
     # (CAVEAT5)
     self.idless_deleted = defaultdict(OrderedMultiSet)
     self.idless_created = defaultdict(OrderedMultiSet)
     self.idless_copied = defaultdict(OrderedMultiSet)
Пример #3
0
 def __init__(self, type, copy_tags, level=1):
     # xml type being parsed
     self.type = type
     # tag names to copy even if unchanged
     self.copy_tags = copy_tags
     # indent level
     self.level = level
     # dict of names-tuples
     self.attrnames = {}
     # sets of (tag, id) preserve order to avoid dangling references during loading
     self.ids_deleted = OrderedMultiSet()
     self.ids_created = OrderedMultiSet()
     self.ids_copied = OrderedMultiSet()
     # dict from (tag, id) to (names, values, children)
     self.id_attrs = {}
     # dict from tag to (names, values)-sets, need to preserve order (CAVEAT5)
     self.idless_deleted = defaultdict(lambda: OrderedMultiSet())
     self.idless_created = defaultdict(lambda: OrderedMultiSet())
     self.idless_copied = defaultdict(lambda: OrderedMultiSet())
Пример #4
0
class AttributeStore:
    def __init__(self, type, copy_tags, level=1):
        # xml type being parsed
        self.type = type
        # tag names to copy even if unchanged
        self.copy_tags = copy_tags
        # indent level
        self.level = level
        # dict of names-tuples
        self.attrnames = {}
        # sets of (tag, id) preserve order to avoid dangling references during
        # loading
        self.ids_deleted = OrderedMultiSet()
        self.ids_created = OrderedMultiSet()
        self.ids_copied = OrderedMultiSet()
        # dict from (tag, id) to (names, values, children)
        self.id_attrs = {}
        # dict from tag to (names, values)-sets, need to preserve order
        # (CAVEAT5)
        self.idless_deleted = defaultdict(lambda: OrderedMultiSet())
        self.idless_created = defaultdict(lambda: OrderedMultiSet())
        self.idless_copied = defaultdict(lambda: OrderedMultiSet())

    # getAttribute returns "" if not present
    def getValue(self, node, name):
        if node.hasAttribute(name):
            return node.getAttribute(name)
        else:
            return None

    def getNames(self, xmlnode):
        idattrs = IDATTRS[xmlnode.localName]
        a = xmlnode.attributes
        all = [a.item(i).localName for i in range(a.length)]
        instance = tuple([n for n in all if n not in idattrs])
        if not instance in self.attrnames:
            self.attrnames[instance] = instance
        # only store a single instance of this tuple to conserve memory
        return self.attrnames[instance]

    def getAttrs(self, xmlnode):
        names = self.getNames(xmlnode)
        values = tuple([self.getValue(xmlnode, a) for a in names])
        children = None
        if any([c.nodeType == Node.ELEMENT_NODE for c in xmlnode.childNodes]):
            children = AttributeStore(self.type, self.copy_tags,
                                      self.level + 1)
        tag = xmlnode.localName
        id = tuple([
            xmlnode.getAttribute(a) for a in IDATTRS[tag]
            if xmlnode.hasAttribute(a)
        ])
        return tag, id, children, (names, values, children)

    def store(self, xmlnode):
        tag, id, children, attrs = self.getAttrs(xmlnode)
        tagid = (tag, id)
        if id != ():
            self.ids_deleted.add(tagid)
            self.id_attrs[tagid] = attrs
            if children:
                for child in xmlnode.childNodes:
                    if child.nodeType == Node.ELEMENT_NODE:
                        children.store(child)
        else:
            self.no_children_supported(children, tag)
            self.idless_deleted[tag].add(attrs)

    def compare(self, xmlnode):
        tag, id, children, attrs = self.getAttrs(xmlnode)
        tagid = (tag, id)
        if id != ():
            if tagid in self.ids_deleted:
                self.ids_deleted.remove(tagid)
                self.id_attrs[tagid] = self.compareAttrs(
                    self.id_attrs[tagid], attrs, tag)
            else:
                self.ids_created.add(tagid)
                self.id_attrs[tagid] = attrs

            children = self.id_attrs[tagid][2]
            if children:
                for child in xmlnode.childNodes:
                    if child.nodeType == Node.ELEMENT_NODE:
                        children.compare(child)
                if tag == TAG_TLL or tag in self.copy_tags:  # see CAVEAT2
                    child_strings = StringIO.StringIO()
                    children.writeDeleted(child_strings)
                    children.writeCreated(child_strings)
                    children.writeChanged(child_strings)
                    if child_strings.len > 0 or tag in self.copy_tags:
                        # there are some changes. Go back and store everything
                        children = AttributeStore(self.type, self.copy_tags,
                                                  self.level + 1)
                        for child in xmlnode.childNodes:
                            if child.nodeType == Node.ELEMENT_NODE:
                                children.compare(child)
                        self.id_attrs[tagid] = self.id_attrs[tagid][0:2] + (
                            children, )

        else:
            self.no_children_supported(children, tag)
            if attrs in self.idless_deleted[tag]:
                self.idless_deleted[tag].remove(attrs)
                if tag in self.copy_tags:
                    self.idless_copied[tag].add(attrs)
            else:
                self.idless_created[tag].add(attrs)

    def no_children_supported(self, children, tag):
        if children:
            print(
                "WARNING: Handling of children only supported for elements with id. Ignored for element '%s'"
                % tag)

    def compareAttrs(self, sourceAttrs, destAttrs, tag):
        snames, svalues, schildren = sourceAttrs
        dnames, dvalues, dchildren = destAttrs
        # for traffic lights, always use dchildren
        if schildren and dchildren:
            dchildren = schildren
        if snames == dnames:
            values = tuple([
                self.diff(n, s, d) for n, s, d in zip(snames, svalues, dvalues)
            ])
            return snames, values, dchildren
        else:
            sdict = defaultdict(lambda: None, zip(snames, svalues))
            ddict = defaultdict(lambda: None, zip(dnames, dvalues))
            names = tuple(set(snames + dnames))
            values = tuple([self.diff(n, sdict[n], ddict[n]) for n in names])
            return names, values, dchildren

    def diff(self, name, sourceValue, destValue):
        if sourceValue == destValue:
            return None
        elif destValue == None:
            return DEFAULT_VALUES[name]
        else:
            return destValue

    def writeDeleted(self, file):
        # data loss if two elements with different tags
        # have the same id
        for tag, id in self.ids_deleted:
            additional = ""
            if self.type == TYPE_TLLOGICS and tag == TAG_CONNECTION:
                # see CAVEAT4
                names, values, children = self.id_attrs[(tag, id)]
                additional = " " + self.attr_string(names, values)

            comment_start, comment_end = ("", "")
            if tag == TAG_TLL:  # see CAVEAT3
                comment_start, comment_end = (
                    "<!-- implicit via changed node type: ", " -->")
            self.write(
                file, '%s<%s %s%s/>%s\n' %
                (comment_start, DELETE_ELEMENT, self.id_string(
                    tag, id), additional, comment_end))
        # data loss if two elements with different tags
        # have the same list of attributes and values
        for value_set in self.idless_deleted.itervalues():
            self.write_idless(file, value_set, DELETE_ELEMENT)

    def writeCreated(self, file):
        self.write_tagids(file, self.ids_created, True)
        for tag, value_set in self.idless_created.iteritems():
            self.write_idless(file, value_set, tag)

    def writeChanged(self, file):
        tagids_changed = OrderedMultiSet(
            self.id_attrs.keys()) - (self.ids_deleted | self.ids_created)
        self.write_tagids(file, tagids_changed, False)

    def writeCopies(self, file, copy_tags):
        tagids_unchanged = OrderedMultiSet(
            self.id_attrs.keys()) - (self.ids_deleted | self.ids_created)
        self.write_tagids(file, tagids_unchanged, False)
        for tag, value_set in self.idless_copied.iteritems():
            self.write_idless(file, value_set, tag)

    def write_idless(self, file, attr_set, tag):
        for names, values, children in attr_set:
            self.write(file,
                       '<%s %s/>\n' % (tag, self.attr_string(names, values)))

    def write_tagids(self, file, tagids, create):
        for tagid in tagids:
            tag, id = tagid
            names, values, children = self.id_attrs[tagid]
            attrs = self.attr_string(names, values)
            child_strings = StringIO.StringIO()
            if children:
                # writeDeleted is not supported
                children.writeCreated(child_strings)
                children.writeChanged(child_strings)

            if len(
                    attrs
            ) > 0 or child_strings.len > 0 or create or tag in self.copy_tags:
                close_tag = "/>\n"
                if child_strings.len > 0:
                    close_tag = ">\n%s" % child_strings.getvalue()
                self.write(
                    file, '<%s %s %s%s' %
                    (tag, self.id_string(tag, id), attrs, close_tag))
                if child_strings.len > 0:
                    self.write(file, "</%s>\n" % tag)

    def write(self, file, item):
        file.write(" " * INDENT * self.level)
        file.write(item)

    def attr_string(self, names, values):
        return ' '.join(
            ['%s="%s"' % (n, v) for n, v in zip(names, values) if v != None])

    def id_string(self, tag, id):
        idattrs = IDATTRS[tag]
        return ' '.join(['%s="%s"' % (n, v) for n, v in zip(idattrs, id)])
Пример #5
0
 def writeCopies(self, file, copy_tags):
     tagids_unchanged = OrderedMultiSet(
         self.id_attrs.keys()) - (self.ids_deleted | self.ids_created)
     self.write_tagids(file, tagids_unchanged, False)
     for tag, value_set in self.idless_copied.iteritems():
         self.write_idless(file, value_set, tag)
Пример #6
0
 def writeChanged(self, file):
     tagids_changed = OrderedMultiSet(
         self.id_attrs.keys()) - (self.ids_deleted | self.ids_created)
     self.write_tagids(file, tagids_changed, False)
Пример #7
0
class AttributeStore:

    def __init__(self, type, copy_tags, level=1):
        # xml type being parsed
        self.type = type
        # tag names to copy even if unchanged
        self.copy_tags = copy_tags
        # indent level
        self.level = level
        # dict of names-tuples
        self.attrnames = {}
        # sets of (tag, id) preserve order to avoid dangling references during loading
        self.ids_deleted = OrderedMultiSet()
        self.ids_created = OrderedMultiSet()
        self.ids_copied = OrderedMultiSet()
        # dict from (tag, id) to (names, values, children)
        self.id_attrs = {}
        # dict from tag to (names, values)-sets, need to preserve order (CAVEAT5)
        self.idless_deleted = defaultdict(lambda:OrderedMultiSet())
        self.idless_created = defaultdict(lambda:OrderedMultiSet())
        self.idless_copied = defaultdict(lambda:OrderedMultiSet())


    # getAttribute returns "" if not present
    def getValue(self, node, name):
        if node.hasAttribute(name):
            return node.getAttribute(name)
        else:
            return None


    def getNames(self, xmlnode):
        idattrs = IDATTRS[xmlnode.localName]
        a = xmlnode.attributes
        all = [a.item(i).localName for i in range(a.length)]
        instance = tuple([n for n in all if n not in idattrs])
        if not instance in self.attrnames:
            self.attrnames[instance] = instance
        # only store a single instance of this tuple to conserve memory
        return self.attrnames[instance]


    def getAttrs(self, xmlnode):
        names = self.getNames(xmlnode)
        values = tuple([self.getValue(xmlnode, a) for a in names])
        children = None
        if any([c.nodeType == Node.ELEMENT_NODE for c in xmlnode.childNodes]):
            children = AttributeStore(self.type, self.copy_tags, self.level + 1)
        tag = xmlnode.localName
        id = tuple([xmlnode.getAttribute(a) for a in IDATTRS[tag] if xmlnode.hasAttribute(a)])
        return tag, id, children, (names, values, children)


    def store(self, xmlnode):
        tag, id, children, attrs = self.getAttrs(xmlnode)
        tagid = (tag, id)
        if id != ():
            self.ids_deleted.add(tagid)
            self.id_attrs[tagid] = attrs
            if children:
                for child in xmlnode.childNodes:
                    if child.nodeType == Node.ELEMENT_NODE:
                        children.store(child)
        else:
            self.no_children_supported(children, tag)
            self.idless_deleted[tag].add(attrs)


    def compare(self, xmlnode):
        tag, id, children, attrs = self.getAttrs(xmlnode)
        tagid = (tag, id)
        if id != ():
            if tagid in self.ids_deleted:
                self.ids_deleted.remove(tagid)
                self.id_attrs[tagid] = self.compareAttrs(self.id_attrs[tagid], attrs, tag)
            else:
                self.ids_created.add(tagid)
                self.id_attrs[tagid] = attrs

            children = self.id_attrs[tagid][2]
            if children:
                for child in xmlnode.childNodes:
                    if child.nodeType == Node.ELEMENT_NODE:
                        children.compare(child)
                if tag == TAG_TLL or tag in self.copy_tags: # see CAVEAT2
                    child_strings = StringIO.StringIO()
                    children.writeDeleted(child_strings)
                    children.writeCreated(child_strings)
                    children.writeChanged(child_strings)
                    if child_strings.len > 0 or tag in self.copy_tags:
                        # there are some changes. Go back and store everything
                        children = AttributeStore(self.type, self.copy_tags, self.level + 1)
                        for child in xmlnode.childNodes:
                            if child.nodeType == Node.ELEMENT_NODE:
                                children.compare(child)
                        self.id_attrs[tagid] = self.id_attrs[tagid][0:2] + (children,)


        else:
            self.no_children_supported(children, tag)
            if attrs in self.idless_deleted[tag]:
                self.idless_deleted[tag].remove(attrs)
                if tag in self.copy_tags:
                    self.idless_copied[tag].add(attrs)
            else:
                self.idless_created[tag].add(attrs)


    def no_children_supported(self, children, tag):
        if children:
            print("WARNING: Handling of children only supported for elements with id. Ignored for element '%s'" % tag)


    def compareAttrs(self, sourceAttrs, destAttrs, tag):
        snames, svalues, schildren = sourceAttrs
        dnames, dvalues, dchildren = destAttrs
        # for traffic lights, always use dchildren
        if schildren and dchildren:
            dchildren = schildren
        if snames == dnames:
            values = tuple([self.diff(n,s,d) for n,s,d in zip (snames,svalues,dvalues)])
            return snames, values, dchildren
        else:
            sdict = defaultdict(lambda:None, zip(snames, svalues))
            ddict = defaultdict(lambda:None, zip(dnames, dvalues))
            names = tuple(set(snames + dnames))
            values = tuple([self.diff(n,sdict[n],ddict[n]) for n in names])
            return names, values, dchildren


    def diff(self, name, sourceValue, destValue):
        if sourceValue == destValue:
            return None
        elif destValue == None:
            return DEFAULT_VALUES[name]
        else:
            return destValue


    def writeDeleted(self, file):
        # data loss if two elements with different tags 
        # have the same id
        for tag, id in self.ids_deleted:
            additional = ""
            if self.type == TYPE_TLLOGICS and tag == TAG_CONNECTION:
                # see CAVEAT4
                names, values, children = self.id_attrs[(tag, id)]
                additional = " " + self.attr_string(names, values)

            comment_start, comment_end = ("", "")
            if tag == TAG_TLL: # see CAVEAT3
                comment_start, comment_end = ("<!-- implicit via changed node type: ", " -->")
            self.write(file, '%s<%s %s%s/>%s\n' % (
                comment_start,
                DELETE_ELEMENT, self.id_string(tag, id), additional,
                comment_end))
        # data loss if two elements with different tags 
        # have the same list of attributes and values
        for value_set in self.idless_deleted.itervalues():
            self.write_idless(file, value_set, DELETE_ELEMENT)


    def writeCreated(self, file):
        self.write_tagids(file, self.ids_created, True)
        for tag, value_set in self.idless_created.iteritems():
            self.write_idless(file, value_set, tag)


    def writeChanged(self, file):
        tagids_changed = OrderedMultiSet(self.id_attrs.keys()) - (self.ids_deleted | self.ids_created)
        self.write_tagids(file, tagids_changed, False)


    def writeCopies(self, file, copy_tags):
        tagids_unchanged = OrderedMultiSet(self.id_attrs.keys()) - (self.ids_deleted | self.ids_created)
        self.write_tagids(file, tagids_unchanged, False)
        for tag, value_set in self.idless_copied.iteritems():
            self.write_idless(file, value_set, tag)


    def write_idless(self, file, attr_set, tag):
        for names, values, children in attr_set:
            self.write(file, '<%s %s/>\n' % (tag, self.attr_string(names, values)))


    def write_tagids(self, file, tagids, create):
        for tagid in tagids:
            tag, id = tagid
            names, values, children = self.id_attrs[tagid]
            attrs = self.attr_string(names, values)
            child_strings = StringIO.StringIO()
            if children:
                # writeDeleted is not supported
                children.writeCreated(child_strings)
                children.writeChanged(child_strings)

            if len(attrs) > 0 or child_strings.len > 0 or create or tag in self.copy_tags:
                close_tag = "/>\n"
                if child_strings.len > 0:
                    close_tag = ">\n%s" % child_strings.getvalue()
                self.write(file, '<%s %s %s%s' % (
                    tag,
                    self.id_string(tag, id),
                    attrs,
                    close_tag))
                if child_strings.len > 0:
                    self.write(file, "</%s>\n" % tag)


    def write(self, file, item):
        file.write(" " * INDENT * self.level)
        file.write(item)


    def attr_string(self, names, values):
        return ' '.join(['%s="%s"' % (n,v) for n,v in zip(names, values) if v != None])

    def id_string(self, tag, id):
        idattrs = IDATTRS[tag]
        return ' '.join(['%s="%s"' % (n,v) for n,v in zip(idattrs, id)])
Пример #8
0
class AttributeStore:
    patchImport = False

    def __init__(self, type, copy_tags, level=1):
        # xml type being parsed
        self.type = type
        # tag names to copy even if unchanged
        self.copy_tags = copy_tags
        # indent level
        self.level = level
        # dict of names-tuples
        self.attrnames = {}
        # sets of (tag, id) preserve order to avoid dangling references during
        # loading
        self.ids_deleted = OrderedMultiSet()
        self.ids_created = OrderedMultiSet()
        self.ids_copied = OrderedMultiSet()
        # dict from (tag, id) to (names, values, children)
        self.id_attrs = {}
        # dict from tag to (names, values)-sets, need to preserve order
        # (CAVEAT5)
        self.idless_deleted = defaultdict(OrderedMultiSet)
        self.idless_created = defaultdict(OrderedMultiSet)
        self.idless_copied = defaultdict(OrderedMultiSet)

    # getAttribute returns "" if not present
    def getValue(self, node, name):
        if node.hasAttribute(name):
            return node.getAttribute(name)
        else:
            return None

    def getNames(self, xmlnode):
        idattrs = IDATTRS[xmlnode.localName]
        a = xmlnode.attributes
        all = [a.item(i).localName for i in range(a.length)]
        instance = tuple([n for n in all if n not in idattrs])
        if instance not in self.attrnames:
            self.attrnames[instance] = instance
        # only store a single instance of this tuple to conserve memory
        return self.attrnames[instance]

    def getAttrs(self, xmlnode):
        names = self.getNames(xmlnode)
        values = tuple([self.getValue(xmlnode, a) for a in names])
        children = None
        if any([c.nodeType == Node.ELEMENT_NODE for c in xmlnode.childNodes]):
            children = AttributeStore(self.type, self.copy_tags,
                                      self.level + 1)
        tag = xmlnode.localName
        id = tuple([
            xmlnode.getAttribute(a) for a in IDATTRS[tag]
            if xmlnode.hasAttribute(a)
        ])
        return tag, id, children, (names, values, children)

    def store(self, xmlnode):
        tag, id, children, attrs = self.getAttrs(xmlnode)
        tagid = (tag, id)
        if id != ():
            self.ids_deleted.add(tagid)
            self.ids_copied.add(tagid)
            self.id_attrs[tagid] = attrs
            if children:
                for child in xmlnode.childNodes:
                    if child.nodeType == Node.ELEMENT_NODE:
                        children.store(child)
        else:
            self.no_children_supported(children, tag)
            self.idless_deleted[tag].add(attrs)

    def compare(self, xmlnode):
        tag, id, children, attrs = self.getAttrs(xmlnode)
        tagid = (tag, id)
        if id != ():
            if AttributeStore.patchImport:
                if self.hasChangedConnection(tagid, attrs):
                    # export all connections from the same edge
                    fromEdge = id[0]
                    markChanged = []
                    for tagid2 in self.ids_deleted:
                        fromEdge2 = tagid2[1][0]
                        if fromEdge == fromEdge2:
                            markChanged.append(tagid2)
                    for tagid2 in markChanged:
                        self.ids_deleted.remove(tagid2)
                return
            if tagid in self.ids_deleted:
                self.ids_deleted.remove(tagid)
                self.id_attrs[tagid] = self.compareAttrs(
                    self.id_attrs[tagid], attrs, tag)
            else:
                self.ids_created.add(tagid)
                self.id_attrs[tagid] = attrs

            children = self.id_attrs[tagid][2]
            if children:
                for child in xmlnode.childNodes:
                    if child.nodeType == Node.ELEMENT_NODE:
                        children.compare(child)
                if tag == TAG_TLL or tag in self.copy_tags:  # see CAVEAT2
                    child_strings = StringIO()
                    children.writeDeleted(child_strings)
                    children.writeCreated(child_strings)
                    children.writeChanged(child_strings)

                    if len(child_strings.getvalue()
                           ) > 0 or tag in self.copy_tags:
                        # there are some changes. Go back and store everything
                        children = AttributeStore(self.type, self.copy_tags,
                                                  self.level + 1)
                        for child in xmlnode.childNodes:
                            if child.nodeType == Node.ELEMENT_NODE:
                                children.compare(child)
                        self.id_attrs[tagid] = self.id_attrs[tagid][0:2] + (
                            children, )

        else:
            self.no_children_supported(children, tag)
            if attrs in self.idless_deleted[tag]:
                self.idless_deleted[tag].remove(attrs)
                if tag in self.copy_tags:
                    self.idless_copied[tag].add(attrs)
            elif tag in IGNORE_TAGS:
                self.idless_deleted[tag].clear()
            else:
                self.idless_created[tag].add(attrs)

    def no_children_supported(self, children, tag):
        if children:
            print(
                "WARNING: Handling of children only supported for elements with id. Ignored for element '%s'"
                % tag)

    def compareAttrs(self, sourceAttrs, destAttrs, tag):
        snames, svalues, schildren = sourceAttrs
        dnames, dvalues, dchildren = destAttrs
        # for traffic lights, always use dchildren
        if schildren and dchildren:
            dchildren = schildren
        if snames == dnames:
            values = tuple([
                self.diff(tag, n, s, d)
                for n, s, d in zip(snames, svalues, dvalues)
            ])
            return snames, values, dchildren
        else:
            sdict = defaultdict(lambda: None, zip(snames, svalues))
            ddict = defaultdict(lambda: None, zip(dnames, dvalues))
            names = tuple(set(snames + dnames))
            values = tuple(
                [self.diff(tag, n, sdict[n], ddict[n]) for n in names])
            return names, values, dchildren

    def diff(self, tag, name, sourceValue, destValue):
        if (sourceValue == destValue or
                # CAVEAT7
            (tag == TAG_EDGE and name == "type")):
            return None
        elif destValue is None:
            return DEFAULT_VALUES[name]
        else:
            return destValue

    def hasChangedConnection(self, tagid, attrs):
        tag, id = tagid
        if tag != TAG_CONNECTION:
            return False
        if tagid in self.ids_deleted:
            names, values, children = self.compareAttrs(
                self.id_attrs[tagid], attrs, tag)
            for v in values:
                if v is not None:
                    return True
            return False
        else:
            return True

    def writeDeleted(self, file):
        # data loss if two elements with different tags
        # have the same id
        for tag, id in self.ids_deleted:
            comment_start, comment_end = ("", "")
            additional = ""
            delete_element = DELETE_ELEMENT

            if self.type == TYPE_TLLOGICS and tag == TAG_CONNECTION:
                # see CAVEAT4
                names, values, children = self.id_attrs[(tag, id)]
                additional = " " + self.attr_string(names, values)

            if tag == TAG_TLL:  # see CAVEAT3
                comment_start, comment_end = (
                    "<!-- implicit via changed node type: ", " -->")

            if tag == TAG_CROSSING:
                delete_element = tag
                additional = ' discard="true"'

            if tag == TAG_ROUNDABOUT:
                delete_element = tag
                additional = ' discard="true"'
                comment_start, comment_end = (
                    "<!-- deletion of roundabouts not yet supported. see #2225 ",
                    " -->")

            self.write(
                file, '%s<%s %s%s/>%s\n' %
                (comment_start, delete_element, self.id_string(
                    tag, id), additional, comment_end))
        # data loss if two elements with different tags
        # have the same list of attributes and values
        for value_set in self.idless_deleted.values():
            self.write_idless(file, value_set, DELETE_ELEMENT)

    def writeCreated(self, file, whiteList=None, blackList=None):
        self.write_tagids(
            file, self.filterTags(self.ids_created, whiteList, blackList),
            True)
        for tag, value_set in self.idless_created.items():
            if ((whiteList is not None and tag not in whiteList)
                    or (blackList is not None and tag in blackList)):
                continue
            self.write_idless(file, value_set, tag)

    def getTagidsChanged(self):
        return self.ids_copied - (self.ids_deleted | self.ids_created)

    def writeChanged(self, file, whiteList=None, blackList=None):
        tagids_changed = self.getTagidsChanged()
        self.write_tagids(
            file, self.filterTags(tagids_changed, whiteList, blackList), False)

    def writeCopies(self, file, copy_tags):
        tagids_unchanged = self.ids_copied - \
            (self.ids_deleted | self.ids_created)
        self.write_tagids(file, tagids_unchanged, False)
        for tag, value_set in self.idless_copied.items():
            self.write_idless(file, value_set, tag)

    def write_idless(self, file, attr_set, tag):
        for names, values, children in attr_set:
            self.write(file,
                       '<%s %s/>\n' % (tag, self.attr_string(names, values)))

    def write_tagids(self, file, tagids, create):
        for tagid in tagids:
            tag, id = tagid
            names, values, children = self.id_attrs[tagid]
            attrs = self.attr_string(names, values)
            child_strings = StringIO()
            if children:
                # writeDeleted is not supported
                children.writeCreated(child_strings)
                children.writeChanged(child_strings)

            if len(attrs) > 0 or len(child_strings.getvalue()
                                     ) > 0 or create or tag in self.copy_tags:
                close_tag = "/>\n"
                if len(child_strings.getvalue()) > 0:
                    close_tag = ">\n%s" % child_strings.getvalue()
                self.write(
                    file, '<%s %s %s%s' %
                    (tag, self.id_string(tag, id), attrs, close_tag))
                if len(child_strings.getvalue()) > 0:
                    self.write(file, "</%s>\n" % tag)

    def write(self, file, item):
        file.write(" " * INDENT * self.level)
        file.write(item)

    def attr_string(self, names, values):
        return ' '.join([
            '%s="%s"' % (n, v) for n, v in sorted(zip(names, values))
            if v is not None
        ])

    def id_string(self, tag, id):
        idattrs = IDATTRS[tag]
        return ' '.join(
            ['%s="%s"' % (n, v) for n, v in sorted(zip(idattrs, id))])

    def filterTags(self, tagids, whiteList, blackList):
        if whiteList is not None:
            return [tagid for tagid in tagids if tagid[0] in whiteList]
        elif blackList is not None:
            return [tagid for tagid in tagids if tagid[0] not in blackList]
        else:
            return tagids

    def reorderTLL(self):
        for tag, id in self.ids_created:
            if tag == TAG_CONNECTION:
                for tag2, id2 in self.getTagidsChanged():
                    if tag2 == TAG_TLL:
                        return True
                return False
        return False
Пример #9
0
class AttributeStore:
    patchImport = False

    def __init__(self, type, copy_tags, level=1):
        # xml type being parsed
        self.type = type
        # tag names to copy even if unchanged
        self.copy_tags = copy_tags
        # indent level
        self.level = level
        # dict of names-tuples
        self.attrnames = {}
        # sets of (tag, id) preserve order to avoid dangling references during
        # loading
        self.ids_deleted = OrderedMultiSet()
        self.ids_created = OrderedMultiSet()
        self.ids_copied = OrderedMultiSet()
        # dict from (tag, id) to (names, values, children)
        self.id_attrs = {}
        # dict from tag to (names, values)-sets, need to preserve order
        # (CAVEAT5)
        self.idless_deleted = defaultdict(OrderedMultiSet)
        self.idless_created = defaultdict(OrderedMultiSet)
        self.idless_copied = defaultdict(OrderedMultiSet)

    # getAttribute returns "" if not present
    def getValue(self, node, name):
        if node.hasAttribute(name):
            return node.getAttribute(name)
        else:
            return None

    def getNames(self, xmlnode):
        idattrs = IDATTRS[xmlnode.localName]
        a = xmlnode.attributes
        all = [a.item(i).localName for i in range(a.length)]
        instance = tuple([n for n in all if n not in idattrs])
        if instance not in self.attrnames:
            self.attrnames[instance] = instance
        # only store a single instance of this tuple to conserve memory
        return self.attrnames[instance]

    def getAttrs(self, xmlnode):
        names = self.getNames(xmlnode)
        values = tuple([self.getValue(xmlnode, a) for a in names])
        children = None
        if any([c.nodeType == Node.ELEMENT_NODE for c in xmlnode.childNodes]):
            children = AttributeStore(
                self.type, self.copy_tags, self.level + 1)
        tag = xmlnode.localName
        id = tuple([xmlnode.getAttribute(a)
                    for a in IDATTRS[tag] if xmlnode.hasAttribute(a)])
        return tag, id, children, (names, values, children)

    def store(self, xmlnode):
        tag, id, children, attrs = self.getAttrs(xmlnode)
        tagid = (tag, id)
        if id != ():
            self.ids_deleted.add(tagid)
            self.ids_copied.add(tagid)
            self.id_attrs[tagid] = attrs
            if children:
                for child in xmlnode.childNodes:
                    if child.nodeType == Node.ELEMENT_NODE:
                        children.store(child)
        else:
            self.no_children_supported(children, tag)
            self.idless_deleted[tag].add(attrs)

    def compare(self, xmlnode):
        tag, id, children, attrs = self.getAttrs(xmlnode)
        tagid = (tag, id)
        if id != ():
            if AttributeStore.patchImport:
                if self.hasChangedConnection(tagid, attrs):
                    # export all connections from the same edge
                    fromEdge = id[0]
                    markChanged = []
                    for tagid2 in self.ids_deleted:
                        fromEdge2 = tagid2[1][0]
                        if fromEdge == fromEdge2:
                            markChanged.append(tagid2)
                    for tagid2 in markChanged:
                        self.ids_deleted.remove(tagid2)
                return
            if tagid in self.ids_deleted:
                self.ids_deleted.remove(tagid)
                self.id_attrs[tagid] = self.compareAttrs(
                    self.id_attrs[tagid], attrs, tag)
            else:
                self.ids_created.add(tagid)
                self.id_attrs[tagid] = attrs

            children = self.id_attrs[tagid][2]
            if children:
                for child in xmlnode.childNodes:
                    if child.nodeType == Node.ELEMENT_NODE:
                        children.compare(child)
                if tag == TAG_TLL or tag in self.copy_tags:  # see CAVEAT2
                    child_strings = StringIO()
                    children.writeDeleted(child_strings)
                    children.writeCreated(child_strings)
                    children.writeChanged(child_strings)

                    if len(child_strings.getvalue()) > 0 or tag in self.copy_tags:
                        # there are some changes. Go back and store everything
                        children = AttributeStore(
                            self.type, self.copy_tags, self.level + 1)
                        for child in xmlnode.childNodes:
                            if child.nodeType == Node.ELEMENT_NODE:
                                children.compare(child)
                        self.id_attrs[tagid] = self.id_attrs[
                            tagid][0:2] + (children,)

        else:
            self.no_children_supported(children, tag)
            if attrs in self.idless_deleted[tag]:
                self.idless_deleted[tag].remove(attrs)
                if tag in self.copy_tags:
                    self.idless_copied[tag].add(attrs)
            elif tag in IGNORE_TAGS:
                self.idless_deleted[tag].clear()
            else:
                self.idless_created[tag].add(attrs)

    def no_children_supported(self, children, tag):
        if children:
            print(
                "WARNING: Handling of children only supported for elements with id. Ignored for element '%s'" % tag)

    def compareAttrs(self, sourceAttrs, destAttrs, tag):
        snames, svalues, schildren = sourceAttrs
        dnames, dvalues, dchildren = destAttrs
        # for traffic lights, always use dchildren
        if schildren and dchildren:
            dchildren = schildren
        if snames == dnames:
            values = tuple([self.diff(tag, n, s, d)
                            for n, s, d in zip(snames, svalues, dvalues)])
            return snames, values, dchildren
        else:
            sdict = defaultdict(lambda: None, zip(snames, svalues))
            ddict = defaultdict(lambda: None, zip(dnames, dvalues))
            names = tuple(set(snames + dnames))
            values = tuple([self.diff(tag, n, sdict[n], ddict[n]) for n in names])
            return names, values, dchildren

    def diff(self, tag, name, sourceValue, destValue):
        if (sourceValue == destValue or
                # CAVEAT7
                (tag == TAG_EDGE and name == "type")):
            return None
        elif destValue is None:
            return DEFAULT_VALUES[name]
        else:
            return destValue

    def hasChangedConnection(self, tagid, attrs):
        tag, id = tagid
        if tag != TAG_CONNECTION:
            return False
        if tagid in self.ids_deleted:
            names, values, children = self.compareAttrs(self.id_attrs[tagid], attrs, tag)
            for v in values:
                if v is not None:
                    return True
            return False
        else:
            return True

    def writeDeleted(self, file):
        # data loss if two elements with different tags
        # have the same id
        for tag, id in self.ids_deleted:
            comment_start, comment_end = ("", "")
            additional = ""
            delete_element = DELETE_ELEMENT

            if self.type == TYPE_TLLOGICS and tag == TAG_CONNECTION:
                # see CAVEAT4
                names, values, children = self.id_attrs[(tag, id)]
                additional = " " + self.attr_string(names, values)

            if tag == TAG_TLL:  # see CAVEAT3
                comment_start, comment_end = (
                    "<!-- implicit via changed node type: ", " -->")

            if tag == TAG_CROSSING:
                delete_element = tag
                additional = ' discard="true"'

            if tag == TAG_ROUNDABOUT:
                delete_element = tag
                additional = ' discard="true"'
                comment_start, comment_end = (
                    "<!-- deletion of roundabouts not yet supported. see #2225 ", " -->")

            self.write(file, '%s<%s %s%s/>%s\n' % (
                comment_start,
                delete_element, self.id_string(tag, id), additional,
                comment_end))
        # data loss if two elements with different tags
        # have the same list of attributes and values
        for value_set in self.idless_deleted.values():
            self.write_idless(file, value_set, DELETE_ELEMENT)

    def writeCreated(self, file, whiteList=None, blackList=None):
        self.write_tagids(file, self.filterTags(self.ids_created, whiteList, blackList), True)
        for tag, value_set in self.idless_created.items():
            if ((whiteList is not None and tag not in whiteList)
                    or (blackList is not None and tag in blackList)):
                continue
            self.write_idless(file, value_set, tag)

    def getTagidsChanged(self):
        return self.ids_copied - (self.ids_deleted | self.ids_created)

    def writeChanged(self, file, whiteList=None, blackList=None):
        tagids_changed = self.getTagidsChanged()
        self.write_tagids(file, self.filterTags(tagids_changed, whiteList, blackList), False)

    def writeCopies(self, file, copy_tags):
        tagids_unchanged = self.ids_copied - \
            (self.ids_deleted | self.ids_created)
        self.write_tagids(file, tagids_unchanged, False)
        for tag, value_set in self.idless_copied.items():
            self.write_idless(file, value_set, tag)

    def write_idless(self, file, attr_set, tag):
        for names, values, children in attr_set:
            self.write(file, '<%s %s/>\n' %
                       (tag, self.attr_string(names, values)))

    def write_tagids(self, file, tagids, create):
        for tagid in tagids:
            tag, id = tagid
            names, values, children = self.id_attrs[tagid]
            attrs = self.attr_string(names, values)
            child_strings = StringIO()
            if children:
                # writeDeleted is not supported
                children.writeCreated(child_strings)
                children.writeChanged(child_strings)

            if len(attrs) > 0 or len(child_strings.getvalue()) > 0 or create or tag in self.copy_tags:
                close_tag = "/>\n"
                if len(child_strings.getvalue()) > 0:
                    close_tag = ">\n%s" % child_strings.getvalue()
                self.write(file, '<%s %s %s%s' % (
                    tag,
                    self.id_string(tag, id),
                    attrs,
                    close_tag))
                if len(child_strings.getvalue()) > 0:
                    self.write(file, "</%s>\n" % tag)

    def write(self, file, item):
        file.write(" " * INDENT * self.level)
        file.write(item)

    def attr_string(self, names, values):
        return ' '.join(['%s="%s"' % (n, v) for n, v in sorted(zip(names, values)) if v is not None])

    def id_string(self, tag, id):
        idattrs = IDATTRS[tag]
        return ' '.join(['%s="%s"' % (n, v) for n, v in sorted(zip(idattrs, id))])

    def filterTags(self, tagids, whiteList, blackList):
        if whiteList is not None:
            return [tagid for tagid in tagids if tagid[0] in whiteList]
        elif blackList is not None:
            return [tagid for tagid in tagids if tagid[0] not in blackList]
        else:
            return tagids

    def reorderTLL(self):
        for tag, id in self.ids_created:
            if tag == TAG_CONNECTION:
                for tag2, id2 in self.getTagidsChanged():
                    if tag2 == TAG_TLL:
                        return True
                return False
        return False