Make serialized resources more compact to omit redundant parent resource ID

Change-Id: Icafe92f35bded405dd39e57ad8380bca82a6d720
(cherry picked from commit d68739b5839e1d2be907d3806e2c742df9da95fd)
diff --git a/core/api/src/main/java/org/onosproject/net/resource/DiscreteResourceSet.java b/core/api/src/main/java/org/onosproject/net/resource/DiscreteResourceSet.java
deleted file mode 100644
index c11f9f6..0000000
--- a/core/api/src/main/java/org/onosproject/net/resource/DiscreteResourceSet.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright 2016-present 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.net.resource;
-
-import com.google.common.annotations.Beta;
-import com.google.common.collect.ImmutableSet;
-
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-
-/**
- * Represents a set of discrete type resources.
- * This class is intended to be used by ConsistentResourceStore though it is exposed to the public.
- */
-@Beta
-public final class DiscreteResourceSet {
-    private final Set<DiscreteResource> values;
-    private final DiscreteResourceCodec codec;
-
-    private static final DiscreteResourceSet EMPTY = new DiscreteResourceSet(ImmutableSet.of(), NoOpCodec.INSTANCE);
-
-    /**
-     * Creates an instance with resources and the codec for them.
-     *
-     * @param values resources to be contained in the instance
-     * @param codec codec for the specified resources
-     * @return an instance
-     */
-    public static DiscreteResourceSet of(Set<DiscreteResource> values, DiscreteResourceCodec codec) {
-        checkNotNull(values);
-        checkNotNull(codec);
-        checkArgument(!values.isEmpty());
-
-        return new DiscreteResourceSet(ImmutableSet.copyOf(values), codec);
-    }
-
-    /**
-     * Creates the instance representing an empty resource set.
-     *
-     * @return an empty resource set
-     */
-    public static DiscreteResourceSet empty() {
-        return EMPTY;
-    }
-
-    private DiscreteResourceSet(Set<DiscreteResource> values, DiscreteResourceCodec codec) {
-        this.values = values;
-        this.codec = codec;
-    }
-
-    private DiscreteResourceSet() {
-        this.values = null;
-        this.codec = null;
-    }
-
-    /**
-     * Returns resources contained in this instance.
-     *
-     * @return resources
-     */
-    public Set<DiscreteResource> values() {
-        return values;
-    }
-
-    /**
-     * Returns the parent resource of the resources contained in this instance.
-     *
-     * @return the parent resource of the resources
-     */
-    public DiscreteResourceId parent() {
-        if (values.isEmpty()) {
-            // Dummy value avoiding null
-            return ResourceId.ROOT;
-        }
-        Optional<DiscreteResourceId> parent = values.iterator().next().id().parent();
-        checkState(parent.isPresent());
-
-        return parent.get();
-    }
-
-    /**
-     * Returns the codec for the resources contained in this instance.
-     *
-     * @return the codec for the resources
-     */
-    public DiscreteResourceCodec codec() {
-        return codec;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(values, codec);
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-
-        if (obj == null || getClass() != obj.getClass()) {
-            return false;
-        }
-
-        final DiscreteResourceSet other = (DiscreteResourceSet) obj;
-        return Objects.equals(this.values, other.values)
-                && Objects.equals(this.codec, other.codec);
-    }
-
-    public boolean contains(DiscreteResource resource) {
-        return values.contains(resource);
-    }
-}
diff --git a/core/api/src/main/java/org/onosproject/net/resource/NoOpCodec.java b/core/api/src/main/java/org/onosproject/net/resource/NoOpCodec.java
deleted file mode 100644
index 12f0c79..0000000
--- a/core/api/src/main/java/org/onosproject/net/resource/NoOpCodec.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2016-present 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.net.resource;
-
-/**
- * Represents no-op codec intended to used in an empty discrete resource set only.
- * It's not supposed to be used by other classes.
- */
-public class NoOpCodec implements DiscreteResourceCodec<Object> {
-    public static final DiscreteResourceCodec INSTANCE = new NoOpCodec();
-
-    @Override
-    public int encode(Object resource) {
-        return 0;
-    }
-
-    @Override
-    public Object decode(int value) {
-        return Resource.ROOT;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == this) {
-            return true;
-        }
-
-        if (obj == null || getClass() != obj.getClass()) {
-            return false;
-        }
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        return NoOpCodec.class.hashCode();
-    }
-}
diff --git a/core/store/dist/src/main/java/org/onosproject/store/resource/impl/ConsistentResourceStore.java b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/ConsistentResourceStore.java
index 3aa07f4..f343994 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/resource/impl/ConsistentResourceStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/ConsistentResourceStore.java
@@ -76,6 +76,7 @@
             .register(new EncodableDiscreteResourcesSerializer(), EncodableDiscreteResources.class)
             .register(GenericDiscreteResources.class)
             .register(EmptyDiscreteResources.class)
+            .register(new EncodedResourcesSerializer(), EncodedDiscreteResources.class)
             .register(ContinuousResourceAllocation.class)
             .build());
 
diff --git a/core/store/dist/src/main/java/org/onosproject/store/resource/impl/EncodableDiscreteResources.java b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/EncodableDiscreteResources.java
index a20ca52..7eaedc1 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/resource/impl/EncodableDiscreteResources.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/EncodableDiscreteResources.java
@@ -19,7 +19,6 @@
 import org.onosproject.net.resource.DiscreteResource;
 import org.onosproject.net.resource.DiscreteResourceCodec;
 import org.onosproject.net.resource.DiscreteResourceId;
-import org.onosproject.net.resource.DiscreteResourceSet;
 import org.onosproject.net.resource.Resources;
 
 import java.util.LinkedHashMap;
@@ -36,7 +35,7 @@
 final class EncodableDiscreteResources implements DiscreteResources {
     private static final Codecs CODECS = Codecs.getInstance();
     private final DiscreteResource parent;
-    private final Map<Class<?>, DiscreteResourceSet> values;
+    private final Map<Class<?>, EncodedDiscreteResources> values;
 
     private static Class<?> getClass(DiscreteResource resource) {
         return resource.valueAs(Object.class).map(Object::getClass).get();
@@ -55,16 +54,16 @@
         Map<Class<?>, Set<DiscreteResource>> grouped = resources.stream()
                 .collect(Collectors.groupingBy(x -> getClass(x), Collectors.toCollection(LinkedHashSet::new)));
 
-        Map<Class<?>, DiscreteResourceSet> values = new LinkedHashMap<>();
+        Map<Class<?>, EncodedDiscreteResources> values = new LinkedHashMap<>();
         for (Map.Entry<Class<?>, Set<DiscreteResource>> entry : grouped.entrySet()) {
             DiscreteResourceCodec<?> codec = CODECS.getCodec(entry.getKey());
-            values.put(entry.getKey(), DiscreteResourceSet.of(entry.getValue(), codec));
+            values.put(entry.getKey(), EncodedDiscreteResources.of(entry.getValue(), codec));
         }
 
         return new EncodableDiscreteResources(parent, values);
     }
 
-    private EncodableDiscreteResources(DiscreteResource parent, Map<Class<?>, DiscreteResourceSet> values) {
+    private EncodableDiscreteResources(DiscreteResource parent, Map<Class<?>, EncodedDiscreteResources> values) {
         this.parent = parent;
         this.values = values;
     }
@@ -93,10 +92,8 @@
 
     @Override
     public boolean isEmpty() {
-        return !values.values().stream()
-                .flatMap(x -> x.values().stream())
-                .findAny()
-                .isPresent();
+        return values.values().stream()
+                .allMatch(x -> x.isEmpty());
     }
 
     @Override
@@ -120,7 +117,7 @@
     @Override
     public Set<DiscreteResource> values() {
         return values.values().stream()
-                .flatMap(x -> x.values().stream())
+                .flatMap(x -> x.resources(parent.id()).stream())
                 .collect(Collectors.toCollection(LinkedHashSet::new));
     }
 
@@ -128,7 +125,7 @@
         return parent;
     }
 
-    Map<Class<?>, DiscreteResourceSet> rawValues() {
+    Map<Class<?>, EncodedDiscreteResources> rawValues() {
         return values;
     }
 }
diff --git a/core/store/dist/src/main/java/org/onosproject/store/resource/impl/EncodableDiscreteResourcesSerializer.java b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/EncodableDiscreteResourcesSerializer.java
index 4681f5a..6f65392 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/resource/impl/EncodableDiscreteResourcesSerializer.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/EncodableDiscreteResourcesSerializer.java
@@ -20,7 +20,6 @@
 import com.esotericsoftware.kryo.io.Input;
 import com.esotericsoftware.kryo.io.Output;
 import org.onosproject.net.resource.DiscreteResource;
-import org.onosproject.net.resource.DiscreteResourceSet;
 
 import java.util.LinkedHashSet;
 import java.util.Set;
@@ -40,11 +39,11 @@
     public EncodableDiscreteResources read(Kryo kryo, Input input, Class<EncodableDiscreteResources> cls) {
         DiscreteResource parent = kryo.readObject(input, DiscreteResource.class);
         @SuppressWarnings("unchecked")
-        Set<DiscreteResourceSet> resources = kryo.readObject(input, LinkedHashSet.class);
+        Set<EncodedDiscreteResources> resources = kryo.readObject(input, LinkedHashSet.class);
 
         return EncodableDiscreteResources.of(parent,
                 resources.stream()
-                        .flatMap(x -> x.values().stream())
+                        .flatMap(x -> x.resources(parent.id()).stream())
                         .collect(Collectors.toCollection(LinkedHashSet::new)));
     }
 }
diff --git a/core/store/dist/src/main/java/org/onosproject/store/resource/impl/EncodedDiscreteResources.java b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/EncodedDiscreteResources.java
new file mode 100644
index 0000000..c79b23a
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/EncodedDiscreteResources.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2016-present 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.store.resource.impl;
+
+import org.onlab.util.Tools;
+import org.onosproject.net.resource.DiscreteResource;
+import org.onosproject.net.resource.DiscreteResourceCodec;
+import org.onosproject.net.resource.DiscreteResourceId;
+import org.onosproject.net.resource.Resources;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Represents discrete resources encoded by a codec.
+ */
+final class EncodedDiscreteResources {
+    private final Set<Integer> rawValues;
+    private final DiscreteResourceCodec codec;
+
+    EncodedDiscreteResources(Set<Integer> rawValues, DiscreteResourceCodec codec) {
+        this.rawValues = rawValues;
+        this.codec = codec;
+    }
+
+    static EncodedDiscreteResources of(Set<DiscreteResource> resources, DiscreteResourceCodec codec) {
+        Set<Integer> rawValues = resources.stream()
+                .map(x -> x.valueAs(Object.class))
+                .flatMap(Tools::stream)
+                .map(x -> codec.encode(x))
+                .collect(Collectors.toCollection(LinkedHashSet::new));
+
+        return new EncodedDiscreteResources(rawValues, codec);
+    }
+
+    Set<Integer> rawValues() {
+        return rawValues;
+    }
+
+    DiscreteResourceCodec codec() {
+        return codec;
+    }
+
+    Set<DiscreteResource> resources(DiscreteResourceId parent) {
+        return rawValues.stream()
+                .map(x -> codec.decode(x))
+                .map(x -> Resources.discrete(parent, x).resource())
+                .collect(Collectors.toCollection(LinkedHashSet::new));
+    }
+
+    @SuppressWarnings("unchecked")
+    boolean contains(DiscreteResource resource) {
+        return rawValues.contains(codec.encode(resource));
+    }
+
+    boolean isEmpty() {
+        return rawValues.isEmpty();
+    }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/resource/DiscreteResourceSetSerializer.java b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/EncodedResourcesSerializer.java
similarity index 60%
rename from core/api/src/main/java/org/onosproject/net/resource/DiscreteResourceSetSerializer.java
rename to core/store/dist/src/main/java/org/onosproject/store/resource/impl/EncodedResourcesSerializer.java
index 2d6446a..587fb4c 100644
--- a/core/api/src/main/java/org/onosproject/net/resource/DiscreteResourceSetSerializer.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/EncodedResourcesSerializer.java
@@ -13,42 +13,33 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.net.resource;
+package org.onosproject.store.resource.impl;
 
 import com.esotericsoftware.kryo.Kryo;
 import com.esotericsoftware.kryo.Serializer;
 import com.esotericsoftware.kryo.io.Input;
 import com.esotericsoftware.kryo.io.Output;
-import com.google.common.annotations.Beta;
 import com.google.common.collect.DiscreteDomain;
 import com.google.common.collect.Range;
 import com.google.common.collect.TreeRangeSet;
 import org.onlab.util.ClosedOpenRange;
-import org.onlab.util.Tools;
+import org.onosproject.net.resource.DiscreteResourceCodec;
 
 import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
-import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
 /**
- * Kryo serializer for {@link DiscreteResourceSet}.
+ * Kryo Serializer for {@link EncodedDiscreteResources}.
  */
-@Beta
-public final class DiscreteResourceSetSerializer extends Serializer<DiscreteResourceSet> {
-
-    public DiscreteResourceSetSerializer() {
-        super(false, true);
-    }
-
+final class EncodedResourcesSerializer extends Serializer<EncodedDiscreteResources> {
     @Override
-    public void write(Kryo kryo, Output output, DiscreteResourceSet object) {
+    public void write(Kryo kryo, Output output, EncodedDiscreteResources object) {
         TreeRangeSet<Integer> rangeSet = TreeRangeSet.create();
-        object.values().stream()
-                .map(x -> x.valueAs(Object.class))
-                .flatMap(Tools::stream)
-                .map(x -> object.codec().encode(x))
+        object.rawValues().stream()
                 .map(Range::singleton)
                 .map(x -> x.canonical(DiscreteDomain.integers()))
                 .forEach(rangeSet::add);
@@ -57,26 +48,18 @@
                 .collect(Collectors.toList());
         kryo.writeObject(output, ranges);
         kryo.writeClassAndObject(output, object.codec());
-        kryo.writeObject(output, object.parent());
     }
 
     @Override
-    public DiscreteResourceSet read(Kryo kryo, Input input, Class<DiscreteResourceSet> type) {
+    public EncodedDiscreteResources read(Kryo kryo, Input input, Class<EncodedDiscreteResources> cls) {
         @SuppressWarnings("unchecked")
         List<ClosedOpenRange> ranges = kryo.readObject(input, ArrayList.class);
         DiscreteResourceCodec codec = (DiscreteResourceCodec) kryo.readClassAndObject(input);
-        DiscreteResourceId parent = kryo.readObject(input, DiscreteResourceId.class);
 
-        if (ranges.isEmpty()) {
-            return DiscreteResourceSet.empty();
-        }
-
-        Set<DiscreteResource> resources = ranges.stream()
+        HashSet<Integer> rawValues = ranges.stream()
                 .flatMapToInt(x -> IntStream.range(x.lowerBound(), x.upperBound()))
-                .mapToObj(x -> codec.decode(x))
-                .map(x -> Resources.discrete(parent, x).resource())
-                .collect(Collectors.toSet());
-
-        return DiscreteResourceSet.of(resources, codec);
+                .boxed()
+                .collect(Collectors.toCollection(LinkedHashSet::new));
+        return new EncodedDiscreteResources(rawValues, codec);
     }
 }
diff --git a/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java b/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
index 4073804..51232a4 100644
--- a/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
+++ b/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
@@ -194,10 +194,7 @@
 import org.onosproject.net.resource.DiscreteResource;
 import org.onosproject.net.resource.DiscreteResourceCodec;
 import org.onosproject.net.resource.DiscreteResourceId;
-import org.onosproject.net.resource.DiscreteResourceSet;
-import org.onosproject.net.resource.DiscreteResourceSetSerializer;
 import org.onosproject.net.resource.MplsCodec;
-import org.onosproject.net.resource.NoOpCodec;
 import org.onosproject.net.resource.ResourceAllocation;
 import org.onosproject.net.resource.ResourceConsumerId;
 import org.onosproject.net.packet.DefaultOutboundPacket;
@@ -537,11 +534,9 @@
                     org.onlab.packet.MPLS.class
             )
             .register(ClosedOpenRange.class)
-            .register(new DiscreteResourceSetSerializer(), DiscreteResourceSet.class)
             .register(DiscreteResourceCodec.class)
             .register(VlanCodec.class)
             .register(MplsCodec.class)
-            .register(NoOpCodec.class)
             .build("API");
 
 
diff --git a/core/store/serializers/src/test/java/org/onosproject/store/serializers/KryoSerializerTest.java b/core/store/serializers/src/test/java/org/onosproject/store/serializers/KryoSerializerTest.java
index 260a9a0..dedadaa 100644
--- a/core/store/serializers/src/test/java/org/onosproject/store/serializers/KryoSerializerTest.java
+++ b/core/store/serializers/src/test/java/org/onosproject/store/serializers/KryoSerializerTest.java
@@ -24,7 +24,6 @@
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
-import org.onlab.packet.MplsLabel;
 import org.onlab.packet.VlanId;
 import org.onlab.util.Bandwidth;
 import org.onlab.util.Frequency;
@@ -63,9 +62,6 @@
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.FlowRuleBatchEntry;
 import org.onosproject.net.intent.IntentId;
-import org.onosproject.net.resource.DiscreteResource;
-import org.onosproject.net.resource.DiscreteResourceSet;
-import org.onosproject.net.resource.MplsCodec;
 import org.onosproject.net.resource.ResourceAllocation;
 import org.onosproject.net.resource.ResourceConsumerId;
 import org.onosproject.net.resource.Resources;
@@ -84,15 +80,11 @@
 import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.Ip6Prefix;
 import org.onlab.packet.MacAddress;
-import org.onosproject.net.resource.VlanCodec;
 
 import java.nio.ByteBuffer;
 import java.util.Arrays;
 import java.util.Collections;
 import java.time.Duration;
-import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
 
 import static java.util.Arrays.asList;
 import static org.junit.Assert.*;
@@ -355,38 +347,6 @@
     }
 
     @Test
-    public void testVlanIdResourceSet() {
-        DiscreteResource port = Resources.discrete(DID1, P1).resource();
-
-        Set<DiscreteResource> vlans = IntStream.range(0, 4096)
-                .mapToObj(x -> VlanId.vlanId((short) x))
-                .map(x -> Resources.discrete(port.id(), x).resource())
-                .collect(Collectors.toSet());
-
-        DiscreteResourceSet sut = DiscreteResourceSet.of(vlans, new VlanCodec());
-        testSerializedEquals(sut);
-    }
-
-    @Test
-    public void testMplsLabelResourceSet() {
-        DiscreteResource port = Resources.discrete(DID1, P1).resource();
-
-        Set<DiscreteResource> labels = IntStream.range(0, 1024)
-                .mapToObj(MplsLabel::mplsLabel)
-                .map(x -> Resources.discrete(port.id(), x).resource())
-                .collect(Collectors.toSet());
-
-        DiscreteResourceSet sut = DiscreteResourceSet.of(labels, new MplsCodec());
-        testSerializedEquals(sut);
-    }
-
-    @Test
-    public void testEmptyResourceSet() {
-        DiscreteResourceSet sut = DiscreteResourceSet.empty();
-        testSerializedEquals(sut);
-    }
-
-    @Test
     public void testResourceId() {
         testSerializedEquals(Resources.discrete(DID1, P1).id());
     }