Define valueAs() to get the enclosed value and remove volume()

volume() is replaced by valueAs()

Change-Id: I3dbcbd6a0b8fcd28f0064272fe1fa6d7259e0a87
diff --git a/cli/src/main/java/org/onosproject/cli/net/AllocationsCommand.java b/cli/src/main/java/org/onosproject/cli/net/AllocationsCommand.java
index ccaebd4..52d9bb8 100644
--- a/cli/src/main/java/org/onosproject/cli/net/AllocationsCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/AllocationsCommand.java
@@ -115,7 +115,7 @@
 
             for (ResourceAllocation a : allocations) {
                 print("%s%s allocated by %s", Strings.repeat(" ", level + 1),
-                                          a.resource().last(), asVerboseString(a.consumer()));
+                                          a.resource().valueAs(Object.class).orElse(""), asVerboseString(a.consumer()));
             }
         }
     }
diff --git a/cli/src/main/java/org/onosproject/cli/net/ResourcesCommand.java b/cli/src/main/java/org/onosproject/cli/net/ResourcesCommand.java
index 3b8ca89..49a56c5 100644
--- a/cli/src/main/java/org/onosproject/cli/net/ResourcesCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/ResourcesCommand.java
@@ -128,7 +128,7 @@
             } else {
                 String resourceName = resource.last().getClass().getSimpleName();
 
-                String toString = String.valueOf(resource.last());
+                String toString = String.valueOf(resource.valueAs(Object.class).orElse(""));
                 if (toString.startsWith(resourceName)) {
                     print("%s%s", Strings.repeat(" ", level),
                           toString);
@@ -178,14 +178,13 @@
 
                 // aggregate into RangeSet
                 e.getValue().stream()
-                    .map(Resource::last)
                     .map(res -> {
-                            if (res instanceof VlanId) {
-                                return (long) ((VlanId) res).toShort();
-                            } else if (res instanceof MplsLabel) {
-                                return (long) ((MplsLabel) res).toInt();
-                            } else if (res instanceof TributarySlot) {
-                                return ((TributarySlot) res).index();
+                            if (res.isTypeOf(VlanId.class)) {
+                                return (long) res.valueAs(VlanId.class).get().toShort();
+                            } else if (res.isTypeOf(MplsLabel.class)) {
+                                return (long) res.valueAs(MplsLabel.class).get().toInt();
+                            } else if (res.isTypeOf(TributarySlot.class)) {
+                                return res.valueAs(TributarySlot.class).get().index();
                             }
                             // TODO support Lambda (OchSignal types)
                             return 0L;
diff --git a/core/api/src/main/java/org/onosproject/net/newresource/ContinuousResource.java b/core/api/src/main/java/org/onosproject/net/newresource/ContinuousResource.java
index a55124e..00221ae 100644
--- a/core/api/src/main/java/org/onosproject/net/newresource/ContinuousResource.java
+++ b/core/api/src/main/java/org/onosproject/net/newresource/ContinuousResource.java
@@ -57,24 +57,10 @@
     }
 
     /**
-     * The user of this methods must receive the return value as Double or double.
-     * Otherwise, this methods throws an exception.
-     *
-     * @param <T> type of the return value
-     * @return the volume of this resource
-     */
-    @SuppressWarnings("unchecked")
-    @Override
-    public <T> T volume() {
-        return (T) Double.valueOf(value);
-    }
-
-    /**
      * Returns the value of the resource amount.
      *
      * @return the value of the resource amount
      */
-    // FIXME: overlapping a purpose with volume()
     public double value() {
         return value;
     }
@@ -93,6 +79,24 @@
         return foundInAncestor || foundInLeaf;
     }
 
+    /**
+     * {@inheritDoc}
+     *
+     * A user must specify Double.class or double.class to avoid an empty value.
+     */
+    @Override
+    public <T> Optional<T> valueAs(Class<T> type) {
+        checkNotNull(type);
+
+        if (type == Object.class || type == double.class || type == Double.class) {
+            @SuppressWarnings("unchecked")
+            T value = (T) Double.valueOf(this.value);
+            return Optional.of(value);
+        }
+
+        return Optional.empty();
+    }
+
     @Override
     public Object last() {
         if (id.components().isEmpty()) {
@@ -138,7 +142,7 @@
     public String toString() {
         return MoreObjects.toStringHelper(this)
                 .add("id", id)
-                .add("volume", value)
+                .add("value", value)
                 .toString();
     }
 }
diff --git a/core/api/src/main/java/org/onosproject/net/newresource/DiscreteResource.java b/core/api/src/main/java/org/onosproject/net/newresource/DiscreteResource.java
index 6ce51d3..beb564a 100644
--- a/core/api/src/main/java/org/onosproject/net/newresource/DiscreteResource.java
+++ b/core/api/src/main/java/org/onosproject/net/newresource/DiscreteResource.java
@@ -56,20 +56,6 @@
         return type.isAssignableFrom(id.components().get(id.components().size() - 1).getClass());
     }
 
-    /**
-     * The user of this methods must receive the return value as the correct type.
-     * Otherwise, this methods throws an exception.
-     *
-     * @param <T> type of the return value
-     * @return the volume of this resource
-     */
-    @SuppressWarnings("unchecked")
-    @Override
-    // TODO: consider receiving Class<T> as an argument. Which approach is convenient?
-    public <T> T volume() {
-        return (T) last();
-    }
-
     @Override
     public boolean isSubTypeOf(Class<?> ancestor) {
         checkNotNull(ancestor);
@@ -82,6 +68,19 @@
     }
 
     @Override
+    public <T> Optional<T> valueAs(Class<T> type) {
+        checkNotNull(type);
+
+        if (!isTypeOf(type)) {
+            return Optional.empty();
+        }
+
+        @SuppressWarnings("unchecked")
+        T value = (T) id.components().get(id.components().size() - 1);
+        return Optional.of(value);
+    }
+
+    @Override
     public Object last() {
         if (id.components().isEmpty()) {
             return null;
@@ -133,7 +132,6 @@
     public String toString() {
         return MoreObjects.toStringHelper(this)
                 .add("id", id)
-                .add("volume", volume())
                 .toString();
     }
 }
diff --git a/core/api/src/main/java/org/onosproject/net/newresource/Resource.java b/core/api/src/main/java/org/onosproject/net/newresource/Resource.java
index f2a0c73..5572592 100644
--- a/core/api/src/main/java/org/onosproject/net/newresource/Resource.java
+++ b/core/api/src/main/java/org/onosproject/net/newresource/Resource.java
@@ -64,13 +64,15 @@
     boolean isSubTypeOf(Class<?> ancestor);
 
     /**
-     * Returns the volume of this resource.
+     * Returns value interpreted as the specified type. If the specified type is
+     * incompatible with the underlying value, an empty instance is returned.
      *
-     * @param <T> type of return value
-     * @return the volume of this resource
+     * @param type class instance specifying the type of return value
+     * @param <T> type of the return value
+     * @return the value of this resource as the specified type. If type mismatches,
+     * returns an empty instance.
      */
-    // TODO: think about other naming possibilities. amount? quantity?
-    <T> T volume();
+    <T> Optional<T> valueAs(Class<T> type);
 
     /**
      * Returns the last component of this instance.
diff --git a/core/api/src/test/java/org/onosproject/net/newresource/ResourceTest.java b/core/api/src/test/java/org/onosproject/net/newresource/ResourceTest.java
index bc6fd29..b404d10 100644
--- a/core/api/src/test/java/org/onosproject/net/newresource/ResourceTest.java
+++ b/core/api/src/test/java/org/onosproject/net/newresource/ResourceTest.java
@@ -126,18 +126,25 @@
     }
 
     @Test
-    public void testVolumeOfDiscrete() {
+    public void testValueOfDiscrete() {
         Resource resource = Resources.discrete(D1).resource();
 
-        DeviceId volume = resource.volume();
-        assertThat(volume, is(D1));
+        Optional<DeviceId> volume = resource.valueAs(DeviceId.class);
+        assertThat(volume.get(), is(D1));
     }
 
     @Test
-    public void testVolumeOfContinuous() {
+    public void testValueOfRoot() {
+        Resource resource = Resource.ROOT;
+
+        assertThat(resource.valueAs(Object.class), is(Optional.empty()));
+    }
+
+    @Test
+    public void testValueOfContinuous() {
         Resource resource = Resources.continuous(D1, P1, Bandwidth.class).resource(BW1.bps());
 
-        double volume = resource.volume();
-        assertThat(volume, is(BW1.bps()));
+        Optional<Double> volume = resource.valueAs(double.class);
+        assertThat(volume.get(), is(BW1.bps()));
     }
 }
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompiler.java
index 94b178e..71ae925 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompiler.java
@@ -27,6 +27,7 @@
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.MplsLabel;
 import org.onlab.packet.VlanId;
+import org.onlab.util.Tools;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.net.ConnectPoint;
@@ -158,8 +159,7 @@
 
     private Set<MplsLabel> findMplsLabel(ConnectPoint cp) {
         return resourceService.getAvailableResources(Resources.discrete(cp.deviceId(), cp.port()).id()).stream()
-                .filter(x -> x.last() instanceof MplsLabel)
-                .map(x -> (MplsLabel) x.last())
+                .flatMap(x -> Tools.stream(x.valueAs(MplsLabel.class)))
                 .collect(Collectors.toSet());
     }
 
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalConnectivityIntentCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalConnectivityIntentCompiler.java
index 5b741bc..b113b07 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalConnectivityIntentCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalConnectivityIntentCompiler.java
@@ -17,7 +17,6 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -25,6 +24,7 @@
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.onlab.util.Frequency;
+import org.onlab.util.Tools;
 import org.onosproject.net.AnnotationKeys;
 import org.onosproject.net.ChannelSpacing;
 import org.onosproject.net.ConnectPoint;
@@ -218,8 +218,8 @@
                         Resources.discrete(x.dst().deviceId(), x.dst().port()).id()
                 ))
                 .map(resourceService::getAvailableResources)
-                .map(x -> Iterables.filter(x, r -> r.last() instanceof OchSignal))
-                .map(x -> Iterables.transform(x, r -> (OchSignal) r.last()))
+                .map(x -> x.stream()
+                        .flatMap(r -> Tools.stream(r.valueAs(OchSignal.class))).collect(Collectors.toList()))
                 .map(x -> (Set<OchSignal>) ImmutableSet.copyOf(x))
                 .reduce(Sets::intersection)
                 .orElse(Collections.emptySet());
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentCompiler.java
index 7309a09..569f095 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentCompiler.java
@@ -23,6 +23,7 @@
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.onlab.packet.VlanId;
+import org.onlab.util.Tools;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.net.ConnectPoint;
@@ -286,8 +287,7 @@
 
     private Set<VlanId> findVlanId(ConnectPoint cp) {
         return resourceService.getAvailableResources(Resources.discrete(cp.deviceId(), cp.port()).id()).stream()
-                .filter(x -> x.last() instanceof VlanId)
-                .map(x -> (VlanId) x.last())
+                .flatMap(x -> Tools.stream(x.valueAs(VlanId.class)))
                 .collect(Collectors.toSet());
     }