Adding more unit tests.
Made some classes abstract which should have been.
diff --git a/core/api/src/main/java/org/onlab/onos/net/AbstractAnnotated.java b/core/api/src/main/java/org/onlab/onos/net/AbstractAnnotated.java
index f29ad43..c329598 100644
--- a/core/api/src/main/java/org/onlab/onos/net/AbstractAnnotated.java
+++ b/core/api/src/main/java/org/onlab/onos/net/AbstractAnnotated.java
@@ -5,7 +5,7 @@
 /**
  * Base abstraction of an annotated entity.
  */
-public class AbstractAnnotated implements Annotated {
+public abstract class AbstractAnnotated implements Annotated {
 
     private static final Annotations EMPTY = DefaultAnnotations.builder().build();
 
diff --git a/core/api/src/main/java/org/onlab/onos/net/AbstractDescription.java b/core/api/src/main/java/org/onlab/onos/net/AbstractDescription.java
index 5e5b117..5ebde83 100644
--- a/core/api/src/main/java/org/onlab/onos/net/AbstractDescription.java
+++ b/core/api/src/main/java/org/onlab/onos/net/AbstractDescription.java
@@ -5,7 +5,7 @@
 /**
  * Base implementation of an annotated model description.
  */
-public class AbstractDescription implements Annotated {
+public abstract class AbstractDescription implements Annotated {
 
     private static final SparseAnnotations EMPTY = DefaultAnnotations.builder().build();
 
diff --git a/core/api/src/main/java/org/onlab/onos/net/AbstractElement.java b/core/api/src/main/java/org/onlab/onos/net/AbstractElement.java
index 251d68a..f39469b 100644
--- a/core/api/src/main/java/org/onlab/onos/net/AbstractElement.java
+++ b/core/api/src/main/java/org/onlab/onos/net/AbstractElement.java
@@ -5,7 +5,7 @@
 /**
  * Base implementation of network elements, i.e. devices or hosts.
  */
-public class AbstractElement extends AbstractModel implements Element {
+public abstract class AbstractElement extends AbstractModel implements Element {
 
     protected final ElementId id;
 
@@ -27,9 +27,4 @@
         this.id = id;
     }
 
-    @Override
-    public ElementId id() {
-        return id;
-    }
-
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/AbstractModel.java b/core/api/src/main/java/org/onlab/onos/net/AbstractModel.java
index 482ec68..f5f7689 100644
--- a/core/api/src/main/java/org/onlab/onos/net/AbstractModel.java
+++ b/core/api/src/main/java/org/onlab/onos/net/AbstractModel.java
@@ -5,7 +5,7 @@
 /**
  * Base implementation of a network model entity.
  */
-public class AbstractModel extends AbstractAnnotated implements Provided {
+public abstract class AbstractModel extends AbstractAnnotated implements Provided {
 
     private final ProviderId providerId;
 
diff --git a/core/api/src/main/java/org/onlab/onos/net/DefaultAnnotations.java b/core/api/src/main/java/org/onlab/onos/net/DefaultAnnotations.java
index 88eafb2..001518e 100644
--- a/core/api/src/main/java/org/onlab/onos/net/DefaultAnnotations.java
+++ b/core/api/src/main/java/org/onlab/onos/net/DefaultAnnotations.java
@@ -5,6 +5,8 @@
 import java.util.Objects;
 import java.util.Set;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 /**
  * Represents a set of simple annotations that can be used to add arbitrary
  * attributes to various parts of the data model.
@@ -19,9 +21,9 @@
     }
 
     /**
-     * Creates a new set of annotations using the specified immutable map.
+     * Creates a new set of annotations using clone of the specified hash map.
      *
-     * @param map immutable map of key/value pairs
+     * @param map hash map of key/value pairs
      */
     private DefaultAnnotations(Map<String, String> map) {
         this.map = map;
@@ -36,6 +38,38 @@
         return new Builder();
     }
 
+    /**
+     * Merges the specified base set of annotations and additional sparse
+     * annotations into new combined annotations. If the supplied sparse
+     * annotations are empty, the original base annotations are returned.
+     * Any keys tagged for removal in the sparse annotations will be omitted
+     * in the resulting merged annotations.
+     *
+     * @param annotations       base annotations
+     * @param sparseAnnotations additional sparse annotations
+     * @return combined annotations or the original base annotations if there
+     * are not additional annotations
+     */
+    public static DefaultAnnotations merge(DefaultAnnotations annotations,
+                                           SparseAnnotations sparseAnnotations) {
+        checkNotNull(annotations, "Annotations cannot be null");
+        if (sparseAnnotations == null || sparseAnnotations.keys().isEmpty()) {
+            return annotations;
+        }
+
+        // Merge the two maps. Yes, this is not very efficient, but the
+        // use-case implies small maps and infrequent merges, so we opt for
+        // simplicity.
+        Map<String, String> merged = copy(annotations.map);
+        for (String key : sparseAnnotations.keys()) {
+            if (sparseAnnotations.isRemoved(key)) {
+                merged.remove(key);
+            } else {
+                merged.put(key, sparseAnnotations.value(key));
+            }
+        }
+        return new DefaultAnnotations(merged);
+    }
 
     @Override
     public Set<String> keys() {
@@ -53,15 +87,20 @@
         return Objects.equals(Builder.REMOVED, map.get(key));
     }
 
+    @SuppressWarnings("unchecked")
+    private static HashMap<String, String> copy(Map<String, String> original) {
+        if (original instanceof HashMap) {
+            return (HashMap) ((HashMap) original).clone();
+        }
+        throw new IllegalArgumentException("Expecting HashMap instance");
+    }
+
     /**
      * Facility for gradually building model annotations.
      */
     public static final class Builder {
 
         private static final String REMOVED = "~rEmOvEd~";
-
-        // FIXME: Figure out whether and how to make this immutable and serializable
-//        private final ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
         private final Map<String, String> builder = new HashMap<>();
 
         // Private construction is forbidden.
@@ -99,8 +138,7 @@
          * @return annotations
          */
         public DefaultAnnotations build() {
-//            return new DefaultAnnotations(builder.build());
-            return new DefaultAnnotations(builder);
+            return new DefaultAnnotations(copy(builder));
         }
     }
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/DefaultDevice.java b/core/api/src/main/java/org/onlab/onos/net/DefaultDevice.java
index 2c6c4ff..8b3eee1 100644
--- a/core/api/src/main/java/org/onlab/onos/net/DefaultDevice.java
+++ b/core/api/src/main/java/org/onlab/onos/net/DefaultDevice.java
@@ -51,7 +51,7 @@
 
     @Override
     public DeviceId id() {
-        return (DeviceId) super.id();
+        return (DeviceId) id;
     }
 
     @Override
diff --git a/core/api/src/main/java/org/onlab/onos/net/DefaultHost.java b/core/api/src/main/java/org/onlab/onos/net/DefaultHost.java
index efa876b..85712e8 100644
--- a/core/api/src/main/java/org/onlab/onos/net/DefaultHost.java
+++ b/core/api/src/main/java/org/onlab/onos/net/DefaultHost.java
@@ -45,7 +45,7 @@
 
     @Override
     public HostId id() {
-        return (HostId) super.id();
+        return (HostId) id;
     }
 
     @Override