#!/usr/bin/env python
# Copyright 2013, Big Switch Networks, Inc.
#
# LoxiGen is licensed under the Eclipse Public License, version 1.0 (EPL), with
# the following special exception:
#
# LOXI Exception
#
# As a special exception to the terms of the EPL, you may distribute libraries
# generated by LoxiGen (LoxiGen Libraries) under the terms of your choice, provided
# that copyright and licensing notices generated by LoxiGen are not altered or removed
# from the LoxiGen Libraries and the notice provided below is (i) included in
# the LoxiGen Libraries, if distributed in source code form and (ii) included in any
# documentation for the LoxiGen Libraries, if distributed in binary form.
#
# Notice: "Copyright 2013, Big Switch Networks, Inc. This library was generated by the LoxiGen Compiler."
#
# You may not use this file except in compliance with the EPL or LOXI Exception. You may obtain
# a copy of the EPL at:
#
# http://www.eclipse.org/legal/epl-v10.html
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# EPL for the specific language governing permissions and limitations
# under the EPL.

import copy
from collections import OrderedDict
from itertools import chain
import logging

import ir

def build_unified_ir(name_protocol_map):
    class UnifiedClassSpec(object):
        def __init__(self, name):
            self.name = name
            self.members = OrderedDict()
            self.superclass_name = None
            self.superclass_set = False
            self.params = OrderedDict()
            self.version_class = OrderedDict()
            self.virtual = False
            self.base_length = None
            self.is_fixed_length = True

        def add_class(self, version, v_class):
            for v_member in v_class.members:
                if hasattr(v_member, "name"):
                    if not v_member.name in self.members:
                        self.members[v_member.name] = v_member
                    else:
                        if not type(self.members[v_member.name]) == type(v_member):
                            raise Exception("Error unifying ir class {} - adding version: {} - member_type {} <-> {}".format(
                                    self.name, v_class.protocol.version, self.members[v_member.name], v_member))

            if not self.superclass_set:
                self.superclass_name = v_class.superclass.name if v_class.superclass else None
            else:
                if self.superclass_name != v_class.superclass_name:
                    raise Exception("Error unifying ir class {} - adding version {} - superclass: param {} <-> {}".format(
                            self.name, v_class.protocol.version, self.superclass_name, v_class.superclass_name))

            for name, value in v_class.params.items():
                if not name in self.params:
                    self.params[name] = value
                else:
                    if self.params[name] != value:
                        raise Exception("Error unifying ir class {} - adding version: {} - param {} <-> {}".format(
                                self.name, v_class.protocol.version, self.params[name], value))

            if v_class.virtual:
                self.virtual = True

            if not v_class.is_fixed_length:
                self.is_fixed_length = False

            if self.base_length is None:
                self.base_length = v_class.base_length
            elif self.base_length != v_class.base_length:
                self.is_fixed_length = False
                if self.base_length > v_class.base_length:
                    self.base_length = v_class.base_length
            self.version_class[version] = v_class

    class UnifiedEnumSpec(object):
        def __init__(self, name):
            self.name = name
            self.entries = {}
            self.params = {}
            self.version_enums = OrderedDict()

        def add_enum(self, version, v_enum):
            for e in v_enum.entries:
                if not e.name in self.entries:
                    self.entries[e.name] = ir.OFEnumEntry(e.name, e.value, copy.copy(e.params))
                else:
                    entry = self.entries[e.name]
                    if v_enum.params.get('stable') == 'True' and e.value != entry.value:
                        raise Exception("Error unifying stable ir enum {} - adding entry {} version {} value {} <-> {}".format(
                            self.name, e.name, version, entry.value, e.value))
                    for name, value in e.params.items():
                        if not name in entry.params:
                            entry.params[name] = value
                        elif entry.params[name] != value:
                            raise Exception("Error unifying ir enum {} - adding version: param {} <-> {}".format(
                                self.name, entry.params[name], value))
            for name, value in v_enum.params.items():
                if not name in self.params:
                    self.params[name] = value
                else:
                    if self.params[name] != value:
                        if name == "wire_type":
                            self.params[name] = None
                        else:
                            raise Exception("Error unifying ir enum {} - adding version: {} param {} <-> {}".format(
                                self.name, v_enum.protocol.version, self.params[name], value))

            self.version_enums[version]=v_enum

    u_name_classes = OrderedDict()
    u_name_enums = OrderedDict()

    for version, protocol in name_protocol_map.items():
        assert isinstance(version, ir.OFVersion)
        for v_class in protocol.classes:
            name = v_class.name
            if not name in u_name_classes:
                u_name_classes[name] = UnifiedClassSpec(name)
            spec = u_name_classes[name]
            spec.add_class(version, v_class)

        for v_enum in protocol.enums:
            name = v_enum.name
            if not name in u_name_enums:
                u_name_enums[name] = UnifiedEnumSpec(name)
            spec = u_name_enums[name]
            spec.add_enum(version, v_enum)

    unified_enums = tuple(ir.OFEnum(name=s.name, entries=tuple(s.entries.values()), params=s.params) for s in u_name_enums.values())
    unified_classes = OrderedDict()
    for name, spec in u_name_classes.items():
        u = ir.OFUnifiedClass(
                name = spec.name,
                version_classes=spec.version_class,
                superclass=None if not spec.superclass_name else unified_classes[spec.superclass_name],
                members=spec.members.values(),
                virtual=spec.virtual,
                params=spec.params,
                base_length=spec.base_length,
                is_fixed_length=spec.is_fixed_length)
        unified_classes[name] = u

    unified = ir.OFProtocol(version=None, classes = tuple(unified_classes.values()), enums=unified_enums)
    for e in chain(unified.classes, unified.enums):
        e.protocol = unified
    return unified

#!/usr/bin/env python
# Copyright 2013, Big Switch Networks, Inc.
#
# LoxiGen is licensed under the Eclipse Public License, version 1.0 (EPL), with
# the following special exception:
#
# LOXI Exception
#
# As a special exception to the terms of the EPL, you may distribute libraries
# generated by LoxiGen (LoxiGen Libraries) under the terms of your choice, provided
# that copyright and licensing notices generated by LoxiGen are not altered or removed
# from the LoxiGen Libraries and the notice provided below is (i) included in
# the LoxiGen Libraries, if distributed in source code form and (ii) included in any
# documentation for the LoxiGen Libraries, if distributed in binary form.
#
# Notice: "Copyright 2013, Big Switch Networks, Inc. This library was generated by the LoxiGen Compiler."
#
# You may not use this file except in compliance with the EPL or LOXI Exception. You may obtain
# a copy of the EPL at:
#
# http://www.eclipse.org/legal/epl-v10.html
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# EPL for the specific language governing permissions and limitations
# under the EPL.

import copy
from collections import OrderedDict
from itertools import chain
import logging

import ir

def build_unified_ir(name_protocol_map):
    class UnifiedClassSpec(object):
        def __init__(self, name):
            self.name = name
            self.members = OrderedDict()
            self.superclass_name = None
            self.superclass_set = False
            self.params = OrderedDict()
            self.version_class = OrderedDict()
            self.virtual = False
            self.base_length = None
            self.is_fixed_length = True

        def add_class(self, version, v_class):
            for v_member in v_class.members:
                if hasattr(v_member, "name"):
                    if not v_member.name in self.members:
                        self.members[v_member.name] = v_member
                    else:
                        if not type(self.members[v_member.name]) == type(v_member):
                            raise Exception("Error unifying ir class {} - adding version: {} - member_type {} <-> {}".format(
                                    self.name, v_class.protocol.version, self.members[v_member.name], v_member))

            if not self.superclass_set:
                self.superclass_name = v_class.superclass.name if v_class.superclass else None
            else:
                if self.superclass_name != v_class.superclass_name:
                    raise Exception("Error unifying ir class {} - adding version {} - superclass: param {} <-> {}".format(
                            self.name, v_class.protocol.version, self.superclass_name, v_class.superclass_name))

            for name, value in v_class.params.items():
                if not name in self.params:
                    self.params[name] = value
                else:
                    if self.params[name] != value:
                        raise Exception("Error unifying ir class {} - adding version: {} - param {} <-> {}".format(
                                self.name, v_class.protocol.version, self.params[name], value))

            if v_class.virtual:
                self.virtual = True

            if not v_class.is_fixed_length:
                self.is_fixed_length = False

            if self.base_length is None:
                self.base_length = v_class.base_length
            elif self.base_length != v_class.base_length:
                self.is_fixed_length = False
                if self.base_length > v_class.base_length:
                    self.base_length = v_class.base_length
            self.version_class[version] = v_class

    class UnifiedEnumSpec(object):
        def __init__(self, name):
            self.name = name
            self.entries = {}
            self.params = {}
            self.version_enums = OrderedDict()

        def add_enum(self, version, v_enum):
            for e in v_enum.entries:
                if not e.name in self.entries:
                    self.entries[e.name] = ir.OFEnumEntry(e.name, e.value, copy.copy(e.params))
                else:
                    entry = self.entries[e.name]
                    if v_enum.params.get('stable') == 'True' and e.value != entry.value:
                        raise Exception("Error unifying stable ir enum {} - adding entry {} version {} value {} <-> {}".format(
                            self.name, e.name, version, entry.value, e.value))
                    for name, value in e.params.items():
                        if not name in entry.params:
                            entry.params[name] = value
                        elif entry.params[name] != value:
                            raise Exception("Error unifying ir enum {} - adding version: param {} <-> {}".format(
                                self.name, entry.params[name], value))
            for name, value in v_enum.params.items():
                if not name in self.params:
                    self.params[name] = value
                else:
                    if self.params[name] != value:
                        if name == "wire_type":
                            self.params[name] = None
                        else:
                            raise Exception("Error unifying ir enum {} - adding version: {} param {} <-> {}".format(
                                self.name, v_enum.protocol.version, self.params[name], value))

            self.version_enums[version]=v_enum

    u_name_classes = OrderedDict()
    u_name_enums = OrderedDict()

    for version, protocol in name_protocol_map.items():
        assert isinstance(version, ir.OFVersion)
        for v_class in protocol.classes:
            name = v_class.name
            if not name in u_name_classes:
                u_name_classes[name] = UnifiedClassSpec(name)
            spec = u_name_classes[name]
            spec.add_class(version, v_class)

        for v_enum in protocol.enums:
            name = v_enum.name
            if not name in u_name_enums:
                u_name_enums[name] = UnifiedEnumSpec(name)
            spec = u_name_enums[name]
            spec.add_enum(version, v_enum)

    unified_enums = tuple(ir.OFEnum(name=s.name, entries=tuple(s.entries.values()), params=s.params) for s in u_name_enums.values())
    unified_classes = OrderedDict()
    for name, spec in u_name_classes.items():
        u = ir.OFUnifiedClass(
                name = spec.name,
                version_classes=spec.version_class,
                superclass=None if not spec.superclass_name else unified_classes[spec.superclass_name],
                members=spec.members.values(),
                virtual=spec.virtual,
                params=spec.params,
                base_length=spec.base_length,
                is_fixed_length=spec.is_fixed_length)
        unified_classes[name] = u

    unified = ir.OFProtocol(version=None, classes = tuple(unified_classes.values()), enums=unified_enums)
    for e in chain(unified.classes, unified.enums):
        e.protocol = unified
    return unified

#!/usr/bin/env python # Copyright 2013, Big Switch Networks, Inc. # # LoxiGen is licensed under the Eclipse Public License, version 1.0 (EPL), with # the following special exception: # # LOXI Exception # # As a special exception to the terms of the EPL, you may distribute libraries # generated by LoxiGen (LoxiGen Libraries) under the terms of your choice, provided # that copyright and licensing notices generated by LoxiGen are not altered or removed # from the LoxiGen Libraries and the notice provided below is (i) included in # the LoxiGen Libraries, if distributed in source code form and (ii) included in any # documentation for the LoxiGen Libraries, if distributed in binary form. # # Notice: "Copyright 2013, Big Switch Networks, Inc. This library was generated by the LoxiGen Compiler." # # You may not use this file except in compliance with the EPL or LOXI Exception. You may obtain # a copy of the EPL at: # # http://www.eclipse.org/legal/epl-v10.html # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # EPL for the specific language governing permissions and limitations # under the EPL.