Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowEntry.java b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowEntry.java
new file mode 100644
index 0000000..3f86697
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowEntry.java
@@ -0,0 +1,88 @@
+package org.onlab.onos.net.flow;
+
+import org.onlab.onos.net.DeviceId;
+
+public class DefaultFlowEntry extends DefaultFlowRule implements FlowEntry {
+
+    private final int priority;
+    private final long created;
+    private final FlowId id;
+
+    public DefaultFlowEntry(DefaultFlowEntry entry) {
+        super(entry.deviceId(), entry.selector(), entry.treatment());
+        this.priority = entry.priority;
+        this.created = entry.created;
+        this.id = entry.id;
+    }
+
+    public DefaultFlowEntry(DeviceId deviceId, TrafficSelector selector,
+            TrafficTreatment treatment, int priority) {
+        super(deviceId, selector, treatment);
+        this.priority = priority;
+        this.created = System.currentTimeMillis();
+        this.id = FlowId.valueOf(this.hashCode());
+    }
+
+    @Override
+    public FlowId id() {
+        return null;
+    }
+
+    @Override
+    public int priority() {
+        return priority;
+    }
+
+    @Override
+    public long lifeMillis() {
+        return (created - System.currentTimeMillis());
+    }
+
+    @Override
+    public long idleMillis() {
+        return 0;
+    }
+
+    @Override
+    public long packets() {
+        return 0;
+    }
+
+    @Override
+    public long bytes() {
+        return 0;
+    }
+
+    @Override
+    /*
+     * currently uses the parts that definitely have a defined hashcode...
+     *
+     * (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    public int hashCode() {
+        final int prime = 31;
+        int result = prime * this.deviceId().hashCode();
+        result = prime * result + Long.valueOf(this.created).hashCode();
+        return result;
+    }
+
+    @Override
+    /*
+     * The priority and statistics can change on a given treatment and selector
+     *
+     * (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public boolean equals(Object obj) {
+        if (obj instanceof DefaultFlowEntry) {
+            DefaultFlowEntry that = (DefaultFlowEntry) obj;
+            if (!this.id.equals(that.id())) {
+                return false;
+            }
+            return super.equals(obj);
+        }
+        return false;
+    }
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowRule.java b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowRule.java
index 801e8f9..9a24091 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowRule.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowRule.java
@@ -35,4 +35,37 @@
         return treatment;
     }
 
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = prime * this.deviceId().hashCode();
+        result = prime * result + selector.hashCode();
+        result = prime * result + treatment.hashCode();
+        return result;
+    }
+
+    @Override
+    /*
+     * The priority and statistics can change on a given treatment and selector
+     *
+     * (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public boolean equals(Object obj) {
+        if (obj instanceof FlowRule) {
+            DefaultFlowRule that = (DefaultFlowRule) obj;
+            if (!this.deviceId().equals(that.deviceId())) {
+                return false;
+            }
+            if (!this.treatment().equals(that.treatment())) {
+                return false;
+            }
+            if (!this.selector().equals(that.selector())) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowEntry.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowEntry.java
index 7bef2c8..7fee1c1 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowEntry.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowEntry.java
@@ -5,7 +5,11 @@
  */
 public interface FlowEntry extends FlowRule {
 
-
+    /**
+     * Returns the ID of this flow.
+     *
+     * @return the flow ID
+     */
     FlowId id();
 
     /**
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleService.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleService.java
index 85d5680..71434c7 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleService.java
@@ -1,5 +1,7 @@
 package org.onlab.onos.net.flow;
 
+import java.util.List;
+
 import org.onlab.onos.net.DeviceId;
 
 /**
@@ -30,7 +32,7 @@
      * throws SomeKindOfException that indicates which ones were applied and
      *                  which ones failed
      */
-    void applyFlowRules(FlowRule... flowRules);
+    List<FlowEntry> applyFlowRules(FlowRule... flowRules);
 
     /**
      * Removes the specified flow rules from their respective devices. If the
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/flow/impl/SimpleFlowRuleManager.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/flow/impl/SimpleFlowRuleManager.java
index 267af5b..7540ec8 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/flow/impl/SimpleFlowRuleManager.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/flow/impl/SimpleFlowRuleManager.java
@@ -2,6 +2,9 @@
 
 import static org.slf4j.LoggerFactory.getLogger;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -25,17 +28,22 @@
 import org.onlab.onos.net.provider.AbstractProviderService;
 import org.slf4j.Logger;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 @Component(immediate = true)
 @Service
 public class SimpleFlowRuleManager
 extends AbstractProviderRegistry<FlowRuleProvider, FlowRuleProviderService>
 implements FlowRuleService, FlowRuleProviderRegistry {
 
+    public static final String FLOW_RULE_NULL = "FlowRule cannot be null";
     private final Logger log = getLogger(getClass());
 
     private final AbstractListenerRegistry<FlowRuleEvent, FlowRuleListener>
     listenerRegistry = new AbstractListenerRegistry<>();
 
+    private final SimpleFlowRuleStore store = new SimpleFlowRuleStore();
+
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     private EventDeliveryService eventDispatcher;
 
@@ -56,30 +64,31 @@
 
     @Override
     public Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
-        //TODO: store rules somewhere and return them here
-        return null;
+        return store.getFlowEntries(deviceId);
     }
 
     @Override
-    public void applyFlowRules(FlowRule... flowRules) {
+    public List<FlowEntry> applyFlowRules(FlowRule... flowRules) {
+        List<FlowEntry> entries = new ArrayList<FlowEntry>();
+
         for (int i = 0; i < flowRules.length; i++) {
-            FlowRule f = flowRules[0];
+            FlowRule f = flowRules[i];
             final Device device = deviceService.getDevice(f.deviceId());
             final FlowRuleProvider frp = getProvider(device.providerId());
-            //TODO: store rules somewhere
+            entries.add(store.storeFlowRule(f));
             frp.applyFlowRule(f);
         }
 
-
+        return entries;
     }
 
     @Override
     public void removeFlowRules(FlowRule... flowRules) {
         for (int i = 0; i < flowRules.length; i++) {
-            FlowRule f = flowRules[0];
+            FlowRule f = flowRules[i];
             final Device device = deviceService.getDevice(f.deviceId());
             final FlowRuleProvider frp = getProvider(device.providerId());
-            //TODO: remove stored rules from wherever they are
+            store.removeFlowRule(f);
             frp.removeFlowRule(f);
         }
 
@@ -102,8 +111,8 @@
     }
 
     private class InternalFlowRuleProviderService
-    extends AbstractProviderService<FlowRuleProvider>
-    implements FlowRuleProviderService {
+            extends AbstractProviderService<FlowRuleProvider>
+            implements FlowRuleProviderService {
 
         protected InternalFlowRuleProviderService(FlowRuleProvider provider) {
             super(provider);
@@ -111,22 +120,44 @@
 
         @Override
         public void flowRemoved(FlowRule flowRule) {
-            // TODO Auto-generated method stub
+            checkNotNull(flowRule, FLOW_RULE_NULL);
+            checkValidity();
+            FlowRuleEvent event = store.removeFlowRule(flowRule);
 
+            if (event != null) {
+                log.debug("Flow {} removed", flowRule);
+                post(event);
+            }
         }
 
         @Override
         public void flowMissing(FlowRule flowRule) {
+            checkNotNull(flowRule, FLOW_RULE_NULL);
+            checkValidity();
             // TODO Auto-generated method stub
 
         }
 
         @Override
         public void flowAdded(FlowRule flowRule) {
-            // TODO Auto-generated method stub
+            checkNotNull(flowRule, FLOW_RULE_NULL);
+            checkValidity();
 
+            FlowRuleEvent event = store.addOrUpdateFlowRule(flowRule);
+            if (event == null) {
+                log.debug("Flow {} updated", flowRule);
+            } else {
+                log.debug("Flow {} added", flowRule);
+                post(event);
+            }
         }
 
+        // Posts the specified event to the local event dispatcher.
+        private void post(FlowRuleEvent event) {
+            if (event != null) {
+                eventDispatcher.post(event);
+            }
+        }
     }
 
 }
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/flow/impl/SimpleFlowRuleStore.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/flow/impl/SimpleFlowRuleStore.java
new file mode 100644
index 0000000..f839396
--- /dev/null
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/flow/impl/SimpleFlowRuleStore.java
@@ -0,0 +1,85 @@
+package org.onlab.onos.net.trivial.flow.impl;
+
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.flow.DefaultFlowEntry;
+import org.onlab.onos.net.flow.FlowEntry;
+import org.onlab.onos.net.flow.FlowRule;
+import org.onlab.onos.net.flow.FlowRuleEvent;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
+
+import static org.onlab.onos.net.flow.FlowRuleEvent.Type.*;
+
+/**
+ * Manages inventory of flow rules using trivial in-memory implementation.
+ */
+public class SimpleFlowRuleStore {
+
+    // store entries as a pile of rules, no info about device tables
+    private final Multimap<DeviceId, FlowEntry> flowEntries = HashMultimap.create();
+
+    /**
+     * Returns the flow entries associated with a device.
+     *
+     * @param deviceId the device ID
+     * @return the flow entries
+     */
+    Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
+        return ImmutableSet.copyOf(flowEntries.get(deviceId));
+    }
+
+    /**
+     * Stores a new flow rule, and generates a FlowEntry for it.
+     *
+     * @param rule the flow rule to add
+     * @return a flow entry
+     */
+    FlowEntry storeFlowRule(FlowRule rule) {
+        DeviceId did = rule.deviceId();
+        FlowEntry entry = new DefaultFlowEntry(did,
+                rule.selector(), rule.treatment(), rule.priority());
+        flowEntries.put(did, entry);
+        return entry;
+    }
+
+    /**
+     * Stores a new flow rule, or updates an existing entry.
+     *
+     * @param rule the flow rule to add or update
+     * @return flow_added event, or null if just an update
+     */
+    FlowRuleEvent addOrUpdateFlowRule(FlowRule rule) {
+        DeviceId did = rule.deviceId();
+
+        // check if this new rule is an update to an existing entry
+        for (FlowEntry fe : flowEntries.get(did)) {
+            if (rule.equals(fe)) {
+                // TODO update the stats on this flowEntry?
+                return null;
+            }
+        }
+
+        FlowEntry newfe = new DefaultFlowEntry(did,
+                rule.selector(), rule.treatment(), rule.priority());
+        flowEntries.put(did, newfe);
+        return new FlowRuleEvent(RULE_ADDED, rule);
+    }
+
+    /**
+     *
+     * @param rule the flow rule to remove
+     * @return flow_removed event, or null if nothing removed
+     */
+    FlowRuleEvent removeFlowRule(FlowRule rule) {
+        synchronized (this) {
+            if (flowEntries.remove(rule.deviceId(), rule)) {
+                return new FlowRuleEvent(RULE_REMOVED, rule);
+            } else {
+                return null;
+            }
+        }
+    }
+
+}
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/host/impl/SimpleHostStore.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/host/impl/SimpleHostStore.java
index c53d2c8..f1a64e7 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/host/impl/SimpleHostStore.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/host/impl/SimpleHostStore.java
@@ -106,7 +106,7 @@
      * Removes the specified host from the inventory.
      *
      * @param hostId host identification
-     * @return remove even or null if host was not found
+     * @return remove event or null if host was not found
      */
     HostEvent removeHost(HostId hostId) {
         synchronized (this) {
diff --git a/utils/misc/src/main/java/org/onlab/packet/IpAddress.java b/utils/misc/src/main/java/org/onlab/packet/IpAddress.java
index 1c2bc1b..02ed37a 100644
--- a/utils/misc/src/main/java/org/onlab/packet/IpAddress.java
+++ b/utils/misc/src/main/java/org/onlab/packet/IpAddress.java
@@ -7,6 +7,8 @@
  */
 public final class IpAddress {
 
+    // TODO a comparator for netmasks? E.g. for sorting by prefix match order.
+
     //IP Versions
     public enum Version { INET, INET6 };
 
@@ -99,7 +101,8 @@
 
     /**
      * Converts a dotted-decimal string (x.x.x.x) into an IPv4 address. The
-     * string can also be in CIDR (slash) notation.
+     * string can also be in CIDR (slash) notation. If the netmask is omitted,
+     * it will be set to DEFAULT_MASK (0).
      *
      * @param address a IP address in string form, e.g. "10.0.0.1", "10.0.0.1/24"
      * @return an IP address