<commit_before>/*
 * Copyright (c) 2020 Orange. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */
package org.opendaylight.bgpcep.bgp.topology.provider;

import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;

import com.google.common.annotations.VisibleForTesting;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import org.opendaylight.graph.ConnectedGraph;
import org.opendaylight.graph.ConnectedGraphProvider;
import org.opendaylight.mdsal.binding.api.DataBroker;
import org.opendaylight.mdsal.binding.api.ReadWriteTransaction;
import org.opendaylight.protocol.bgp.rib.RibReference;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.Ipv4InterfaceIdentifier;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.Ipv6InterfaceIdentifier;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.LinkstateAddressFamily;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.LinkstateSubsequentAddressFamily;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.bgp.rib.rib.loc.rib.tables.routes.LinkstateRoutesCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.ObjectType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.attribute.SrAdjIds;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.object.type.LinkCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.object.type.NodeCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.object.type.PrefixCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.object.type.link._case.LinkDescriptors;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.path.attribute.LinkStateAttribute;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.path.attribute.link.state.attribute.LinkAttributesCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.path.attribute.link.state.attribute.NodeAttributesCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.path.attribute.link.state.attribute.PrefixAttributesCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.path.attribute.link.state.attribute.link.attributes._case.LinkAttributes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.path.attribute.link.state.attribute.node.attributes._case.NodeAttributes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.path.attribute.link.state.attribute.prefix.attributes._case.PrefixAttributes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.routes.LinkstateRoutes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.routes.linkstate.routes.LinkstateRoute;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.routes.linkstate.routes.linkstate.route.Attributes1;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.node.identifier.CRouterIdentifier;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.node.identifier.c.router.identifier.IsisNodeCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.node.identifier.c.router.identifier.OspfNodeCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.path.attributes.Attributes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.segment.routing.ext.rev200120.adj.flags.flags.isis.adj.flags._case.IsisAdjFlags;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.segment.routing.ext.rev200120.adj.flags.flags.ospf.adj.flags._case.OspfAdjFlags;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.segment.routing.ext.rev200120.prefix.sid.tlv.flags.IsisPrefixFlagsCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.segment.routing.ext.rev200120.sid.label.index.SidLabelIndex;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.segment.routing.ext.rev200120.sid.label.index.sid.label.index.LocalLabelCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.segment.routing.ext.rev200120.sid.label.index.sid.label.index.SidCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.DecimalBandwidth;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.Delay;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.Loss;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.Vertex.VertexType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.edge.EdgeAttributes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.edge.EdgeAttributesBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.edge.attributes.MinMaxDelay;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.edge.attributes.MinMaxDelayBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.edge.attributes.UnreservedBandwidth;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.edge.attributes.UnreservedBandwidthBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.edge.attributes.UnreservedBandwidthKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.Graph.DomainScope;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Edge;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.EdgeBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.EdgeKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Prefix;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.PrefixBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Vertex;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.VertexBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.VertexKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.vertex.SrgbBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.network.concepts.rev131125.Bandwidth;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.odl.bgp.topology.types.rev160524.TopologyTypes1Builder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.odl.bgp.topology.types.rev160524.bgp.linkstate.topology.type.BgpLinkstateTopologyBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.rsvp.rev150820.SrlgId;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.TopologyTypes;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.TopologyTypesBuilder;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.Uint32;
import org.opendaylight.yangtools.yang.common.Uint64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This Class build the Traffic Engineering Database as a Connected Graph
 * suitable to be used latter by Path Computation algorithms to compute end to
 * end path.
 *
 * @author <NAME>
 * @author <NAME>
 */
public class LinkstateGraphBuilder extends AbstractTopologyBuilder<LinkstateRoute> {
    private static final TopologyTypes LINKSTATE_TOPOLOGY_TYPE = new TopologyTypesBuilder().addAugmentation(
            new TopologyTypes1Builder().setBgpLinkstateTopology(new BgpLinkstateTopologyBuilder().build()).build())
            .build();

    private static final String UNHANDLED_OBJECT_CLASS = "Unhandled object class {}";

    private static final Logger LOG = LoggerFactory.getLogger(LinkstateGraphBuilder.class);

    private final ConnectedGraph cgraph;

    public LinkstateGraphBuilder(final DataBroker dataProvider, final RibReference locRibReference,
            final TopologyId topologyId, final ConnectedGraphProvider provider) {
        super(dataProvider, locRibReference, topologyId, LINKSTATE_TOPOLOGY_TYPE, LinkstateAddressFamily.class,
                LinkstateSubsequentAddressFamily.class);
        this.cgraph = requireNonNull(provider).createConnectedGraph("ted://" + topologyId.getValue(),
                DomainScope.IntraDomain);
        /* LinkStateGraphBuilder doesn't write information in the Network Topology tree of the Data Store.
         * This is performed by ConnectedGraphProvider which write element in Graph tree of the Data Store */
        this.networkTopologyTransaction = false;
        LOG.info("Started Traffic Engineering Graph Builder");
    }

    @VisibleForTesting
    LinkstateGraphBuilder(final DataBroker dataProvider, final RibReference locRibReference,
            final TopologyId topologyId, final ConnectedGraphProvider provider, final long listenerResetLimitInMillsec,
            final int listenerResetEnforceCounter) {
        super(dataProvider, locRibReference, topologyId, LINKSTATE_TOPOLOGY_TYPE, LinkstateAddressFamily.class,
                LinkstateSubsequentAddressFamily.class, listenerResetLimitInMillsec, listenerResetEnforceCounter);
        this.cgraph = requireNonNull(provider).createConnectedGraph("ted://" + topologyId.getValue(),
                DomainScope.IntraDomain);
        /* LinkStateGraphBuilder doesn't write information in the Network Topology tree of the Data Store.
         * This is performed by ConnectedGraphProvider which write element in Graph tree of the Data Store */
        this.networkTopologyTransaction = false;
        LOG.info("Started Traffic Engineering Graph Builder");
    }

    @Override
    protected void createObject(final ReadWriteTransaction trans, final InstanceIdentifier<LinkstateRoute> id,
            final LinkstateRoute value) {
        final ObjectType t = value.getObjectType();
        checkArgument(t != null, "Route %s value %s has null object type", id, value);

        if (t instanceof LinkCase) {
            createEdge(value, (LinkCase) t, value.getAttributes());
        } else if (t instanceof NodeCase) {
            createVertex(value, (NodeCase) t, value.getAttributes());
        } else if (t instanceof PrefixCase) {
            createPrefix(value, (PrefixCase) t, value.getAttributes());
        } else {
            LOG.debug(UNHANDLED_OBJECT_CLASS, t.implementedInterface());
        }
    }

    /**
     * Verify that mandatory information (Local & Remote Node and Link
     * descriptors) are present in the link.
     *
     * @param linkCase  The Link part of the Linkstate route
     *
     * @return True if all information are present, false otherwise
     */
    private static boolean checkLinkState(final LinkCase linkCase) {
        if (linkCase.getLocalNodeDescriptors() == null || linkCase.getRemoteNodeDescriptors() == null) {
            LOG.warn("Missing Local or Remote Node descriptor in link {}, skipping it", linkCase);
            return false;
        }
        if (linkCase.getLinkDescriptors() == null) {
            LOG.warn("Missing Link descriptor in link {}, skipping it", linkCase);
            return false;
        }
        return true;
    }

    /**
     * Get Link attributes from the Link State route.
     *
     * @param attributes  Link State Route Attributes
     *
     * @return Link Attributes
     */
    private static LinkAttributes getLinkAttributes(final Attributes attributes) {
        final Attributes1 attr = attributes.augmentation(Attributes1.class);
        if (attr != null) {
            final LinkStateAttribute attrType = attr.getLinkStateAttribute();
            if (attrType instanceof LinkAttributesCase) {
                return ((LinkAttributesCase) attrType).getLinkAttributes();
            }
        }
        return null;
    }

    /**
     * Determine the Source Edge Key from the link descriptor.
     * There is several case: IPv4, IPv6 address or Unnumbered Interface.
     *
     * @param linkCase The Link part of the Linkstate route
     *
     * @return Unique key
     */
    private static Uint64 getEdgeId(final LinkCase linkCase) {
        Uint64 key = Uint64.ZERO;
        final LinkDescriptors linkDescriptors = linkCase.getLinkDescriptors();
        if (linkDescriptors.getIpv4InterfaceAddress() != null) {
            key = ipv4ToKey(linkDescriptors.getIpv4InterfaceAddress());
        }
        if (linkDescriptors.getIpv6InterfaceAddress() != null) {
            key = ipv6ToKey(linkDescriptors.getIpv6InterfaceAddress());
        }
        if (linkDescriptors.getLinkLocalIdentifier() != null) {
            key = linkDescriptors.getLinkLocalIdentifier().toUint64();
        }
        return key;
    }

    /**
     * Create new Connected Edge in the Connected Graph.
     *
     * @param value       The complete Linkstate route information
     * @param linkCase    The Link part of the Linkstate route
     * @param attributes  The Link attributes
     *
     */
    private void createEdge(final LinkstateRoute value, final LinkCase linkCase, final Attributes attributes) {
        checkArgument(checkLinkState(linkCase), "Missing mandatory information in link {}", linkCase);

        final LinkAttributes la = getLinkAttributes(attributes);
        if (la == null) {
            LOG.warn("Missing attributes in link {} route {}, skipping it", linkCase, value);
            return;
        }

        /* Get Source and Destination Vertex from the graph */
        Uint64 srcId = getVertexId(linkCase.getLocalNodeDescriptors().getCRouterIdentifier());
        Uint64 dstId = getVertexId(linkCase.getRemoteNodeDescriptors().getCRouterIdentifier());
        if (srcId == Uint64.ZERO || dstId == Uint64.ZERO) {
            LOG.warn("Unable to get the Source or Destination Vertex Identifier from link {}, skipping it", linkCase);
            return;
        }

        /* Get Source and Destination Key for the corresponding Edge */
        Uint64 edgeId = getEdgeId(linkCase);
        if (edgeId == Uint64.ZERO) {
            LOG.warn("Unable to get the Edge Identifier from link {}, skipping it", linkCase);
            return;
        }

        /* Add associated Edge */
        Edge edge = new EdgeBuilder().setEdgeId(edgeId).setLocalVertexId(srcId).setRemoteVertexId(dstId)
                .setName(srcId + " - " + dstId)
                .setEdgeAttributes(createEdgeAttributes(la, linkCase.getLinkDescriptors())).build();

        /*
         * Add corresponding Prefix for the Local Address. Remote address will be added with the remote Edge */
        final var localAddress = edge.getEdgeAttributes().getLocalAddress();
        PrefixBuilder prefBuilder = new PrefixBuilder().setVertexId(srcId);
        if (localAddress.getIpv4Address() != null) {
            prefBuilder.setPrefix(new IpPrefix(IetfInetUtil.INSTANCE.ipv4PrefixFor(localAddress.getIpv4Address())));
        }
        if (localAddress.getIpv6Address() != null) {
            prefBuilder.setPrefix(new IpPrefix(IetfInetUtil.INSTANCE.ipv6PrefixFor(localAddress.getIpv6Address())));
        }
        Prefix prefix = prefBuilder.build();

        /* Add the Edge in the Connected Graph */
        LOG.info("Add Edge {} and associated Prefix {} in TED[{}]", edge.getName(), prefix.getPrefix(), cgraph);
        cgraph.addEdge(edge);
        cgraph.addPrefix(prefix);
    }

    private static final int MAX_PRIORITY = 8;

    /**
     * Create Edge Attributes from Link attributes.
     *
     * @param la         Linkstate Attributes
     * @param linkDesc   Linkstate Descriptors
     *
     * @return EdgeAttributes
     */
    private static EdgeAttributes createEdgeAttributes(final LinkAttributes la, final LinkDescriptors linkDesc) {
        EdgeAttributesBuilder builder = new EdgeAttributesBuilder();

        if (linkDesc.getIpv4InterfaceAddress() != null) {
            builder.setLocalAddress(new IpAddress(new Ipv4Address(linkDesc.getIpv4InterfaceAddress())));
        }
        if (linkDesc.getIpv6InterfaceAddress() != null) {
            builder.setLocalAddress(new IpAddress(new Ipv6Address(linkDesc.getIpv6InterfaceAddress())));
        }
        if (linkDesc.getIpv4NeighborAddress() != null) {
            builder.setRemoteAddress(new IpAddress(new Ipv4Address(linkDesc.getIpv4NeighborAddress())));
        }
        if (linkDesc.getIpv6NeighborAddress() != null) {
            builder.setRemoteAddress(new IpAddress(new Ipv6Address(linkDesc.getIpv6NeighborAddress())));
        }
        if (linkDesc.getLinkLocalIdentifier() != null) {
            builder.setLocalIdentifier(linkDesc.getLinkLocalIdentifier());
        }
        if (linkDesc.getLinkRemoteIdentifier() != null) {
            builder.setRemoteIdentifier(linkDesc.getLinkRemoteIdentifier());
        }
        if (la.getMetric() != null) {
            builder.setMetric(la.getMetric().getValue());
        }
        if (la.getTeMetric() != null) {
            builder.setTeMetric(la.getTeMetric().getValue());
        }
        if (la.getMaxLinkBandwidth() != null) {
            builder.setMaxLinkBandwidth(bandwithToDecimalBandwidth(la.getMaxLinkBandwidth()));
        }
        if (la.getMaxReservableBandwidth() != null) {
            builder.setMaxResvLinkBandwidth(bandwithToDecimalBandwidth(la.getMaxReservableBandwidth()));
        }
        if (la.getUnreservedBandwidth() != null) {
            int upperBound = Math.min(la.getUnreservedBandwidth().size(), MAX_PRIORITY);
            final List<UnreservedBandwidth> unRsvBw = new ArrayList<>(upperBound);

            for (final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120
                    .UnreservedBandwidth bandwidth : la.nonnullUnreservedBandwidth().values()) {
                unRsvBw.add(new UnreservedBandwidthBuilder()
                        .setBandwidth(bandwithToDecimalBandwidth(bandwidth.getBandwidth()))
                        .withKey(new UnreservedBandwidthKey(bandwidth.getPriority())).build());
            }
            builder.setUnreservedBandwidth(unRsvBw);
        }
        if (la.getAdminGroup() != null) {
            builder.setAdminGroup(la.getAdminGroup().getValue());
        }
        if (la.getLinkDelay() != null) {
            builder.setDelay(new Delay(la.getLinkDelay().getValue()));
        }
        if (la.getLinkMinMaxDelay() != null && la.getLinkMinMaxDelay() != null) {
            MinMaxDelay mmDelay = new MinMaxDelayBuilder()
                    .setMaxDelay(new Delay(la.getLinkMinMaxDelay().getMaxDelay().getValue()))
                    .setMinDelay(new Delay(la.getLinkMinMaxDelay().getMinDelay().getValue())).build();
            builder.setMinMaxDelay(mmDelay);
        }
        if (la.getDelayVariation() != null) {
            builder.setJitter(new Delay(la.getDelayVariation().getValue()));
        }
        if (la.getLinkLoss() != null) {
            builder.setLoss(new Loss(la.getLinkLoss().getValue()));
        }
        if (la.getAvailableBandwidth() != null) {
            builder.setAvailableBandwidth(bandwithToDecimalBandwidth(la.getAvailableBandwidth()));
        }
        if (la.getResidualBandwidth() != null) {
            builder.setResidualBandwidth(bandwithToDecimalBandwidth(la.getResidualBandwidth()));
        }
        if (la.getUtilizedBandwidth() != null) {
            builder.setUtilizedBandwidth(bandwithToDecimalBandwidth(la.getUtilizedBandwidth()));
        }
        if (la.getSharedRiskLinkGroups() != null) {
            List<Uint32> srlgs = new ArrayList<>();
            for (SrlgId srlg : la.getSharedRiskLinkGroups()) {
                srlgs.add(srlg.getValue());
            }
            builder.setSrlgs(srlgs);
        }
        for (SrAdjIds adj : la.nonnullSrAdjIds()) {
            if (adj.getSidLabelIndex() instanceof LocalLabelCase) {
                boolean backup = false;
                if (adj.getFlags() instanceof OspfAdjFlags) {
                    backup = ((OspfAdjFlags) adj.getFlags()).getBackup();
                }
                if (adj.getFlags() instanceof IsisAdjFlags) {
                    backup = ((IsisAdjFlags) adj.getFlags()).getBackup();
                }
                if (!backup) {
                    builder.setAdjSid(((LocalLabelCase) adj.getSidLabelIndex()).getLocalLabel().getValue());
                } else {
                    builder.setBackupAdjSid(((LocalLabelCase) adj.getSidLabelIndex()).getLocalLabel().getValue());
                }
            }
        }
        return builder.build();
    }

    /**
     * Get Node Attributes from Link State Route attributes.
     *
     * @param attributes  The attribute part from the Link State route
     *
     * @return Node Attributes
     */
    private static NodeAttributes getNodeAttributes(final Attributes attributes) {
        final NodeAttributes na;
        final Attributes1 attr = attributes.augmentation(Attributes1.class);
        if (attr != null) {
            final LinkStateAttribute attrType = attr.getLinkStateAttribute();
            if (attrType != null) {
                na = ((NodeAttributesCase) attrType).getNodeAttributes();
            } else {
                return null;
            }
        } else {
            return null;
        }
        return na;
    }

    /**
     * Create Vertex from the Node Attributes.
     *
     * @param na       Node Attributes
     * @param cvertex  Connected Vertex associated to this Vertex
     * @param as       As number
     *
     * @return New Vertex
     */
    private static Vertex getVertex(final NodeAttributes na, final Uint64 id, final Uint32 as) {
        VertexBuilder builder = new VertexBuilder().setVertexId(id).setAsn(as);
        if (na.getIpv4RouterId() != null) {
            builder.setRouterId(new IpAddress(na.getIpv4RouterId()));
        }
        if (na.getIpv6RouterId() != null) {
            builder.setRouterId(new IpAddress(na.getIpv6RouterId()));
        }
        /*
         * Set Router Name with dynamic hostname (IS-IS) or IPv4 address in dot decimal format (OSPF)
         */
        if (na.getDynamicHostname() != null) {
            builder.setName(na.getDynamicHostname());
        } else {
            int key = id.intValue();
            builder.setName(
                    (key << 24 & 0xFF) + "." + (key << 16 & 0xFF) + "." + (key << 8 & 0xFF) + "." + (key & 0xFF));
        }
        if (na.getSrCapabilities() != null) {
            final SidLabelIndex labelIndex = na.getSrCapabilities().getSidLabelIndex();
            if (labelIndex instanceof LocalLabelCase) {
                builder.setSrgb(new SrgbBuilder()
                        .setLowerBound(((LocalLabelCase) labelIndex).getLocalLabel().getValue())
                        .setRangeSize(na.getSrCapabilities().getRangeSize().getValue())
                        .build());
            } else if (labelIndex instanceof SidCase) {
                builder.setSrgb(new SrgbBuilder()
                        .setLowerBound(((SidCase) labelIndex).getSid())
                        .setRangeSize(na.getSrCapabilities().getRangeSize().getValue())
                        .build());
            }
        }
        if (na.getNodeFlags() != null) {
            if (na.getNodeFlags().getAbr()) {
                builder.setVertexType(VertexType.Abr);
            }
            if (na.getNodeFlags().getExternal()) {
                builder.setVertexType(VertexType.AsbrOut);
            }
        } else {
            builder.setVertexType(VertexType.Standard);
        }
        return builder.build();
    }

    /**
     * Create new Connected Vertex in the Connected Graph.
     *
     * @param value       The complete Linkstate route information
     * @param nodeCase    The node part of the Linkstate route
     * @param attributes  The node attributes
     */
    private void createVertex(final LinkstateRoute value, final NodeCase nodeCase, final Attributes attributes) {
        checkArgument(nodeCase != null, "Missing Node Case. Skip this Node");
        checkArgument(nodeCase.getNodeDescriptors() != null, "Missing Node Descriptors. Skip this Node");

        Uint64 vertexId = getVertexId(nodeCase.getNodeDescriptors().getCRouterIdentifier());
        if (vertexId == Uint64.ZERO) {
            LOG.warn("Unable to get Vertex Identifier from descriptor {}, skipping it", nodeCase.getNodeDescriptors());
            return;
        }

        NodeAttributes na = getNodeAttributes(attributes);
        if (na == null) {
            LOG.warn("Missing attributes in node {} route {}, skipping it", nodeCase, value);
            return;
        }

        Uint32 asNumber = Uint32.ZERO;
        if (nodeCase.getNodeDescriptors() != null) {
            asNumber = nodeCase.getNodeDescriptors().getAsNumber().getValue();
        }
        Vertex vertex = getVertex(na, vertexId, asNumber);

        /* Add the Connected Vertex and associated Vertex in the Graph */
        LOG.info("Add Vertex {} in TED[{}]", vertex.getName(), cgraph);
        cgraph.addVertex(vertex);
    }

    /**
     * Create new Prefix in the Connected Graph.
     *
     * @param value       The complete Linkstate route information
     * @param prefixCase  The Prefix part of the Linkstate route
     * @param attributes  The Prefix attributes
     */
    private void createPrefix(final LinkstateRoute value, final PrefixCase prefixCase, final Attributes attributes) {
        final IpPrefix ippfx = prefixCase.getPrefixDescriptors().getIpReachabilityInformation();
        if (ippfx == null) {
            LOG.warn("IP reachability not present in prefix {} route {}, skipping it", prefixCase, value);
            return;
        }

        /* Verify that all mandatory information are present */
        final PrefixAttributes pa;
        final Attributes1 attr = attributes.augmentation(Attributes1.class);
        if (attr != null) {
            final LinkStateAttribute attrType = attr.getLinkStateAttribute();
            if (attrType instanceof PrefixAttributesCase) {
                pa = ((PrefixAttributesCase) attrType).getPrefixAttributes();
            } else {
                LOG.warn("Missing attribute type in IP {} prefix {} route {}, skipping it", ippfx, prefixCase, value);
                return;
            }
        } else {
            LOG.warn("Missing attributes in IP {} prefix {} route {}, skipping it", ippfx, prefixCase, value);
            return;
        }

        /*
         * Get Connected Vertex from Connected Graph corresponding to the
         * Advertising Node Descriptor
         */
        Uint64 vertexId = getVertexId(prefixCase.getAdvertisingNodeDescriptors().getCRouterIdentifier());
        if (vertexId == Uint64.ZERO) {
            LOG.warn("Unable to get the Vertex Identifier from descriptor {}, skipping it",
                    prefixCase.getAdvertisingNodeDescriptors());
            return;
        }

        /* Create Prefix */
        PrefixBuilder builder = new PrefixBuilder().setVertexId(vertexId);
        if (pa.getSrPrefix() != null && pa.getSrPrefix().getSidLabelIndex() instanceof SidCase) {
            builder.setPrefixSid(((SidCase) pa.getSrPrefix().getSidLabelIndex()).getSid());
            if (pa.getSrPrefix().getFlags() instanceof IsisPrefixFlagsCase) {
                builder.setNodeSid(
                        ((IsisPrefixFlagsCase) pa.getSrPrefix().getFlags()).getIsisPrefixFlags().getNodeSid());
            } else {
                /*
                 * Seems that OSPF Flags are not accessible. Assuming that the
                 * Prefix is a Node SID
                 */
                builder.setNodeSid(true);
            }
        }
        if (ippfx.getIpv4Prefix() != null) {
            builder.setPrefix(new IpPrefix(ippfx.getIpv4Prefix()));
        }
        if (ippfx.getIpv6Prefix() != null) {
            builder.setPrefix(new IpPrefix(ippfx.getIpv6Prefix()));
        }
        Prefix prefix = builder.build();

        /* Add the Prefix to the Connected Vertex within the Connected Graph */
        LOG.info("Add prefix {} in TED[{}]", builder.getPrefix(), cgraph);<commit_msg>Remove unneeded wrapping<commit_after>/*
 * Copyright (c) 2020 Orange. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */
package org.opendaylight.bgpcep.bgp.topology.provider;

import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;

import com.google.common.annotations.VisibleForTesting;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import org.opendaylight.graph.ConnectedGraph;
import org.opendaylight.graph.ConnectedGraphProvider;
import org.opendaylight.mdsal.binding.api.DataBroker;
import org.opendaylight.mdsal.binding.api.ReadWriteTransaction;
import org.opendaylight.protocol.bgp.rib.RibReference;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.Ipv4InterfaceIdentifier;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.Ipv6InterfaceIdentifier;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.LinkstateAddressFamily;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.LinkstateSubsequentAddressFamily;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.bgp.rib.rib.loc.rib.tables.routes.LinkstateRoutesCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.ObjectType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.attribute.SrAdjIds;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.object.type.LinkCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.object.type.NodeCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.object.type.PrefixCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.object.type.link._case.LinkDescriptors;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.path.attribute.LinkStateAttribute;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.path.attribute.link.state.attribute.LinkAttributesCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.path.attribute.link.state.attribute.NodeAttributesCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.path.attribute.link.state.attribute.PrefixAttributesCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.path.attribute.link.state.attribute.link.attributes._case.LinkAttributes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.path.attribute.link.state.attribute.node.attributes._case.NodeAttributes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.path.attribute.link.state.attribute.prefix.attributes._case.PrefixAttributes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.routes.LinkstateRoutes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.routes.linkstate.routes.LinkstateRoute;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.linkstate.routes.linkstate.routes.linkstate.route.Attributes1;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.node.identifier.CRouterIdentifier;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.node.identifier.c.router.identifier.IsisNodeCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120.node.identifier.c.router.identifier.OspfNodeCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.path.attributes.Attributes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.segment.routing.ext.rev200120.adj.flags.flags.isis.adj.flags._case.IsisAdjFlags;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.segment.routing.ext.rev200120.adj.flags.flags.ospf.adj.flags._case.OspfAdjFlags;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.segment.routing.ext.rev200120.prefix.sid.tlv.flags.IsisPrefixFlagsCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.segment.routing.ext.rev200120.sid.label.index.SidLabelIndex;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.segment.routing.ext.rev200120.sid.label.index.sid.label.index.LocalLabelCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.segment.routing.ext.rev200120.sid.label.index.sid.label.index.SidCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.DecimalBandwidth;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.Delay;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.Loss;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.Vertex.VertexType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.edge.EdgeAttributes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.edge.EdgeAttributesBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.edge.attributes.MinMaxDelay;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.edge.attributes.MinMaxDelayBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.edge.attributes.UnreservedBandwidth;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.edge.attributes.UnreservedBandwidthBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.edge.attributes.UnreservedBandwidthKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.Graph.DomainScope;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Edge;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.EdgeBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.EdgeKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Prefix;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.PrefixBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.Vertex;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.VertexBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.VertexKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.vertex.SrgbBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.network.concepts.rev131125.Bandwidth;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.odl.bgp.topology.types.rev160524.TopologyTypes1Builder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.odl.bgp.topology.types.rev160524.bgp.linkstate.topology.type.BgpLinkstateTopologyBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.rsvp.rev150820.SrlgId;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.TopologyTypes;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.TopologyTypesBuilder;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.Uint32;
import org.opendaylight.yangtools.yang.common.Uint64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This Class build the Traffic Engineering Database as a Connected Graph
 * suitable to be used latter by Path Computation algorithms to compute end to
 * end path.
 *
 * @author <NAME>
 * @author <NAME>
 */
public class LinkstateGraphBuilder extends AbstractTopologyBuilder<LinkstateRoute> {
    private static final TopologyTypes LINKSTATE_TOPOLOGY_TYPE = new TopologyTypesBuilder().addAugmentation(
            new TopologyTypes1Builder().setBgpLinkstateTopology(new BgpLinkstateTopologyBuilder().build()).build())
            .build();

    private static final String UNHANDLED_OBJECT_CLASS = "Unhandled object class {}";

    private static final Logger LOG = LoggerFactory.getLogger(LinkstateGraphBuilder.class);

    private final ConnectedGraph cgraph;

    public LinkstateGraphBuilder(final DataBroker dataProvider, final RibReference locRibReference,
            final TopologyId topologyId, final ConnectedGraphProvider provider) {
        super(dataProvider, locRibReference, topologyId, LINKSTATE_TOPOLOGY_TYPE, LinkstateAddressFamily.class,
                LinkstateSubsequentAddressFamily.class);
        this.cgraph = requireNonNull(provider).createConnectedGraph("ted://" + topologyId.getValue(),
                DomainScope.IntraDomain);
        /* LinkStateGraphBuilder doesn't write information in the Network Topology tree of the Data Store.
         * This is performed by ConnectedGraphProvider which write element in Graph tree of the Data Store */
        this.networkTopologyTransaction = false;
        LOG.info("Started Traffic Engineering Graph Builder");
    }

    @VisibleForTesting
    LinkstateGraphBuilder(final DataBroker dataProvider, final RibReference locRibReference,
            final TopologyId topologyId, final ConnectedGraphProvider provider, final long listenerResetLimitInMillsec,
            final int listenerResetEnforceCounter) {
        super(dataProvider, locRibReference, topologyId, LINKSTATE_TOPOLOGY_TYPE, LinkstateAddressFamily.class,
                LinkstateSubsequentAddressFamily.class, listenerResetLimitInMillsec, listenerResetEnforceCounter);
        this.cgraph = requireNonNull(provider).createConnectedGraph("ted://" + topologyId.getValue(),
                DomainScope.IntraDomain);
        /* LinkStateGraphBuilder doesn't write information in the Network Topology tree of the Data Store.
         * This is performed by ConnectedGraphProvider which write element in Graph tree of the Data Store */
        this.networkTopologyTransaction = false;
        LOG.info("Started Traffic Engineering Graph Builder");
    }

    @Override
    protected void createObject(final ReadWriteTransaction trans, final InstanceIdentifier<LinkstateRoute> id,
            final LinkstateRoute value) {
        final ObjectType t = value.getObjectType();
        checkArgument(t != null, "Route %s value %s has null object type", id, value);

        if (t instanceof LinkCase) {
            createEdge(value, (LinkCase) t, value.getAttributes());
        } else if (t instanceof NodeCase) {
            createVertex(value, (NodeCase) t, value.getAttributes());
        } else if (t instanceof PrefixCase) {
            createPrefix(value, (PrefixCase) t, value.getAttributes());
        } else {
            LOG.debug(UNHANDLED_OBJECT_CLASS, t.implementedInterface());
        }
    }

    /**
     * Verify that mandatory information (Local & Remote Node and Link
     * descriptors) are present in the link.
     *
     * @param linkCase  The Link part of the Linkstate route
     *
     * @return True if all information are present, false otherwise
     */
    private static boolean checkLinkState(final LinkCase linkCase) {
        if (linkCase.getLocalNodeDescriptors() == null || linkCase.getRemoteNodeDescriptors() == null) {
            LOG.warn("Missing Local or Remote Node descriptor in link {}, skipping it", linkCase);
            return false;
        }
        if (linkCase.getLinkDescriptors() == null) {
            LOG.warn("Missing Link descriptor in link {}, skipping it", linkCase);
            return false;
        }
        return true;
    }

    /**
     * Get Link attributes from the Link State route.
     *
     * @param attributes  Link State Route Attributes
     *
     * @return Link Attributes
     */
    private static LinkAttributes getLinkAttributes(final Attributes attributes) {
        final Attributes1 attr = attributes.augmentation(Attributes1.class);
        if (attr != null) {
            final LinkStateAttribute attrType = attr.getLinkStateAttribute();
            if (attrType instanceof LinkAttributesCase) {
                return ((LinkAttributesCase) attrType).getLinkAttributes();
            }
        }
        return null;
    }

    /**
     * Determine the Source Edge Key from the link descriptor.
     * There is several case: IPv4, IPv6 address or Unnumbered Interface.
     *
     * @param linkCase The Link part of the Linkstate route
     *
     * @return Unique key
     */
    private static Uint64 getEdgeId(final LinkCase linkCase) {
        Uint64 key = Uint64.ZERO;
        final LinkDescriptors linkDescriptors = linkCase.getLinkDescriptors();
        if (linkDescriptors.getIpv4InterfaceAddress() != null) {
            key = ipv4ToKey(linkDescriptors.getIpv4InterfaceAddress());
        }
        if (linkDescriptors.getIpv6InterfaceAddress() != null) {
            key = ipv6ToKey(linkDescriptors.getIpv6InterfaceAddress());
        }
        if (linkDescriptors.getLinkLocalIdentifier() != null) {
            key = linkDescriptors.getLinkLocalIdentifier().toUint64();
        }
        return key;
    }

    /**
     * Create new Connected Edge in the Connected Graph.
     *
     * @param value       The complete Linkstate route information
     * @param linkCase    The Link part of the Linkstate route
     * @param attributes  The Link attributes
     *
     */
    private void createEdge(final LinkstateRoute value, final LinkCase linkCase, final Attributes attributes) {
        checkArgument(checkLinkState(linkCase), "Missing mandatory information in link {}", linkCase);

        final LinkAttributes la = getLinkAttributes(attributes);
        if (la == null) {
            LOG.warn("Missing attributes in link {} route {}, skipping it", linkCase, value);
            return;
        }

        /* Get Source and Destination Vertex from the graph */
        Uint64 srcId = getVertexId(linkCase.getLocalNodeDescriptors().getCRouterIdentifier());
        Uint64 dstId = getVertexId(linkCase.getRemoteNodeDescriptors().getCRouterIdentifier());
        if (srcId == Uint64.ZERO || dstId == Uint64.ZERO) {
            LOG.warn("Unable to get the Source or Destination Vertex Identifier from link {}, skipping it", linkCase);
            return;
        }

        /* Get Source and Destination Key for the corresponding Edge */
        Uint64 edgeId = getEdgeId(linkCase);
        if (edgeId == Uint64.ZERO) {
            LOG.warn("Unable to get the Edge Identifier from link {}, skipping it", linkCase);
            return;
        }

        /* Add associated Edge */
        Edge edge = new EdgeBuilder().setEdgeId(edgeId).setLocalVertexId(srcId).setRemoteVertexId(dstId)
                .setName(srcId + " - " + dstId)
                .setEdgeAttributes(createEdgeAttributes(la, linkCase.getLinkDescriptors())).build();

        /*
         * Add corresponding Prefix for the Local Address. Remote address will be added with the remote Edge */
        final var localAddress = edge.getEdgeAttributes().getLocalAddress();
        PrefixBuilder prefBuilder = new PrefixBuilder().setVertexId(srcId);
        if (localAddress.getIpv4Address() != null) {
            prefBuilder.setPrefix(new IpPrefix(IetfInetUtil.INSTANCE.ipv4PrefixFor(localAddress.getIpv4Address())));
        }
        if (localAddress.getIpv6Address() != null) {
            prefBuilder.setPrefix(new IpPrefix(IetfInetUtil.INSTANCE.ipv6PrefixFor(localAddress.getIpv6Address())));
        }
        Prefix prefix = prefBuilder.build();

        /* Add the Edge in the Connected Graph */
        LOG.info("Add Edge {} and associated Prefix {} in TED[{}]", edge.getName(), prefix.getPrefix(), cgraph);
        cgraph.addEdge(edge);
        cgraph.addPrefix(prefix);
    }

    private static final int MAX_PRIORITY = 8;

    /**
     * Create Edge Attributes from Link attributes.
     *
     * @param la         Linkstate Attributes
     * @param linkDesc   Linkstate Descriptors
     *
     * @return EdgeAttributes
     */
    private static EdgeAttributes createEdgeAttributes(final LinkAttributes la, final LinkDescriptors linkDesc) {
        EdgeAttributesBuilder builder = new EdgeAttributesBuilder();

        if (linkDesc.getIpv4InterfaceAddress() != null) {
            builder.setLocalAddress(new IpAddress(new Ipv4Address(linkDesc.getIpv4InterfaceAddress())));
        }
        if (linkDesc.getIpv6InterfaceAddress() != null) {
            builder.setLocalAddress(new IpAddress(new Ipv6Address(linkDesc.getIpv6InterfaceAddress())));
        }
        if (linkDesc.getIpv4NeighborAddress() != null) {
            builder.setRemoteAddress(new IpAddress(new Ipv4Address(linkDesc.getIpv4NeighborAddress())));
        }
        if (linkDesc.getIpv6NeighborAddress() != null) {
            builder.setRemoteAddress(new IpAddress(new Ipv6Address(linkDesc.getIpv6NeighborAddress())));
        }
        if (linkDesc.getLinkLocalIdentifier() != null) {
            builder.setLocalIdentifier(linkDesc.getLinkLocalIdentifier());
        }
        if (linkDesc.getLinkRemoteIdentifier() != null) {
            builder.setRemoteIdentifier(linkDesc.getLinkRemoteIdentifier());
        }
        if (la.getMetric() != null) {
            builder.setMetric(la.getMetric().getValue());
        }
        if (la.getTeMetric() != null) {
            builder.setTeMetric(la.getTeMetric().getValue());
        }
        if (la.getMaxLinkBandwidth() != null) {
            builder.setMaxLinkBandwidth(bandwithToDecimalBandwidth(la.getMaxLinkBandwidth()));
        }
        if (la.getMaxReservableBandwidth() != null) {
            builder.setMaxResvLinkBandwidth(bandwithToDecimalBandwidth(la.getMaxReservableBandwidth()));
        }
        if (la.getUnreservedBandwidth() != null) {
            int upperBound = Math.min(la.getUnreservedBandwidth().size(), MAX_PRIORITY);
            final List<UnreservedBandwidth> unRsvBw = new ArrayList<>(upperBound);

            for (final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev200120
                    .UnreservedBandwidth bandwidth : la.nonnullUnreservedBandwidth().values()) {
                unRsvBw.add(new UnreservedBandwidthBuilder()
                        .setBandwidth(bandwithToDecimalBandwidth(bandwidth.getBandwidth()))
                        .withKey(new UnreservedBandwidthKey(bandwidth.getPriority())).build());
            }
            builder.setUnreservedBandwidth(unRsvBw);
        }
        if (la.getAdminGroup() != null) {
            builder.setAdminGroup(la.getAdminGroup().getValue());
        }
        if (la.getLinkDelay() != null) {
            builder.setDelay(new Delay(la.getLinkDelay().getValue()));
        }
        if (la.getLinkMinMaxDelay() != null && la.getLinkMinMaxDelay() != null) {
            MinMaxDelay mmDelay = new MinMaxDelayBuilder()
                    .setMaxDelay(new Delay(la.getLinkMinMaxDelay().getMaxDelay().getValue()))
                    .setMinDelay(new Delay(la.getLinkMinMaxDelay().getMinDelay().getValue())).build();
            builder.setMinMaxDelay(mmDelay);
        }
        if (la.getDelayVariation() != null) {
            builder.setJitter(new Delay(la.getDelayVariation().getValue()));
        }
        if (la.getLinkLoss() != null) {
            builder.setLoss(new Loss(la.getLinkLoss().getValue()));
        }
        if (la.getAvailableBandwidth() != null) {
            builder.setAvailableBandwidth(bandwithToDecimalBandwidth(la.getAvailableBandwidth()));
        }
        if (la.getResidualBandwidth() != null) {
            builder.setResidualBandwidth(bandwithToDecimalBandwidth(la.getResidualBandwidth()));
        }
        if (la.getUtilizedBandwidth() != null) {
            builder.setUtilizedBandwidth(bandwithToDecimalBandwidth(la.getUtilizedBandwidth()));
        }
        if (la.getSharedRiskLinkGroups() != null) {
            List<Uint32> srlgs = new ArrayList<>();
            for (SrlgId srlg : la.getSharedRiskLinkGroups()) {
                srlgs.add(srlg.getValue());
            }
            builder.setSrlgs(srlgs);
        }
        for (SrAdjIds adj : la.nonnullSrAdjIds()) {
            if (adj.getSidLabelIndex() instanceof LocalLabelCase) {
                boolean backup = false;
                if (adj.getFlags() instanceof OspfAdjFlags) {
                    backup = ((OspfAdjFlags) adj.getFlags()).getBackup();
                }
                if (adj.getFlags() instanceof IsisAdjFlags) {
                    backup = ((IsisAdjFlags) adj.getFlags()).getBackup();
                }
                if (!backup) {
                    builder.setAdjSid(((LocalLabelCase) adj.getSidLabelIndex()).getLocalLabel().getValue());
                } else {
                    builder.setBackupAdjSid(((LocalLabelCase) adj.getSidLabelIndex()).getLocalLabel().getValue());
                }
            }
        }
        return builder.build();
    }

    /**
     * Get Node Attributes from Link State Route attributes.
     *
     * @param attributes  The attribute part from the Link State route
     *
     * @return Node Attributes
     */
    private static NodeAttributes getNodeAttributes(final Attributes attributes) {
        final NodeAttributes na;
        final Attributes1 attr = attributes.augmentation(Attributes1.class);
        if (attr != null) {
            final LinkStateAttribute attrType = attr.getLinkStateAttribute();
            if (attrType != null) {
                na = ((NodeAttributesCase) attrType).getNodeAttributes();
            } else {
                return null;
            }
        } else {
            return null;
        }
        return na;
    }

    /**
     * Create Vertex from the Node Attributes.
     *
     * @param na       Node Attributes
     * @param cvertex  Connected Vertex associated to this Vertex
     * @param as       As number
     *
     * @return New Vertex
     */
    private static Vertex getVertex(final NodeAttributes na, final Uint64 id, final Uint32 as) {
        VertexBuilder builder = new VertexBuilder().setVertexId(id).setAsn(as);
        if (na.getIpv4RouterId() != null) {
            builder.setRouterId(new IpAddress(na.getIpv4RouterId()));
        }
        if (na.getIpv6RouterId() != null) {
            builder.setRouterId(new IpAddress(na.getIpv6RouterId()));
        }
        /*
         * Set Router Name with dynamic hostname (IS-IS) or IPv4 address in dot decimal format (OSPF)
         */
        if (na.getDynamicHostname() != null) {
            builder.setName(na.getDynamicHostname());
        } else {
            int key = id.intValue();
            builder.setName(
                    (key << 24 & 0xFF) + "." + (key << 16 & 0xFF) + "." + (key << 8 & 0xFF) + "." + (key & 0xFF));
        }
        if (na.getSrCapabilities() != null) {
            final SidLabelIndex labelIndex = na.getSrCapabilities().getSidLabelIndex();
            if (labelIndex instanceof LocalLabelCase) {
                builder.setSrgb(new SrgbBuilder()
                        .setLowerBound(((LocalLabelCase) labelIndex).getLocalLabel().getValue())
                        .setRangeSize(na.getSrCapabilities().getRangeSize().getValue())
                        .build());
            } else if (labelIndex instanceof SidCase) {
                builder.setSrgb(new SrgbBuilder()
                        .setLowerBound(((SidCase) labelIndex).getSid())
                        .setRangeSize(na.getSrCapabilities().getRangeSize().getValue())
                        .build());
            }
        }
        if (na.getNodeFlags() != null) {
            if (na.getNodeFlags().getAbr()) {
                builder.setVertexType(VertexType.Abr);
            }
            if (na.getNodeFlags().getExternal()) {
                builder.setVertexType(VertexType.AsbrOut);
            }
        } else {
            builder.setVertexType(VertexType.Standard);
        }
        return builder.build();
    }

    /**
     * Create new Connected Vertex in the Connected Graph.
     *
     * @param value       The complete Linkstate route information
     * @param nodeCase    The node part of the Linkstate route
     * @param attributes  The node attributes
     */
    private void createVertex(final LinkstateRoute value, final NodeCase nodeCase, final Attributes attributes) {
        checkArgument(nodeCase != null, "Missing Node Case. Skip this Node");
        checkArgument(nodeCase.getNodeDescriptors() != null, "Missing Node Descriptors. Skip this Node");

        Uint64 vertexId = getVertexId(nodeCase.getNodeDescriptors().getCRouterIdentifier());
        if (vertexId == Uint64.ZERO) {
            LOG.warn("Unable to get Vertex Identifier from descriptor {}, skipping it", nodeCase.getNodeDescriptors());
            return;
        }

        NodeAttributes na = getNodeAttributes(attributes);
        if (na == null) {
            LOG.warn("Missing attributes in node {} route {}, skipping it", nodeCase, value);
            return;
        }

        Uint32 asNumber = Uint32.ZERO;
        if (nodeCase.getNodeDescriptors() != null) {
            asNumber = nodeCase.getNodeDescriptors().getAsNumber().getValue();
        }
        Vertex vertex = getVertex(na, vertexId, asNumber);

        /* Add the Connected Vertex and associated Vertex in the Graph */
        LOG.info("Add Vertex {} in TED[{}]", vertex.getName(), cgraph);
        cgraph.addVertex(vertex);
    }

    /**
     * Create new Prefix in the Connected Graph.
     *
     * @param value       The complete Linkstate route information
     * @param prefixCase  The Prefix part of the Linkstate route
     * @param attributes  The Prefix attributes
     */
    private void createPrefix(final LinkstateRoute value, final PrefixCase prefixCase, final Attributes attributes) {
        final IpPrefix ippfx = prefixCase.getPrefixDescriptors().getIpReachabilityInformation();
        if (ippfx == null) {
            LOG.warn("IP reachability not present in prefix {} route {}, skipping it", prefixCase, value);
            return;
        }

        /* Verify that all mandatory information are present */
        final PrefixAttributes pa;
        final Attributes1 attr = attributes.augmentation(Attributes1.class);
        if (attr != null) {
            final LinkStateAttribute attrType = attr.getLinkStateAttribute();
            if (attrType instanceof PrefixAttributesCase) {
                pa = ((PrefixAttributesCase) attrType).getPrefixAttributes();
            } else {
                LOG.warn("Missing attribute type in IP {} prefix {} route {}, skipping it", ippfx, prefixCase, value);
                return;
            }
        } else {
            LOG.warn("Missing attributes in IP {} prefix {} route {}, skipping it", ippfx, prefixCase, value);
            return;
        }

        /*
         * Get Connected Vertex from Connected Graph corresponding to the
         * Advertising Node Descriptor
         */
        Uint64 vertexId = getVertexId(prefixCase.getAdvertisingNodeDescriptors().getCRouterIdentifier());
        if (vertexId == Uint64.ZERO) {
            LOG.warn("Unable to get the Vertex Identifier from descriptor {}, skipping it",
                    prefixCase.getAdvertisingNodeDescriptors());
            return;
        }

        /* Create Prefix */
        PrefixBuilder builder = new PrefixBuilder().setVertexId(vertexId).setPrefix(ippfx);
        if (pa.getSrPrefix() != null && pa.getSrPrefix().getSidLabelIndex() instanceof SidCase) {
            builder.setPrefixSid(((SidCase) pa.getSrPrefix().getSidLabelIndex()).getSid());
            if (pa.getSrPrefix().getFlags() instanceof IsisPrefixFlagsCase) {
                builder.setNodeSid(
                        ((IsisPrefixFlagsCase) pa.getSrPrefix().getFlags()).getIsisPrefixFlags().getNodeSid());
            } else {
                /*
                 * Seems that OSPF Flags are not accessible. Assuming that the
                 * Prefix is a Node SID
                 */
                builder.setNodeSid(true);
            }
        }
        Prefix prefix = builder.build();

        /* Add the Prefix to the Connected Vertex within the Connected Graph */
        LOG.info("Add prefix {} in TED[{}]", builder.getPrefix(), cgraph);
{'fraction_non_alphanumeric': 0.11082482686185052, 'fraction_numerical': 0.018708766862506974, 'mean_word_length': 3.0461487383798143, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 132}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2567841', 'n_tokens_mistral': 19222, 'n_tokens_neox': 18151, 'n_words': 3045}