Added Change utility class for representing change events + Moved Match class to org.onlab.util

Change-Id: I08e8cd8dd92983bd2764e83016b1abc0bf29388f
diff --git a/core/api/src/main/java/org/onosproject/event/Change.java b/core/api/src/main/java/org/onosproject/event/Change.java
new file mode 100644
index 0000000..b1d2b6f
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/event/Change.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2016 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.event;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+
+/**
+ * Generic representation of an update.
+ *
+ * @param <T> type of value that was updated
+ */
+public class Change<T> {
+
+    private final T oldValue;
+    private final T newValue;
+
+    public Change(T oldValue, T newValue) {
+        this.oldValue = oldValue;
+        this.newValue = newValue;
+    }
+
+    /**
+     * Returns previous value.
+     * @return previous value.
+     */
+    public T oldValue() {
+        return oldValue;
+    }
+
+    /**
+     * Returns new or current value.
+     * @return new value.
+     */
+    public T newValue() {
+        return newValue;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (other == null || !(other instanceof Change)) {
+            return false;
+        }
+        Change<T> that = (Change<T>) other;
+        return Objects.equal(this.oldValue, that.oldValue) &&
+                Objects.equal(this.newValue, that.newValue);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(oldValue, newValue);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("oldValue", oldValue)
+                .add("newValue", newValue)
+                .toString();
+    }
+}
diff --git a/core/api/src/test/java/org/onosproject/event/ChangeTest.java b/core/api/src/test/java/org/onosproject/event/ChangeTest.java
new file mode 100644
index 0000000..996af93
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/event/ChangeTest.java
@@ -0,0 +1,29 @@
+package org.onosproject.event;
+
+import org.junit.Test;
+
+import com.google.common.testing.EqualsTester;
+
+import static org.junit.Assert.*;
+
+/**
+ * Unit tests for {@link Change}.
+ */
+public class ChangeTest {
+
+    @Test
+    public void getters() {
+        Change<String> change = new Change<>("a", "b");
+        assertEquals("a", change.oldValue());
+        assertEquals("b", change.newValue());
+    }
+
+    @Test
+    public void equality() {
+        new EqualsTester()
+        .addEqualityGroup(new Change<>("foo", "bar"),
+                          new Change<>("foo", "bar"))
+        .addEqualityGroup(new Change<>("bar", "car"))
+        .testEquals();
+    }
+}
diff --git a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseProxy.java b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseProxy.java
index 1d81f99..ab5f987 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseProxy.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseProxy.java
@@ -16,6 +16,7 @@
 
 package org.onosproject.store.consistent.impl;
 
+import org.onlab.util.Match;
 import org.onosproject.store.service.Transaction;
 import org.onosproject.store.service.Versioned;
 
diff --git a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseSerializer.java b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseSerializer.java
index de73414..9af7445 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseSerializer.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseSerializer.java
@@ -19,6 +19,7 @@
 import java.nio.ByteBuffer;
 
 import org.onlab.util.KryoNamespace;
+import org.onlab.util.Match;
 import org.onosproject.cluster.NodeId;
 import org.onosproject.store.serializers.KryoNamespaces;
 import org.onosproject.store.serializers.KryoSerializer;
diff --git a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseState.java b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseState.java
index 1136428..65ffd92 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseState.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseState.java
@@ -20,6 +20,8 @@
 import net.kuujo.copycat.state.Initializer;
 import net.kuujo.copycat.state.Query;
 import net.kuujo.copycat.state.StateContext;
+
+import org.onlab.util.Match;
 import org.onosproject.store.service.Transaction;
 import org.onosproject.store.service.Versioned;
 
diff --git a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultAsyncConsistentMap.java b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultAsyncConsistentMap.java
index 7c2613e..cdbbd28 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultAsyncConsistentMap.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultAsyncConsistentMap.java
@@ -22,6 +22,7 @@
 import com.google.common.collect.Maps;
 
 import org.onlab.util.HexString;
+import org.onlab.util.Match;
 import org.onlab.util.SharedExecutors;
 import org.onlab.util.Tools;
 import org.onosproject.core.ApplicationId;
diff --git a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDatabase.java b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDatabase.java
index 2a50fbd..59b6565 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDatabase.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDatabase.java
@@ -17,12 +17,15 @@
 package org.onosproject.store.consistent.impl;
 
 import com.google.common.collect.Sets;
+
 import net.kuujo.copycat.resource.internal.AbstractResource;
 import net.kuujo.copycat.resource.internal.ResourceManager;
 import net.kuujo.copycat.state.StateMachine;
 import net.kuujo.copycat.state.internal.DefaultStateMachine;
 import net.kuujo.copycat.util.concurrent.Futures;
 import net.kuujo.copycat.util.function.TriConsumer;
+
+import org.onlab.util.Match;
 import org.onosproject.store.service.Transaction;
 import org.onosproject.store.service.Versioned;
 
diff --git a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDatabaseState.java b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDatabaseState.java
index 8943fc8..3bb413c 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDatabaseState.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDatabaseState.java
@@ -21,8 +21,11 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
+
 import net.kuujo.copycat.state.Initializer;
 import net.kuujo.copycat.state.StateContext;
+
+import org.onlab.util.Match;
 import org.onosproject.store.service.DatabaseUpdate;
 import org.onosproject.store.service.Transaction;
 import org.onosproject.store.service.Versioned;
diff --git a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/PartitionedDatabase.java b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/PartitionedDatabase.java
index f741b36..a78a44a 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/PartitionedDatabase.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/PartitionedDatabase.java
@@ -20,9 +20,12 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
+
 import net.kuujo.copycat.Task;
 import net.kuujo.copycat.cluster.Cluster;
 import net.kuujo.copycat.resource.ResourceState;
+
+import org.onlab.util.Match;
 import org.onosproject.store.service.DatabaseUpdate;
 import org.onosproject.store.service.Transaction;
 import org.onosproject.store.service.Versioned;
diff --git a/core/store/dist/src/test/java/org/onosproject/store/consistent/impl/DefaultAsyncConsistentMapTest.java b/core/store/dist/src/test/java/org/onosproject/store/consistent/impl/DefaultAsyncConsistentMapTest.java
index 5fc8113..8076ba5 100644
--- a/core/store/dist/src/test/java/org/onosproject/store/consistent/impl/DefaultAsyncConsistentMapTest.java
+++ b/core/store/dist/src/test/java/org/onosproject/store/consistent/impl/DefaultAsyncConsistentMapTest.java
@@ -32,6 +32,7 @@
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.onlab.util.Match;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.DefaultApplicationId;
 import org.onosproject.store.service.Serializer;
diff --git a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/Match.java b/utils/misc/src/main/java/org/onlab/util/Match.java
similarity index 61%
rename from core/store/dist/src/main/java/org/onosproject/store/consistent/impl/Match.java
rename to utils/misc/src/main/java/org/onlab/util/Match.java
index 5f707d6..295e558 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/Match.java
+++ b/utils/misc/src/main/java/org/onlab/util/Match.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.store.consistent.impl;
+package org.onlab.util;
 
 import static com.google.common.base.MoreObjects.toStringHelper;
 
@@ -28,16 +28,21 @@
  */
 public final class Match<T> {
 
+    public static final Match ANY = new Match<>();
+    public static final Match NULL = new Match<>(null, false);
+    public static final Match NOT_NULL = new Match<>(null, true);
+
     private final boolean matchAny;
     private final T value;
+    private final boolean negation;
 
     /**
-     * Returns a Match that matches any value.
+     * Returns a Match that matches any value including null.
      * @param <T> match type
      * @return new instance
      */
     public static <T> Match<T> any() {
-        return new Match<>();
+        return ANY;
     }
 
     /**
@@ -46,27 +51,48 @@
      * @return new instance
      */
     public static <T> Match<T> ifNull() {
-        return ifValue(null);
+        return NULL;
     }
 
     /**
-     * Returns a Match that matches only specified value.
+     * Returns a Match that matches all non-null values.
+     * @param <T> match type
+     * @return new instance
+     */
+    public static <T> Match<T> ifNotNull() {
+        return NOT_NULL;
+    }
+
+    /**
+     * Returns a Match that only matches the specified value.
      * @param value value to match
      * @param <T> match type
      * @return new instance
      */
     public static <T> Match<T> ifValue(T value) {
-        return new Match<>(value);
+        return new Match<>(value, false);
+    }
+
+    /**
+     * Returns a Match that matches any value except the specified value.
+     * @param value value to not match
+     * @param <T> match type
+     * @return new instance
+     */
+    public static <T> Match<T> ifNotValue(T value) {
+        return new Match<>(value, true);
     }
 
     private Match() {
         matchAny = true;
+        negation = false;
         value = null;
     }
 
-    private Match(T value) {
+    private Match(T value, boolean negation) {
         matchAny = false;
         this.value = value;
+        this.negation = negation;
     }
 
     /**
@@ -79,9 +105,9 @@
         if (matchAny) {
             return any();
         } else if (value == null) {
-            return ifNull();
+            return negation ? ifNotNull() : ifNull();
         } else {
-            return ifValue(mapper.apply(value));
+            return negation ? ifNotValue(mapper.apply(value)) : ifValue(mapper.apply(value));
         }
     }
 
@@ -94,21 +120,21 @@
         if (matchAny) {
             return true;
         } else if (other == null) {
-            return value == null;
+            return negation ? value != null : value == null;
         } else {
             if (value instanceof byte[]) {
-                return Arrays.equals((byte[]) value, (byte[]) other);
+                boolean equal = Arrays.equals((byte[]) value, (byte[]) other);
+                return negation ? !equal : equal;
             }
-            return Objects.equals(value, other);
+            return negation ? !Objects.equals(value, other) : Objects.equals(value, other);
         }
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(matchAny, value);
+        return Objects.hash(matchAny, value, negation);
     }
 
-    @SuppressWarnings("unchecked")
     @Override
     public boolean equals(Object other) {
         if (!(other instanceof Match)) {
@@ -116,13 +142,15 @@
         }
         Match<T> that = (Match<T>) other;
         return Objects.equals(this.matchAny, that.matchAny) &&
-               Objects.equals(this.value, that.value);
+               Objects.equals(this.value, that.value) &&
+               Objects.equals(this.negation, that.negation);
     }
 
     @Override
     public String toString() {
         return toStringHelper(this)
                 .add("matchAny", matchAny)
+                .add("negation", negation)
                 .add("value", value)
                 .toString();
     }
diff --git a/utils/misc/src/main/java/org/onlab/util/Tools.java b/utils/misc/src/main/java/org/onlab/util/Tools.java
index ffefbfd..296e14b 100644
--- a/utils/misc/src/main/java/org/onlab/util/Tools.java
+++ b/utils/misc/src/main/java/org/onlab/util/Tools.java
@@ -54,6 +54,7 @@
 
 import org.slf4j.Logger;
 
+import com.google.common.base.Charsets;
 import com.google.common.base.Strings;
 import com.google.common.primitives.UnsignedLongs;
 import com.google.common.util.concurrent.ThreadFactoryBuilder;
@@ -210,6 +211,20 @@
     }
 
     /**
+     * Returns the UTF-8 encoded byte[] representation of a String.
+     */
+    public static byte[] getBytesUtf8(String input) {
+        return input.getBytes(Charsets.UTF_8);
+    }
+
+    /**
+     * Returns the String representation of UTF-8 encoded byte[].
+     */
+    public static String toStringUtf8(byte[] input) {
+        return new String(input, Charsets.UTF_8);
+    }
+
+    /**
      * Returns a copy of the input byte array.
      *
      * @param original input
diff --git a/core/store/dist/src/test/java/org/onosproject/store/consistent/impl/MatchTest.java b/utils/misc/src/test/java/org/onlab/util/MatchTest.java
similarity index 97%
rename from core/store/dist/src/test/java/org/onosproject/store/consistent/impl/MatchTest.java
rename to utils/misc/src/test/java/org/onlab/util/MatchTest.java
index 952393a..d0b1b78 100644
--- a/core/store/dist/src/test/java/org/onosproject/store/consistent/impl/MatchTest.java
+++ b/utils/misc/src/test/java/org/onlab/util/MatchTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.store.consistent.impl;
+package org.onlab.util;
 
 import static junit.framework.TestCase.assertEquals;
 import static junit.framework.TestCase.assertFalse;