+ private static final class TpHolder {
+ private final Set<LinkId> local = new HashSet<>();
+ private final Set<LinkId> remote = new HashSet<>();
+
+ private final TerminationPoint tp;
+
+ private TpHolder(final TerminationPoint tp) {
+ this.tp = Preconditions.checkNotNull(tp);
+ }
+
+ private synchronized void addLink(final LinkId id, final boolean isRemote) {
+ if (isRemote) {
+ this.remote.add(id);
+ } else {
+ this.local.add(id);
+ }
+ }
+
+ private synchronized boolean removeLink(final LinkId id, final boolean isRemote) {
+ final boolean removed;
+ if (isRemote) {
+ removed = this.remote.remove(id);
+ } else {
+ removed = this.local.remove(id);
+ }
+ if (!removed) {
+ LOG.warn("Removed non-reference link {} from TP {} isRemote {}", this.tp.getTpId(), id, isRemote);
+ }
+
+ return this.local.isEmpty() && this.remote.isEmpty();
+ }
+
+ private TerminationPoint getTp() {
+ return this.tp;
+ }
+ }
+
+ private final class NodeHolder {
+ private final Map<PrefixKey, Prefix> prefixes = new HashMap<>();
+ private final Map<TpId, TpHolder> tps = new HashMap<>();
+ private boolean advertized = false;
+ private IgpNodeAttributesBuilder inab;
+ private NodeBuilder nb;
+
+ private NodeHolder(final NodeId id) {
+ this.inab = new IgpNodeAttributesBuilder();
+ this.nb = new NodeBuilder().setKey(new NodeKey(id)).setNodeId(id);
+ }
+
+ /**
+ * Synchronized in-core state of a node into the backing store using the transaction
+ *
+ * @param trans data modification transaction which to use
+ * @return True if the node has been purged, false otherwise.
+ */
+ private boolean syncState(final DataModification<InstanceIdentifier<?>, DataObject> trans) {
+ final InstanceIdentifier<Node> nid = getInstanceIdentifier().child(
+ org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node.class,
+ this.nb.getKey());
+
+ /*
+ * Transaction's putOperationalData() does a merge. Force it onto a replace
+ * by removing the data. If we decide to remove the node -- we just skip the put.
+ */
+ trans.removeOperationalData(nid);
+
+ if (!this.advertized) {
+ if (this.tps.isEmpty() && this.prefixes.isEmpty()) {
+ LOG.debug("Removing unadvertized unused node {}", this.nb.getNodeId());
+ return true;
+ }
+
+ LOG.debug("Node {} is still implied by {} TPs and {} prefixes", this.nb.getNodeId(), this.tps.size(), this.prefixes.size());
+ }
+
+ // Re-generate termination points
+ this.nb.setTerminationPoint(Lists.newArrayList(Collections2.transform(this.tps.values(),
+ new Function<TpHolder, TerminationPoint>() {
+ @Override
+ public TerminationPoint apply(final TpHolder input) {
+ return input.getTp();
+ }
+ })));
+
+ // Re-generate prefixes
+ this.inab.setPrefix(Lists.newArrayList(this.prefixes.values()));
+
+ // Write the node out
+ final Node n = this.nb.addAugmentation(Node1.class, new Node1Builder().setIgpNodeAttributes(this.inab.build()).build()).build();
+ trans.putOperationalData(nid, n);
+ LOG.debug("Created node {} at {}", n, nid);
+ return false;
+ }
+
+ private synchronized void removeTp(final TpId tp, final LinkId link, final boolean isRemote) {
+ final TpHolder h = this.tps.get(tp);
+ if (h != null) {
+ if (h.removeLink(link, isRemote)) {
+ this.tps.remove(tp);
+ LOG.debug("Removed TP {}", tp);
+ }
+ } else {
+ LOG.warn("Removed non-present TP {} by link {}", tp, link);
+ }
+ }
+
+ private void addTp(final TerminationPoint tp, final LinkId link, final boolean isRemote) {
+ TpHolder h = this.tps.get(tp.getTpId());
+ if (h == null) {
+ h = new TpHolder(tp);
+ this.tps.put(tp.getTpId(), h);
+ }
+
+ h.addLink(link, isRemote);
+ }
+
+ private void addPrefix(final Prefix pfx) {
+ this.prefixes.put(pfx.getKey(), pfx);
+ }
+
+ private void removePrefix(final PrefixCase p) {
+ this.prefixes.remove(new PrefixKey(p.getIpReachabilityInformation()));
+ }
+
+ private void unadvertized() {
+ this.inab = new IgpNodeAttributesBuilder();
+ this.nb = new NodeBuilder().setKey(this.nb.getKey()).setNodeId(this.nb.getNodeId());
+ this.advertized = false;
+ LOG.debug("Node {} is unadvertized", this.nb.getNodeId());
+ }
+
+ private void advertized(final NodeBuilder nb, final IgpNodeAttributesBuilder inab) {
+ this.nb = Preconditions.checkNotNull(nb);
+ this.inab = Preconditions.checkNotNull(inab);
+ this.advertized = true;
+ LOG.debug("Node {} is advertized", nb.getNodeId());
+ }
+
+ private Object getNodeId() {
+ return this.nb.getNodeId();
+ }
+ }
+