Refactor ResourcePath internal implementation

Change-Id: Idb3aa467b4d0e8181adf0d1766812a038b5408ac
diff --git a/core/api/src/main/java/org/onosproject/net/newresource/ResourcePath.java b/core/api/src/main/java/org/onosproject/net/newresource/ResourcePath.java
index 3aa29f6..da7d346 100644
--- a/core/api/src/main/java/org/onosproject/net/newresource/ResourcePath.java
+++ b/core/api/src/main/java/org/onosproject/net/newresource/ResourcePath.java
@@ -20,6 +20,7 @@
 import com.google.common.collect.ImmutableList;
 
 import java.util.Arrays;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
@@ -39,18 +40,11 @@
 @Beta
 public final class ResourcePath {
 
-    private final List<Object> resources;
+    private final ResourcePath parent;
+    private final Object last;
 
     public static final ResourcePath ROOT = new ResourcePath(ImmutableList.of());
 
-    public static ResourcePath child(ResourcePath parent, Object child) {
-        ImmutableList<Object> components = ImmutableList.builder()
-                .addAll(parent.components())
-                .add(child)
-                .build();
-        return new ResourcePath(components);
-    }
-
     /**
      * Creates an resource path from the specified components.
      *
@@ -67,13 +61,32 @@
      */
     public ResourcePath(List<Object> components) {
         checkNotNull(components);
+        if (components.isEmpty()) {
+            this.parent = null;
+            this.last = null;
+            return;
+        }
 
-        this.resources = ImmutableList.copyOf(components);
+        LinkedList<Object> children = new LinkedList<>(components);
+        this.last = children.pollLast();
+        this.parent = new ResourcePath(children);
+    }
+
+    /**
+     * Creates an resource path from the specified parent and child.
+     *
+     * @param parent the parent of this resource
+     * @param last a child of the parent
+     */
+    public ResourcePath(ResourcePath parent, Object last) {
+        this.parent = checkNotNull(parent);
+        this.last = checkNotNull(last);
     }
 
     // for serialization
     private ResourcePath() {
-        this.resources = null;
+        this.parent = null;
+        this.last = null;
     }
 
     /**
@@ -82,7 +95,15 @@
      * @return the components of this resource path
      */
     public List<Object> components() {
-        return resources;
+        LinkedList<Object> components = new LinkedList<>();
+
+        ResourcePath parentPath = parent;
+        while (parentPath != null) {
+            components.addFirst(last);
+            parentPath = parent.parent;
+        }
+
+        return components;
     }
 
     /**
@@ -93,20 +114,11 @@
      * If there is no parent, empty instance will be returned.
      */
     public Optional<ResourcePath> parent() {
-        if (!isRoot()) {
-            return Optional.of(new ResourcePath(resources.subList(0, resources.size() - 1)));
-        }
-
-        return Optional.empty();
+        return Optional.ofNullable(parent);
     }
 
-    /**
-     * Returns true if the path represents root.
-     *
-     * @return true if the path represents root, false otherwise.
-     */
-    public boolean isRoot() {
-        return resources.size() == 0;
+    public ResourcePath child(Object child) {
+        return new ResourcePath(this, child);
     }
 
     /**
@@ -115,14 +127,13 @@
      * @return the last component of this instance.
      * The return value is equal to the last object of {@code components()}.
      */
-    public Object lastComponent() {
-        int last = resources.size() - 1;
-        return resources.get(last);
+    public Object last() {
+        return last;
     }
 
     @Override
     public int hashCode() {
-        return resources.hashCode();
+        return Objects.hash(this.parent, this.last);
     }
 
     @Override
@@ -134,13 +145,15 @@
             return false;
         }
         final ResourcePath that = (ResourcePath) obj;
-        return Objects.equals(this.resources, that.resources);
+        return Objects.equals(this.parent, that.parent)
+                && Objects.equals(this.last, that.last);
     }
 
     @Override
     public String toString() {
         return MoreObjects.toStringHelper(this)
-                .add("resources", resources)
+                .add("parent", parent)
+                .add("last", last)
                 .toString();
     }
 }
diff --git a/core/api/src/test/java/org/onosproject/net/newresource/ResourcePathTest.java b/core/api/src/test/java/org/onosproject/net/newresource/ResourcePathTest.java
index 4a8886a..4d10dc4 100644
--- a/core/api/src/test/java/org/onosproject/net/newresource/ResourcePathTest.java
+++ b/core/api/src/test/java/org/onosproject/net/newresource/ResourcePathTest.java
@@ -76,7 +76,7 @@
         LinkKey linkKey = LinkKey.linkKey(CP1_1, CP2_1);
         ResourcePath path = new ResourcePath(linkKey);
 
-        LinkKey child = (LinkKey) path.lastComponent();
+        LinkKey child = (LinkKey) path.last();
         assertThat(child, is(linkKey));
     }
 }
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 acc5a5d..b75ca5d 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
@@ -146,8 +146,8 @@
 
     private Optional<MplsLabel> findMplsLabel(LinkKey link) {
         return resourceService.getAvailableResources(new ResourcePath(link)).stream()
-                .filter(x -> x.lastComponent() instanceof MplsLabel)
-                .map(x -> (MplsLabel) x.lastComponent())
+                .filter(x -> x.last() instanceof MplsLabel)
+                .map(x -> (MplsLabel) x.last())
                 .findFirst();
     }
 
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 d6725b7..05caa66 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
@@ -183,7 +183,7 @@
         IndexedLambda minLambda = findFirstLambda(lambdas);
         List<ResourcePath> lambdaResources = path.links().stream()
                 .map(x -> new ResourcePath(linkKey(x.src(), x.dst())))
-                .map(x -> ResourcePath.child(x, minLambda))
+                .map(x -> x.child(minLambda))
                 .collect(Collectors.toList());
 
         List<ResourceAllocation> allocations = resourceService.allocate(intent.id(), lambdaResources);
@@ -198,8 +198,8 @@
         return links.stream()
                 .map(x -> new ResourcePath(linkKey(x.src(), x.dst())))
                 .map(resourceService::getAvailableResources)
-                .map(x -> Iterables.filter(x, r -> r.lastComponent() instanceof IndexedLambda))
-                .map(x -> Iterables.transform(x, r -> (IndexedLambda) r.lastComponent()))
+                .map(x -> Iterables.filter(x, r -> r.last() instanceof IndexedLambda))
+                .map(x -> Iterables.transform(x, r -> (IndexedLambda) r.last()))
                 .map(x -> (Set<IndexedLambda>) ImmutableSet.copyOf(x))
                 .reduce(Sets::intersection)
                 .orElse(Collections.emptySet());
diff --git a/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceManager.java b/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceManager.java
index 77a32f4..1c6930b 100644
--- a/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceManager.java
+++ b/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceManager.java
@@ -169,7 +169,7 @@
         checkNotNull(children);
         checkArgument(!children.isEmpty());
 
-        List<ResourcePath> resources = Lists.transform(children, x -> ResourcePath.child(parent, x));
+        List<ResourcePath> resources = Lists.transform(children, parent::child);
         return store.register(resources);
     }
 
@@ -179,7 +179,7 @@
         checkNotNull(children);
         checkArgument(!children.isEmpty());
 
-        List<ResourcePath> resources = Lists.transform(children, x -> ResourcePath.child(parent, x));
+        List<ResourcePath> resources = Lists.transform(children, parent::child);
         return store.unregister(resources);
     }
 
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MockResourceService.java b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MockResourceService.java
index 8ec09bd..f5d3d0f 100644
--- a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MockResourceService.java
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MockResourceService.java
@@ -90,7 +90,7 @@
 
     @Override
     public Collection<ResourcePath> getAvailableResources(ResourcePath parent) {
-        ResourcePath resource = ResourcePath.child(parent, MplsLabel.mplsLabel(10));
+        ResourcePath resource = parent.child(MplsLabel.mplsLabel(10));
         return ImmutableList.of(resource);
     }
 
diff --git a/core/store/dist/src/main/java/org/onosproject/store/newresource/impl/ConsistentResourceStore.java b/core/store/dist/src/main/java/org/onosproject/store/newresource/impl/ConsistentResourceStore.java
index 4d9e3cb..0335ba5 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/newresource/impl/ConsistentResourceStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/newresource/impl/ConsistentResourceStore.java
@@ -264,7 +264,7 @@
         }
 
         return children.value().stream()
-                .filter(x -> x.lastComponent().getClass().equals(cls))
+                .filter(x -> x.last().getClass().equals(cls))
                 .filter(consumerMap::containsKey)
                 .collect(Collectors.toList());
     }
@@ -344,7 +344,7 @@
      */
     private boolean isRegistered(TransactionalMap<ResourcePath, List<ResourcePath>> map, ResourcePath resource) {
         // root is always regarded to be registered
-        if (resource.isRoot()) {
+        if (!resource.parent().isPresent()) {
             return true;
         }