ONOS-2186 - GUI Topo Overlay - (WIP)
- split BiLink into abstract superclass and concrete subclasses.
- created BiLinkMap to collate bilinks (and derivative subclasses).
- added missing Javadocs, and other general cleanup.

Change-Id: Icfa85bc44a223c6cf245a4005170583dad1cc801
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/BaseLink.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/BaseLink.java
new file mode 100644
index 0000000..043b471
--- /dev/null
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/BaseLink.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.onosproject.ui.impl.topo;
+
+import org.onosproject.net.Link;
+import org.onosproject.net.LinkKey;
+import org.onosproject.ui.topo.LinkHighlight;
+
+/**
+ * A simple concrete implementation of a {@link BiLink}.
+ * Note that this implementation does not generate any link highlights.
+ */
+public class BaseLink extends BiLink {
+
+    /**
+     * Constructs a base link for the given key and initial link.
+     *
+     * @param key  canonical key for this base link
+     * @param link first link
+     */
+    public BaseLink(LinkKey key, Link link) {
+        super(key, link);
+    }
+
+    @Override
+    public LinkHighlight highlight(Enum<?> type) {
+        return null;
+    }
+}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/LinkStatsType.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/BaseLinkMap.java
similarity index 66%
copy from web/gui/src/main/java/org/onosproject/ui/impl/topo/LinkStatsType.java
copy to web/gui/src/main/java/org/onosproject/ui/impl/topo/BaseLinkMap.java
index 589cddd..14c66ea 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/LinkStatsType.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/BaseLinkMap.java
@@ -17,27 +17,15 @@
 
 package org.onosproject.ui.impl.topo;
 
+import org.onosproject.net.Link;
+import org.onosproject.net.LinkKey;
+
 /**
- * Designates type of stats to report on a highlighted link.
+ * Collection of {@link BaseLink}s.
  */
-public enum LinkStatsType {
-    /**
-     * Number of flows.
-     */
-    FLOW_COUNT,
-
-    /**
-     * Number of bytes.
-     */
-    FLOW_STATS,
-
-    /**
-     * Number of bits per second.
-     */
-    PORT_STATS,
-
-    /**
-     * Custom tagged information.
-     */
-    TAGGED
+public class BaseLinkMap extends BiLinkMap<BaseLink> {
+    @Override
+    public BaseLink create(LinkKey key, Link link) {
+        return new BaseLink(key, link);
+    }
 }
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/BiLink.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/BiLink.java
index 5ab4e0e..8ccf543 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/BiLink.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/BiLink.java
@@ -19,226 +19,87 @@
 
 import org.onosproject.net.Link;
 import org.onosproject.net.LinkKey;
-import org.onosproject.net.statistic.Load;
 import org.onosproject.ui.topo.LinkHighlight;
 
-import static org.onosproject.ui.topo.LinkHighlight.Flavor.NO_HIGHLIGHT;
-import static org.onosproject.ui.topo.LinkHighlight.Flavor.PRIMARY_HIGHLIGHT;
-import static org.onosproject.ui.topo.LinkHighlight.Flavor.SECONDARY_HIGHLIGHT;
+import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
- * Representation of a link and its inverse, and any associated traffic data.
- * This class understands how to generate {@link LinkHighlight}s for sending
- * back to the topology view.
+ * Representation of a link and its inverse, as a partial implementation.
+ * <p>
+ * Subclasses will decide how to generate the link highlighting (coloring
+ * and labeling) for the topology view.
  */
-public class BiLink {
-
-    private static final String EMPTY = "";
+public abstract class BiLink {
 
     private final LinkKey key;
     private final Link one;
     private Link two;
 
-    private boolean hasTraffic = false;
-    private long bytes = 0;
-    private long rate = 0;
-    private long flows = 0;
-    private boolean isOptical = false;
-    private LinkHighlight.Flavor taggedFlavor = NO_HIGHLIGHT;
-    private boolean antMarch = false;
-
     /**
-     * Constructs a bilink for the given key and initial link.
+     * Constructs a bi-link for the given key and initial link. It is expected
+     * that the caller will have used {@link TopoUtils#canonicalLinkKey(Link)}
+     * to generate the key.
      *
-     * @param key canonical key for this bilink
+     * @param key canonical key for this bi-link
      * @param link first link
      */
     public BiLink(LinkKey key, Link link) {
-        this.key = key;
-        this.one = link;
+        this.key = checkNotNull(key);
+        this.one = checkNotNull(link);
     }
 
     /**
-     * Sets the second link for this bilink.
+     * Sets the second link for this bi-link.
      *
      * @param link second link
      */
     public void setOther(Link link) {
-        this.two = link;
+        this.two = checkNotNull(link);
     }
 
     /**
-     * Sets the optical flag to the given value.
+     * Returns the link identifier in the form expected on the Topology View
+     * in the web client.
      *
-     * @param b true if an optical link
+     * @return link identifier
      */
-    public void setOptical(boolean b) {
-        isOptical = b;
-    }
-
-    /**
-     * Sets the ant march flag to the given value.
-     *
-     * @param b true if marching ants required
-     */
-    public void setAntMarch(boolean b) {
-        antMarch = b;
-    }
-
-    /**
-     * Tags this bilink with a link flavor to be used in visual rendering.
-     *
-     * @param flavor the flavor to tag
-     */
-    public void tagFlavor(LinkHighlight.Flavor flavor) {
-        this.taggedFlavor = flavor;
-    }
-
-    /**
-     * Adds load statistics, marks the bilink as having traffic.
-     *
-     * @param load load to add
-     */
-    public void addLoad(Load load) {
-        addLoad(load, 0);
-    }
-
-    /**
-     * Adds load statistics, marks the bilink as having traffic, if the
-     * load rate is greater than the given threshold.
-     *
-     * @param load load to add
-     * @param threshold threshold to register traffic
-     */
-    public void addLoad(Load load, double threshold) {
-        if (load != null) {
-            this.hasTraffic = hasTraffic || load.rate() > threshold;
-            this.bytes += load.latest();
-            this.rate += load.rate();
-        }
-    }
-
-    /**
-     * Adds the given count of flows to this bilink.
-     *
-     * @param count count of flows
-     */
-    public void addFlows(int count) {
-        this.flows += count;
-    }
-
-    /**
-     * Generates a link highlight entity, based on state of this bilink.
-     *
-     * @param type the type of statistics to use to interpret the data
-     * @return link highlight data for this bilink
-     */
-    public LinkHighlight generateHighlight(LinkStatsType type) {
-        switch (type) {
-            case FLOW_COUNT:
-                return highlightForFlowCount(type);
-
-            case FLOW_STATS:
-            case PORT_STATS:
-                return highlightForStats(type);
-
-            case TAGGED:
-                return highlightForTagging(type);
-
-            default:
-                throw new IllegalStateException("unexpected case: " + type);
-        }
-    }
-
-    private LinkHighlight highlightForStats(LinkStatsType type) {
-        return new LinkHighlight(linkId(), SECONDARY_HIGHLIGHT)
-                .setLabel(generateLabel(type));
-    }
-
-    private LinkHighlight highlightForFlowCount(LinkStatsType type) {
-        LinkHighlight.Flavor flavor = flows() > 0 ?
-                PRIMARY_HIGHLIGHT : SECONDARY_HIGHLIGHT;
-        return new LinkHighlight(linkId(), flavor)
-                .setLabel(generateLabel(type));
-    }
-
-    private LinkHighlight highlightForTagging(LinkStatsType type) {
-        LinkHighlight hlite = new LinkHighlight(linkId(), flavor())
-                .setLabel(generateLabel(type));
-        if (isOptical()) {
-            hlite.addMod(LinkHighlight.MOD_OPTICAL);
-        }
-        if (isAntMarch()) {
-            hlite.addMod(LinkHighlight.MOD_ANIMATED);
-        }
-        return hlite;
-    }
-
-    // Generates a link identifier in the form that the Topology View on the
-    private String linkId() {
+    public String linkId() {
         return TopoUtils.compactLinkString(one);
     }
 
-    // Generates a string representation of the load, to be used as a label
-    private String generateLabel(LinkStatsType type) {
-        switch (type) {
-            case FLOW_COUNT:
-                return TopoUtils.formatFlows(flows());
-
-            case FLOW_STATS:
-                return TopoUtils.formatBytes(bytes());
-
-            case PORT_STATS:
-                return TopoUtils.formatBitRate(rate());
-
-            case TAGGED:
-                return hasTraffic() ? TopoUtils.formatBytes(bytes()) : EMPTY;
-
-            default:
-                return "?";
-        }
-    }
-
-    // === ----------------------------------------------------------------
-    // accessors
-
+    /**
+     * Returns the key for this bi-link.
+     *
+     * @return the key
+     */
     public LinkKey key() {
         return key;
     }
 
+    /**
+     * Returns the first link in this bi-link.
+     *
+     * @return the first link
+     */
     public Link one() {
         return one;
     }
 
+    /**
+     * Returns the second link in this bi-link.
+     *
+     * @return the second link
+     */
     public Link two() {
         return two;
     }
 
-    public boolean hasTraffic() {
-        return hasTraffic;
-    }
-
-    public boolean isOptical() {
-        return isOptical;
-    }
-
-    public boolean isAntMarch() {
-        return antMarch;
-    }
-
-    public LinkHighlight.Flavor flavor() {
-        return taggedFlavor;
-    }
-
-    public long bytes() {
-        return bytes;
-    }
-
-    public long rate() {
-        return rate;
-    }
-
-    public long flows() {
-        return flows;
-    }
+    /**
+     * Returns the link highlighting to use, based on this bi-link's current
+     * state.
+     *
+     * @param type optional highlighting type parameter
+     * @return link highlighting model
+     */
+    public abstract LinkHighlight highlight(Enum<?> type);
 }
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/BiLinkMap.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/BiLinkMap.java
new file mode 100644
index 0000000..18565d7
--- /dev/null
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/BiLinkMap.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.onosproject.ui.impl.topo;
+
+import org.onosproject.net.Link;
+import org.onosproject.net.LinkKey;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Represents a collection of {@link BiLink} concrete classes. These maps
+ * are used to collate a set of unidirectional {@link Link}s into a smaller
+ * set of bi-directional {@link BiLink} derivatives.
+ * <p>
+ * @param <B> the type of bi-link subclass
+ */
+public abstract class BiLinkMap<B extends BiLink> {
+
+    private final Map<LinkKey, B> map = new HashMap<>();
+
+    /**
+     * Creates a new instance of a bi-link. Concrete subclasses should
+     * instantiate and return the appropriate bi-link subclass.
+     *
+     * @param key the link key
+     * @param link the initial link
+     * @return a new instance
+     */
+    public abstract B create(LinkKey key, Link link);
+
+    /**
+     * Adds the given link to our collection, returning the corresponding
+     * bi-link (creating one if needed necessary).
+     *
+     * @param link the link to add to the collection
+     * @return the corresponding bi-link wrapper
+     */
+    public B add(Link link) {
+        LinkKey key = TopoUtils.canonicalLinkKey(checkNotNull(link));
+        B blink = map.get(key);
+        if (blink == null) {
+            // no bi-link yet exists for this link
+            blink = create(key, link);
+            map.put(key, blink);
+        } else {
+            // we have a bi-link for this link.
+            if (!blink.one().equals(link)) {
+                blink.setOther(link);
+            }
+        }
+        return blink;
+    }
+
+    /**
+     * Returns the bi-link instances in the collection.
+     *
+     * @return the bi-links in this map
+     */
+    public Collection<B> biLinks() {
+        return map.values();
+    }
+
+    /**
+     * Returns the number of bi-links in the collection.
+     *
+     * @return number of bi-links
+     */
+    public int size() {
+        return map.size();
+    }
+}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/IntentSelection.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/IntentSelection.java
index eb959c5..ae7eab4 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/IntentSelection.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/IntentSelection.java
@@ -47,7 +47,7 @@
      * @param nodes node selection
      * @param filter intent filter
      */
-    public IntentSelection(NodeSelection nodes, TopologyViewIntentFilter filter) {
+    public IntentSelection(NodeSelection nodes, TopoIntentFilter filter) {
         this.nodes = nodes;
         intents = filter.findPathIntents(nodes.hosts(), nodes.devices());
     }
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/NodeSelection.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/NodeSelection.java
index fa776b3..c0597aa 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/NodeSelection.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/NodeSelection.java
@@ -41,7 +41,7 @@
  */
 public class NodeSelection {
 
-    protected static final Logger log =
+    private static final Logger log =
             LoggerFactory.getLogger(NodeSelection.class);
 
     private static final String IDS = "ids";
@@ -183,5 +183,4 @@
         }
         return unmatched;
     }
-
 }
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/ServicesBundle.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/ServicesBundle.java
index 4282cdc..bcc4ad8 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/ServicesBundle.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/ServicesBundle.java
@@ -42,6 +42,7 @@
 
     /**
      * Creates the services bundle.
+     *
      * @param intentService     intent service reference
      * @param deviceService     device service reference
      * @param hostService       host service reference
@@ -66,30 +67,65 @@
         this.portStatsService = checkNotNull(portStatsService);
     }
 
+    /**
+     * Returns a reference to the intent service.
+     *
+     * @return intent service reference
+     */
     public IntentService intentService() {
         return intentService;
     }
 
+    /**
+     * Returns a reference to the device service.
+     *
+     * @return device service reference
+     */
     public DeviceService deviceService() {
         return deviceService;
     }
 
+    /**
+     * Returns a reference to the host service.
+     *
+     * @return host service reference
+     */
     public HostService hostService() {
         return hostService;
     }
 
+    /**
+     * Returns a reference to the link service.
+     *
+     * @return link service reference
+     */
     public LinkService linkService() {
         return linkService;
     }
 
+    /**
+     * Returns a reference to the flow rule service.
+     *
+     * @return flow service reference
+     */
     public FlowRuleService flowService() {
         return flowService;
     }
 
+    /**
+     * Returns a reference to the flow statistics service.
+     *
+     * @return flow statistics service reference
+     */
     public StatisticService flowStatsService() {
         return flowStatsService;
     }
 
+    /**
+     * Returns a reference to the port statistics service.
+     *
+     * @return port statistics service reference
+     */
     public PortStatisticsService portStatsService() {
         return portStatsService;
     }
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/TopologyViewIntentFilter.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/TopoIntentFilter.java
similarity index 96%
rename from web/gui/src/main/java/org/onosproject/ui/impl/topo/TopologyViewIntentFilter.java
rename to web/gui/src/main/java/org/onosproject/ui/impl/topo/TopoIntentFilter.java
index 1bd2b58..8372ded 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/TopologyViewIntentFilter.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/TopoIntentFilter.java
@@ -48,7 +48,7 @@
  * Auxiliary facility to query the intent service based on the specified
  * set of end-station hosts, edge points or infrastructure devices.
  */
-public class TopologyViewIntentFilter {
+public class TopoIntentFilter {
 
     private final IntentService intentService;
     private final DeviceService deviceService;
@@ -60,19 +60,13 @@
      *
      * @param services service references bundle
      */
-    public TopologyViewIntentFilter(ServicesBundle services) {
+    public TopoIntentFilter(ServicesBundle services) {
         this.intentService = services.intentService();
         this.deviceService = services.deviceService();
         this.hostService = services.hostService();
         this.linkService = services.linkService();
     }
 
-
-    // TODO: Review - do we need this signature, with sourceIntents??
-//    public List<Intent> findPathIntents(Set<Host> hosts, Set<Device> devices,
-//                                        Iterable<Intent> sourceIntents) {
-//    }
-
     /**
      * Finds all path (host-to-host or point-to-point) intents that pertain
      * to the given hosts and devices.
@@ -277,5 +271,4 @@
         }
         return null;
     }
-
 }
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/TopoUtils.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/TopoUtils.java
index 8d6b319..d43b376 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/TopoUtils.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/TopoUtils.java
@@ -21,15 +21,16 @@
 import org.onosproject.net.LinkKey;
 
 import java.text.DecimalFormat;
-import java.util.Map;
 
 import static org.onosproject.net.LinkKey.linkKey;
 
 /**
- * Utility methods for helping out with the topology view.
+ * Utility methods for helping out with formatting data for the Topology View
+ * in the web client.
  */
 public final class TopoUtils {
 
+    // explicit decision made to not 'javadoc' these self explanatory constants
     public static final double KILO = 1024;
     public static final double MEGA = 1024 * KILO;
     public static final double GIGA = 1024 * MEGA;
@@ -155,30 +156,4 @@
         }
         return String.valueOf(flows) + SPACE + (flows > 1 ? FLOWS : FLOW);
     }
-
-
-    /**
-     * Creates a new biLink with the supplied link (and adds it to the map),
-     * or attaches the link to an existing biLink (which already has the
-     * peer link).
-     *
-     * @param linkMap map of biLinks
-     * @param link the link to add
-     * @return the biLink to which the link was added
-     */
-    // creates a new biLink with supplied link, or attaches link to the
-    //  existing biLink (which already has its peer link)
-    public static BiLink addLink(Map<LinkKey, BiLink> linkMap, Link link) {
-        LinkKey key = TopoUtils.canonicalLinkKey(link);
-        BiLink biLink = linkMap.get(key);
-        if (biLink != null) {
-            biLink.setOther(link);
-        } else {
-            biLink = new BiLink(key, link);
-            linkMap.put(key, biLink);
-        }
-        return biLink;
-    }
-
-
 }
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/TrafficClass.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/TrafficClass.java
index 1389aba..0fa1581 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/TrafficClass.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/TrafficClass.java
@@ -24,6 +24,7 @@
  * Auxiliary data carrier for assigning a highlight class to a set of
  * intents, for visualization in the topology view.
  */
+@Deprecated
 public class TrafficClass {
 
     private final LinkHighlight.Flavor flavor;
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/TrafficLink.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/TrafficLink.java
new file mode 100644
index 0000000..e83f9fc
--- /dev/null
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/TrafficLink.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.onosproject.ui.impl.topo;
+
+import org.onosproject.net.Link;
+import org.onosproject.net.LinkKey;
+import org.onosproject.net.statistic.Load;
+import org.onosproject.ui.topo.LinkHighlight;
+import org.onosproject.ui.topo.LinkHighlight.Flavor;
+
+import static org.onosproject.ui.topo.LinkHighlight.Flavor.NO_HIGHLIGHT;
+import static org.onosproject.ui.topo.LinkHighlight.Flavor.PRIMARY_HIGHLIGHT;
+import static org.onosproject.ui.topo.LinkHighlight.Flavor.SECONDARY_HIGHLIGHT;
+
+/**
+ * Representation of a link and its inverse, and associated traffic data.
+ * This class understands how to generate the appropriate
+ * {@link LinkHighlight}s for showing traffic data on the topology view.
+ */
+public class TrafficLink extends BiLink {
+
+    private static final String EMPTY = "";
+    private static final String QUE = "?";
+
+    private long bytes = 0;
+    private long rate = 0;
+    private long flows = 0;
+    private Flavor taggedFlavor = NO_HIGHLIGHT;
+    private boolean hasTraffic = false;
+    private boolean isOptical = false;
+    private boolean antMarch = false;
+
+    /**
+     * Constructs a traffic link for the given key and initial link.
+     *
+     * @param key  canonical key for this traffic link
+     * @param link first link
+     */
+    public TrafficLink(LinkKey key, Link link) {
+        super(key, link);
+    }
+
+    /**
+     * Sets the optical flag to the given value.
+     *
+     * @param b true if an optical link
+     * @return self, for chaining
+     */
+    public TrafficLink optical(boolean b) {
+        isOptical = b;
+        return this;
+    }
+
+    /**
+     * Sets the ant march flag to the given value.
+     *
+     * @param b true if marching ants required
+     * @return self, for chaining
+     */
+    public TrafficLink antMarch(boolean b) {
+        antMarch = b;
+        return this;
+    }
+
+    /**
+     * Tags this traffic link with the flavor to be used in visual rendering.
+     *
+     * @param flavor the flavor to tag
+     * @return self, for chaining
+     */
+    public TrafficLink tagFlavor(Flavor flavor) {
+        this.taggedFlavor = flavor;
+        return this;
+    }
+
+    /**
+     * Adds load statistics, marks the traffic link as having traffic.
+     *
+     * @param load load to add
+     */
+    public void addLoad(Load load) {
+        addLoad(load, 0);
+    }
+
+    /**
+     * Adds load statistics, marks the traffic link as having traffic, if the
+     * load {@link Load#rate rate} is greater than the given threshold
+     * (expressed in bytes per second).
+     *
+     * @param load load to add
+     * @param threshold threshold to register traffic
+     */
+    public void addLoad(Load load, double threshold) {
+        if (load != null) {
+            this.hasTraffic = hasTraffic || load.rate() > threshold;
+            this.bytes += load.latest();
+            this.rate += load.rate();
+        }
+    }
+
+    /**
+     * Adds the given count of flows to this traffic link.
+     *
+     * @param count count of flows
+     */
+    public void addFlows(int count) {
+        this.flows += count;
+    }
+
+    @Override
+    public LinkHighlight highlight(Enum<?> type) {
+        StatsType statsType = (StatsType) type;
+        switch (statsType) {
+            case FLOW_COUNT:
+                return highlightForFlowCount(statsType);
+
+            case FLOW_STATS:
+            case PORT_STATS:
+                return highlightForStats(statsType);
+
+            case TAGGED:
+                return highlightForTagging(statsType);
+
+            default:
+                throw new IllegalStateException("unexpected case: " + statsType);
+        }
+    }
+
+    private LinkHighlight highlightForStats(StatsType type) {
+        return new LinkHighlight(linkId(), SECONDARY_HIGHLIGHT)
+                .setLabel(generateLabel(type));
+    }
+
+    private LinkHighlight highlightForFlowCount(StatsType type) {
+        Flavor flavor = flows > 0 ? PRIMARY_HIGHLIGHT : SECONDARY_HIGHLIGHT;
+        return new LinkHighlight(linkId(), flavor)
+                .setLabel(generateLabel(type));
+    }
+
+    private LinkHighlight highlightForTagging(StatsType type) {
+        LinkHighlight hlite = new LinkHighlight(linkId(), taggedFlavor)
+                .setLabel(generateLabel(type));
+        if (isOptical) {
+            hlite.addMod(LinkHighlight.MOD_OPTICAL);
+        }
+        if (antMarch) {
+            hlite.addMod(LinkHighlight.MOD_ANIMATED);
+        }
+        return hlite;
+    }
+
+    // Generates a string representation of the load, to be used as a label
+    private String generateLabel(StatsType type) {
+        switch (type) {
+            case FLOW_COUNT:
+                return TopoUtils.formatFlows(flows);
+
+            case FLOW_STATS:
+                return TopoUtils.formatBytes(bytes);
+
+            case PORT_STATS:
+                return TopoUtils.formatBitRate(rate);
+
+            case TAGGED:
+                return hasTraffic ? TopoUtils.formatBytes(bytes) : EMPTY;
+
+            default:
+                return QUE;
+        }
+    }
+
+    /**
+     * Returns true if this link has been deemed to have enough traffic
+     * to register on the topology view in the web UI.
+     *
+     * @return true if this link has displayable traffic
+     */
+    public boolean hasTraffic() {
+        return hasTraffic;
+    }
+
+    /**
+     * Designates type of traffic statistics to report on a highlighted link.
+     */
+    public enum StatsType {
+        /**
+         * Number of flows.
+         */
+        FLOW_COUNT,
+
+        /**
+         * Number of bytes.
+         */
+        FLOW_STATS,
+
+        /**
+         * Number of bits per second.
+         */
+        PORT_STATS,
+
+        /**
+         * Custom tagged information.
+         */
+        TAGGED
+    }
+}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/LinkStatsType.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/TrafficLinkMap.java
similarity index 66%
rename from web/gui/src/main/java/org/onosproject/ui/impl/topo/LinkStatsType.java
rename to web/gui/src/main/java/org/onosproject/ui/impl/topo/TrafficLinkMap.java
index 589cddd..59965ad 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/LinkStatsType.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/TrafficLinkMap.java
@@ -17,27 +17,16 @@
 
 package org.onosproject.ui.impl.topo;
 
+import org.onosproject.net.Link;
+import org.onosproject.net.LinkKey;
+
 /**
- * Designates type of stats to report on a highlighted link.
+ * Collection of {@link TrafficLink}s.
  */
-public enum LinkStatsType {
-    /**
-     * Number of flows.
-     */
-    FLOW_COUNT,
+public class TrafficLinkMap extends BiLinkMap<TrafficLink> {
 
-    /**
-     * Number of bytes.
-     */
-    FLOW_STATS,
-
-    /**
-     * Number of bits per second.
-     */
-    PORT_STATS,
-
-    /**
-     * Custom tagged information.
-     */
-    TAGGED
+    @Override
+    public TrafficLink create(LinkKey key, Link link) {
+        return new TrafficLink(key, link);
+    }
 }