Turn ApiPath into a record
[netconf.git] / restconf / restconf-nb / src / main / java / org / opendaylight / restconf / nb / rfc8040 / rests / transactions / RestconfStrategy.java
index 98b767bd7bcc51638c9bcd7c75d99106abbbf482..cb175b55d4fcd267f77e721f27b1386535161474 100644 (file)
@@ -9,6 +9,7 @@ package org.opendaylight.restconf.nb.rfc8040.rests.transactions;
 
 import static com.google.common.base.Verify.verifyNotNull;
 import static java.util.Objects.requireNonNull;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.fromInstanceId;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableMap;
@@ -49,8 +50,8 @@ import org.opendaylight.mdsal.dom.api.DOMMountPointService;
 import org.opendaylight.mdsal.dom.api.DOMRpcResult;
 import org.opendaylight.mdsal.dom.api.DOMRpcService;
 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
+import org.opendaylight.mdsal.dom.api.DOMSchemaService.YangTextSourceExtension;
 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
-import org.opendaylight.mdsal.dom.api.DOMYangTextSourceProvider;
 import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult;
 import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
 import org.opendaylight.restconf.api.ApiPath;
@@ -64,20 +65,17 @@ import org.opendaylight.restconf.common.patch.PatchContext;
 import org.opendaylight.restconf.common.patch.PatchStatusContext;
 import org.opendaylight.restconf.common.patch.PatchStatusEntity;
 import org.opendaylight.restconf.nb.rfc8040.Insert;
-import org.opendaylight.restconf.nb.rfc8040.databind.ChildBody;
-import org.opendaylight.restconf.nb.rfc8040.databind.DataPostBody;
-import org.opendaylight.restconf.nb.rfc8040.databind.OperationInputBody;
-import org.opendaylight.restconf.nb.rfc8040.databind.PatchBody;
-import org.opendaylight.restconf.nb.rfc8040.databind.ResourceBody;
 import org.opendaylight.restconf.nb.rfc8040.legacy.ErrorTags;
 import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
 import org.opendaylight.restconf.nb.rfc8040.legacy.QueryParameters;
 import org.opendaylight.restconf.nb.rfc8040.utils.parser.YangInstanceIdentifierSerializer;
+import org.opendaylight.restconf.server.api.ChildBody;
 import org.opendaylight.restconf.server.api.ConfigurationMetadata;
 import org.opendaylight.restconf.server.api.DataGetParams;
 import org.opendaylight.restconf.server.api.DataGetResult;
 import org.opendaylight.restconf.server.api.DataPatchPath;
 import org.opendaylight.restconf.server.api.DataPatchResult;
+import org.opendaylight.restconf.server.api.DataPostBody;
 import org.opendaylight.restconf.server.api.DataPostPath;
 import org.opendaylight.restconf.server.api.DataPostResult;
 import org.opendaylight.restconf.server.api.DataPostResult.CreateResource;
@@ -86,14 +84,17 @@ import org.opendaylight.restconf.server.api.DataPutPath;
 import org.opendaylight.restconf.server.api.DataPutResult;
 import org.opendaylight.restconf.server.api.DataYangPatchResult;
 import org.opendaylight.restconf.server.api.DatabindContext;
+import org.opendaylight.restconf.server.api.OperationInputBody;
 import org.opendaylight.restconf.server.api.OperationsGetResult;
 import org.opendaylight.restconf.server.api.OperationsPostPath;
 import org.opendaylight.restconf.server.api.OperationsPostResult;
+import org.opendaylight.restconf.server.api.PatchBody;
+import org.opendaylight.restconf.server.api.ResourceBody;
 import org.opendaylight.restconf.server.spi.ApiPathNormalizer;
-import org.opendaylight.restconf.server.spi.ApiPathNormalizer.DataPath;
 import org.opendaylight.restconf.server.spi.ApiPathNormalizer.InstanceReference;
-import org.opendaylight.restconf.server.spi.ApiPathNormalizer.OperationPath;
-import org.opendaylight.restconf.server.spi.ApiPathNormalizer.OperationPath.Rpc;
+import org.opendaylight.restconf.server.spi.ApiPathNormalizer.Path.Action;
+import org.opendaylight.restconf.server.spi.ApiPathNormalizer.Path.Data;
+import org.opendaylight.restconf.server.spi.ApiPathNormalizer.Path.Rpc;
 import org.opendaylight.restconf.server.spi.OperationInput;
 import org.opendaylight.restconf.server.spi.RpcImplementation;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.with.defaults.rev110601.WithDefaultsMode;
@@ -127,8 +128,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.UserMapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.builder.CollectionNodeBuilder;
 import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder;
 import org.opendaylight.yangtools.yang.data.api.schema.builder.NormalizedNodeContainerBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
-import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes;
 import org.opendaylight.yangtools.yang.data.util.DataSchemaContext;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
@@ -136,13 +136,13 @@ import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.source.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.source.SourceRepresentation;
+import org.opendaylight.yangtools.yang.model.api.source.YangTextSource;
+import org.opendaylight.yangtools.yang.model.api.source.YinTextSource;
 import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.RpcEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleEffectiveStatement;
-import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
-import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
-import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
-import org.opendaylight.yangtools.yang.model.repo.api.YinTextSchemaSource;
 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
 import org.slf4j.Logger;
@@ -158,7 +158,7 @@ import org.slf4j.LoggerFactory;
 //        operations. This should be handled through proper allocation indirection.
 public abstract class RestconfStrategy {
     @NonNullByDefault
-    public record StrategyAndPath(RestconfStrategy strategy, DataPath path) {
+    public record StrategyAndPath(RestconfStrategy strategy, Data path) {
         public StrategyAndPath {
             requireNonNull(strategy);
             requireNonNull(path);
@@ -188,14 +188,14 @@ public abstract class RestconfStrategy {
     private final @NonNull ImmutableMap<QName, RpcImplementation> localRpcs;
     private final @NonNull ApiPathNormalizer pathNormalizer;
     private final @NonNull DatabindContext databind;
-    private final DOMYangTextSourceProvider sourceProvider;
+    private final YangTextSourceExtension sourceProvider;
     private final DOMMountPointService mountPointService;
     private final DOMActionService actionService;
     private final DOMRpcService rpcService;
 
     RestconfStrategy(final DatabindContext databind, final ImmutableMap<QName, RpcImplementation> localRpcs,
             final @Nullable DOMRpcService rpcService, final @Nullable DOMActionService actionService,
-            final DOMYangTextSourceProvider sourceProvider, final @Nullable DOMMountPointService mountPointService) {
+            final YangTextSourceExtension sourceProvider, final @Nullable DOMMountPointService mountPointService) {
         this.databind = requireNonNull(databind);
         this.localRpcs = requireNonNull(localRpcs);
         this.rpcService = rpcService;
@@ -251,7 +251,7 @@ public abstract class RestconfStrategy {
         final var rpcService = mountPoint.getService(DOMRpcService.class).orElse(null);
         final var actionService = mountPoint.getService(DOMActionService.class).orElse(null);
         final var sourceProvider = mountPoint.getService(DOMSchemaService.class)
-            .flatMap(schema -> Optional.ofNullable(schema.getExtensions().getInstance(DOMYangTextSourceProvider.class)))
+            .flatMap(schema -> Optional.ofNullable(schema.extension(YangTextSourceExtension.class)))
             .orElse(null);
 
         final var netconfService = mountPoint.getService(NetconfDataTreeService.class);
@@ -333,7 +333,7 @@ public abstract class RestconfStrategy {
 
     public @NonNull RestconfFuture<DataPutResult> dataPUT(final ApiPath apiPath, final ResourceBody body,
             final Map<String, String> queryParameters) {
-        final DataPath path;
+        final Data path;
         try {
             path = pathNormalizer.normalizeDataPath(apiPath);
         } catch (RestconfDocumentedException e) {
@@ -446,7 +446,7 @@ public abstract class RestconfStrategy {
         }
 
         int lastInsertedPosition = 0;
-        final var emptySubtree = ImmutableNodes.fromInstanceId(modelContext(), path.getParent());
+        final var emptySubtree = fromInstanceId(modelContext(), path.getParent());
         tx.merge(YangInstanceIdentifier.of(emptySubtree.name()), emptySubtree);
         for (var nodeChild : readList.body()) {
             if (lastInsertedPosition == lastItemPosition) {
@@ -456,6 +456,13 @@ public abstract class RestconfStrategy {
             tx.replace(childPath, nodeChild);
             lastInsertedPosition++;
         }
+
+        // In case we are inserting after last element
+        if (!before) {
+            if (lastInsertedPosition == lastItemPosition) {
+                tx.replace(path, data);
+            }
+        }
     }
 
     private static ListenableFuture<? extends CommitInfo> replaceAndCommit(final RestconfTransaction tx,
@@ -497,9 +504,8 @@ public abstract class RestconfStrategy {
             final NormalizedNode data, final @Nullable Insert insert) {
         final ListenableFuture<? extends CommitInfo> future;
         if (insert != null) {
-            final var parentPath = path.coerceParent();
-            checkListAndOrderedType(parentPath);
-            future = insertAndCommitPost(path, data, insert, parentPath);
+            checkListAndOrderedType(path);
+            future = insertAndCommitPost(path, data, insert);
         } else {
             future = createAndCommit(prepareWriteExecution(), path, data);
         }
@@ -523,41 +529,40 @@ public abstract class RestconfStrategy {
     }
 
     private ListenableFuture<? extends CommitInfo> insertAndCommitPost(final YangInstanceIdentifier path,
-            final NormalizedNode data, final @NonNull Insert insert, final YangInstanceIdentifier parent) {
-        final var grandParent = parent.coerceParent();
+            final NormalizedNode data, final @NonNull Insert insert) {
         final var tx = prepareWriteExecution();
 
         return switch (insert.insert()) {
             case FIRST -> {
-                final var readData = tx.readList(grandParent);
+                final var readData = tx.readList(path);
                 if (readData == null || readData.isEmpty()) {
                     tx.replace(path, data);
                 } else {
-                    checkItemDoesNotExists(exists(path), path);
-                    tx.remove(grandParent);
+                    checkListDataDoesNotExist(path, data);
+                    tx.remove(path);
                     tx.replace(path, data);
-                    tx.replace(grandParent, readData);
+                    tx.replace(path, readData);
                 }
                 yield tx.commit();
             }
             case LAST -> createAndCommit(tx, path, data);
             case BEFORE -> {
-                final var readData = tx.readList(grandParent);
+                final var readData = tx.readList(path);
                 if (readData == null || readData.isEmpty()) {
                     tx.replace(path, data);
                 } else {
-                    checkItemDoesNotExists(exists(path), path);
-                    insertWithPointPost(tx, path, data, verifyNotNull(insert.pointArg()), readData, grandParent, true);
+                    checkListDataDoesNotExist(path, data);
+                    insertWithPointPost(tx, path, data, verifyNotNull(insert.pointArg()), readData, true);
                 }
                 yield tx.commit();
             }
             case AFTER -> {
-                final var readData = tx.readList(grandParent);
+                final var readData = tx.readList(path);
                 if (readData == null || readData.isEmpty()) {
                     tx.replace(path, data);
                 } else {
-                    checkItemDoesNotExists(exists(path), path);
-                    insertWithPointPost(tx, path, data, verifyNotNull(insert.pointArg()), readData, grandParent, false);
+                    checkListDataDoesNotExist(path, data);
+                    insertWithPointPost(tx, path, data, verifyNotNull(insert.pointArg()), readData, false);
                 }
                 yield tx.commit();
             }
@@ -574,7 +579,7 @@ public abstract class RestconfStrategy {
      * @throws NullPointerException if any argument is {@code null}
      */
     public final @NonNull RestconfFuture<DataPatchResult> dataPATCH(final ApiPath apiPath, final ResourceBody body) {
-        final DataPath path;
+        final Data path;
         try {
             path = pathNormalizer.normalizeDataPath(apiPath);
         } catch (RestconfDocumentedException e) {
@@ -592,7 +597,7 @@ public abstract class RestconfStrategy {
     }
 
     public final @NonNull RestconfFuture<DataYangPatchResult> dataPATCH(final ApiPath apiPath, final PatchBody body) {
-        final DataPath path;
+        final Data path;
         try {
             path = pathNormalizer.normalizeDataPath(apiPath);
         } catch (RestconfDocumentedException e) {
@@ -715,8 +720,8 @@ public abstract class RestconfStrategy {
 
     private void insertWithPointPost(final RestconfTransaction tx, final YangInstanceIdentifier path,
             final NormalizedNode data, final PathArgument pointArg, final NormalizedNodeContainer<?> readList,
-            final YangInstanceIdentifier grandParentPath, final boolean before) {
-        tx.remove(grandParentPath);
+            final boolean before) {
+        tx.remove(path);
 
         int lastItemPosition = 0;
         for (var nodeChild : readList.body()) {
@@ -730,15 +735,20 @@ public abstract class RestconfStrategy {
         }
 
         int lastInsertedPosition = 0;
-        final var emptySubtree = ImmutableNodes.fromInstanceId(modelContext(), grandParentPath);
-        tx.merge(YangInstanceIdentifier.of(emptySubtree.name()), emptySubtree);
         for (var nodeChild : readList.body()) {
             if (lastInsertedPosition == lastItemPosition) {
                 tx.replace(path, data);
             }
-            tx.replace(grandParentPath.node(nodeChild.name()), nodeChild);
+            tx.replace(path.node(nodeChild.name()), nodeChild);
             lastInsertedPosition++;
         }
+
+        // In case we are inserting after last element
+        if (!before) {
+            if (lastInsertedPosition == lastItemPosition) {
+                tx.replace(path, data);
+            }
+        }
     }
 
     private static ListenableFuture<? extends CommitInfo> createAndCommit(final RestconfTransaction tx,
@@ -754,6 +764,23 @@ public abstract class RestconfStrategy {
         return tx.commit();
     }
 
+    /**
+     * Check if child items do NOT already exists in List at specified {@code path}.
+     *
+     * @param data Data to be checked
+     * @param path Path to be checked
+     * @throws RestconfDocumentedException if data already exists.
+     */
+    private void checkListDataDoesNotExist(final YangInstanceIdentifier path, final NormalizedNode data) {
+        if (data instanceof NormalizedNodeContainer<?> dataNode) {
+            for (final var node : dataNode.body()) {
+                checkItemDoesNotExists(exists(path.node(node.name())), path.node(node.name()));
+            }
+        } else {
+            throw new RestconfDocumentedException("Unexpected node type: " + data.getClass().getName());
+        }
+    }
+
     /**
      * Check if items do NOT already exists at specified {@code path}.
      *
@@ -780,7 +807,7 @@ public abstract class RestconfStrategy {
      */
     @SuppressWarnings("checkstyle:abbreviationAsWordInName")
     public final @NonNull RestconfFuture<Empty> dataDELETE(final ApiPath apiPath) {
-        final DataPath path;
+        final Data path;
         try {
             path = pathNormalizer.normalizeDataPath(apiPath);
         } catch (RestconfDocumentedException e) {
@@ -797,7 +824,7 @@ public abstract class RestconfStrategy {
 
     public final @NonNull RestconfFuture<DataGetResult> dataGET(final ApiPath apiPath,
             final DataGetParams params) {
-        final DataPath path;
+        final Data path;
         try {
             path = pathNormalizer.normalizeDataPath(apiPath);
         } catch (RestconfDocumentedException e) {
@@ -806,7 +833,7 @@ public abstract class RestconfStrategy {
         return dataGET(path, params);
     }
 
-    abstract @NonNull RestconfFuture<DataGetResult> dataGET(DataPath path, DataGetParams params);
+    abstract @NonNull RestconfFuture<DataGetResult> dataGET(Data path, DataGetParams params);
 
     static final @NonNull RestconfFuture<DataGetResult> completeDataGET(final Inference inference,
             final QueryParameters queryParams, final @Nullable NormalizedNode node,
@@ -832,7 +859,8 @@ public abstract class RestconfStrategy {
      * @return {@link NormalizedNode}
      */
     // FIXME: NETCONF-1155: this method should asynchronous
-    public final @Nullable NormalizedNode readData(final @NonNull ContentParam content,
+    @VisibleForTesting
+    final @Nullable NormalizedNode readData(final @NonNull ContentParam content,
             final @NonNull YangInstanceIdentifier path, final WithDefaultsParam defaultsMode) {
         return switch (content) {
             case ALL -> {
@@ -870,7 +898,7 @@ public abstract class RestconfStrategy {
         // FIXME: we have this readily available in InstanceIdentifierContext
         final var ctxNode = databind.schemaTree().findChild(path).orElseThrow();
         if (readData instanceof ContainerNode container) {
-            final var builder = Builders.containerBuilder().withNodeIdentifier(container.name());
+            final var builder = ImmutableNodes.newContainerBuilder().withNodeIdentifier(container.name());
             buildCont(builder, container.body(), ctxNode, trim);
             return builder.build();
         } else if (readData instanceof MapEntryNode mapEntry) {
@@ -878,7 +906,7 @@ public abstract class RestconfStrategy {
                 throw new IllegalStateException("Input " + mapEntry + " does not match " + ctxNode);
             }
 
-            final var builder = Builders.mapEntryBuilder().withNodeIdentifier(mapEntry.name());
+            final var builder = ImmutableNodes.newMapEntryBuilder().withNodeIdentifier(mapEntry.name());
             buildMapEntryBuilder(builder, mapEntry.body(), ctxNode, trim, listSchema.getKeyDefinition());
             return builder.build();
         } else {
@@ -908,7 +936,7 @@ public abstract class RestconfStrategy {
 
     private static void appendContainer(final DataContainerNodeBuilder<?, ?> builder, final ContainerNode container,
             final DataSchemaContext ctxNode, final boolean trim) {
-        final var childBuilder = Builders.containerBuilder().withNodeIdentifier(container.name());
+        final var childBuilder = ImmutableNodes.newContainerBuilder().withNodeIdentifier(container.name());
         buildCont(childBuilder, container.body(), ctxNode, trim);
         builder.withChild(childBuilder.build());
     }
@@ -972,8 +1000,8 @@ public abstract class RestconfStrategy {
         }
 
         final var childBuilder = switch (map.ordering()) {
-            case SYSTEM -> Builders.mapBuilder();
-            case USER -> Builders.orderedMapBuilder();
+            case SYSTEM -> ImmutableNodes.newSystemMapBuilder();
+            case USER -> ImmutableNodes.newUserMapBuilder();
         };
         buildList(childBuilder.withNodeIdentifier(map.name()), map.body(), childCtx, trim,
             listSchema.getKeyDefinition());
@@ -985,7 +1013,7 @@ public abstract class RestconfStrategy {
             final List<@NonNull QName> keys) {
         for (var entry : entries) {
             final var childCtx = getChildContext(ctxNode, entry);
-            final var mapEntryBuilder = Builders.mapEntryBuilder().withNodeIdentifier(entry.name());
+            final var mapEntryBuilder = ImmutableNodes.newMapEntryBuilder().withNodeIdentifier(entry.name());
             buildMapEntryBuilder(mapEntryBuilder, entry.body(), childCtx, trim, keys);
             builder.withChild(mapEntryBuilder.build());
         }
@@ -1018,22 +1046,15 @@ public abstract class RestconfStrategy {
 
     static final NormalizedNode mergeConfigAndSTateDataIfNeeded(final NormalizedNode stateDataNode,
             final NormalizedNode configDataNode) {
-        // if no data exists
-        if (stateDataNode == null && configDataNode == null) {
-            return null;
-        }
-
-        // return config data
         if (stateDataNode == null) {
+            // No state, return config
             return configDataNode;
         }
-
-        // return state data
         if (configDataNode == null) {
+            // No config, return state
             return stateDataNode;
         }
-
-        // merge data from config and state
+        // merge config and state
         return mergeStateAndConfigData(stateDataNode, configDataNode);
     }
 
@@ -1079,7 +1100,7 @@ public abstract class RestconfStrategy {
      */
     private static @NonNull NormalizedNode prepareRpcData(final @NonNull NormalizedNode configDataNode,
                                                           final @NonNull NormalizedNode stateDataNode) {
-        final var mapEntryBuilder = Builders.mapEntryBuilder()
+        final var mapEntryBuilder = ImmutableNodes.newMapEntryBuilder()
             .withNodeIdentifier((NodeIdentifierWithPredicates) configDataNode.name());
 
         // MAP CONFIG DATA
@@ -1087,7 +1108,7 @@ public abstract class RestconfStrategy {
         // MAP STATE DATA
         mapRpcDataNode(stateDataNode, mapEntryBuilder);
 
-        return Builders.mapBuilder()
+        return ImmutableNodes.newSystemMapBuilder()
             .withNodeIdentifier(NodeIdentifier.create(configDataNode.name().getNodeType()))
             .addChild(mapEntryBuilder.build())
             .build();
@@ -1115,23 +1136,23 @@ public abstract class RestconfStrategy {
     private static @NonNull NormalizedNode prepareData(final @NonNull NormalizedNode configDataNode,
                                                        final @NonNull NormalizedNode stateDataNode) {
         if (configDataNode instanceof UserMapNode configMap) {
-            final var builder = Builders.orderedMapBuilder().withNodeIdentifier(configMap.name());
+            final var builder = ImmutableNodes.newUserMapBuilder().withNodeIdentifier(configMap.name());
             mapValueToBuilder(configMap.body(), ((UserMapNode) stateDataNode).body(), builder);
             return builder.build();
         } else if (configDataNode instanceof SystemMapNode configMap) {
-            final var builder = Builders.mapBuilder().withNodeIdentifier(configMap.name());
+            final var builder = ImmutableNodes.newSystemMapBuilder().withNodeIdentifier(configMap.name());
             mapValueToBuilder(configMap.body(), ((SystemMapNode) stateDataNode).body(), builder);
             return builder.build();
         } else if (configDataNode instanceof MapEntryNode configEntry) {
-            final var builder = Builders.mapEntryBuilder().withNodeIdentifier(configEntry.name());
+            final var builder = ImmutableNodes.newMapEntryBuilder().withNodeIdentifier(configEntry.name());
             mapValueToBuilder(configEntry.body(), ((MapEntryNode) stateDataNode).body(), builder);
             return builder.build();
         } else if (configDataNode instanceof ContainerNode configContaienr) {
-            final var builder = Builders.containerBuilder().withNodeIdentifier(configContaienr.name());
+            final var builder = ImmutableNodes.newContainerBuilder().withNodeIdentifier(configContaienr.name());
             mapValueToBuilder(configContaienr.body(), ((ContainerNode) stateDataNode).body(), builder);
             return builder.build();
         } else if (configDataNode instanceof ChoiceNode configChoice) {
-            final var builder = Builders.choiceBuilder().withNodeIdentifier(configChoice.name());
+            final var builder = ImmutableNodes.newChoiceBuilder().withNodeIdentifier(configChoice.name());
             mapValueToBuilder(configChoice.body(), ((ChoiceNode) stateDataNode).body(), builder);
             return builder.build();
         } else if (configDataNode instanceof LeafNode configLeaf) {
@@ -1139,23 +1160,24 @@ public abstract class RestconfStrategy {
             return configLeaf;
         } else if (configDataNode instanceof UserLeafSetNode) {
             final var configLeafSet = (UserLeafSetNode<Object>) configDataNode;
-            final var builder = Builders.<Object>orderedLeafSetBuilder().withNodeIdentifier(configLeafSet.name());
+            final var builder = ImmutableNodes.<Object>newUserLeafSetBuilder().withNodeIdentifier(configLeafSet.name());
             mapValueToBuilder(configLeafSet.body(), ((UserLeafSetNode<Object>) stateDataNode).body(), builder);
             return builder.build();
         } else if (configDataNode instanceof SystemLeafSetNode) {
             final var configLeafSet = (SystemLeafSetNode<Object>) configDataNode;
-            final var builder = Builders.<Object>leafSetBuilder().withNodeIdentifier(configLeafSet.name());
+            final var builder = ImmutableNodes.<Object>newSystemLeafSetBuilder()
+                .withNodeIdentifier(configLeafSet.name());
             mapValueToBuilder(configLeafSet.body(), ((SystemLeafSetNode<Object>) stateDataNode).body(), builder);
             return builder.build();
         } else if (configDataNode instanceof LeafSetEntryNode<?> configEntry) {
             // config trumps oper
             return configEntry;
         } else if (configDataNode instanceof UnkeyedListNode configList) {
-            final var builder = Builders.unkeyedListBuilder().withNodeIdentifier(configList.name());
+            final var builder = ImmutableNodes.newUnkeyedListBuilder().withNodeIdentifier(configList.name());
             mapValueToBuilder(configList.body(), ((UnkeyedListNode) stateDataNode).body(), builder);
             return builder.build();
         } else if (configDataNode instanceof UnkeyedListEntryNode configEntry) {
-            final var builder = Builders.unkeyedListEntryBuilder().withNodeIdentifier(configEntry.name());
+            final var builder = ImmutableNodes.newUnkeyedListEntryBuilder().withNodeIdentifier(configEntry.name());
             mapValueToBuilder(configEntry.body(), ((UnkeyedListEntryNode) stateDataNode).body(), builder);
             return builder.build();
         } else {
@@ -1234,8 +1256,8 @@ public abstract class RestconfStrategy {
                 .collect(ImmutableSet.toImmutableSet());
             if (!rpcNames.isEmpty()) {
                 final var namespace = entry.getKey();
-                table.computeIfAbsent(namespace.getNamespace(), ignored -> new HashMap<>())
-                    .put(namespace.getRevision().orElse(null), rpcNames);
+                table.computeIfAbsent(namespace.namespace(), ignored -> new HashMap<>())
+                    .put(namespace.revision(), rpcNames);
             }
         }
 
@@ -1245,7 +1267,7 @@ public abstract class RestconfStrategy {
             entry.getValue().entrySet().stream()
             .sorted(Comparator.comparing(Entry::getKey, (first, second) -> Revision.compare(second, first)))
             .findFirst()
-            .ifPresent(row -> rpcs.putAll(QNameModule.create(entry.getKey(), row.getKey()), row.getValue()));
+            .ifPresent(row -> rpcs.putAll(QNameModule.of(entry.getKey(), row.getKey()), row.getValue()));
         }
         return RestconfFuture.of(new OperationsGetResult.Container(modelContext, rpcs.build()));
     }
@@ -1262,13 +1284,12 @@ public abstract class RestconfStrategy {
             return RestconfFuture.failed(e);
         }
 
-        return RestconfFuture.of(
-            new OperationsGetResult.Leaf(rpc.inference().getEffectiveModelContext(), rpc.rpc().argument()));
+        return RestconfFuture.of(new OperationsGetResult.Leaf(rpc.inference().modelContext(), rpc.rpc().argument()));
     }
 
     public @NonNull RestconfFuture<OperationsPostResult> operationsPOST(final URI restconfURI, final ApiPath apiPath,
             final OperationInputBody body) {
-        final OperationPath.Rpc path;
+        final Rpc path;
         try {
             path = pathNormalizer.normalizeRpcPath(apiPath);
         } catch (RestconfDocumentedException e) {
@@ -1326,14 +1347,14 @@ public abstract class RestconfStrategy {
     }
 
     public @NonNull RestconfFuture<CharSource> resolveSource(final SourceIdentifier source,
-            final Class<? extends SchemaSourceRepresentation> representation) {
+            final Class<? extends SourceRepresentation> representation) {
         final var src = requireNonNull(source);
-        if (YangTextSchemaSource.class.isAssignableFrom(representation)) {
+        if (YangTextSource.class.isAssignableFrom(representation)) {
             if (sourceProvider != null) {
                 final var ret = new SettableRestconfFuture<CharSource>();
-                Futures.addCallback(sourceProvider.getSource(src), new FutureCallback<YangTextSchemaSource>() {
+                Futures.addCallback(sourceProvider.getYangTexttSource(src), new FutureCallback<>() {
                     @Override
-                    public void onSuccess(final YangTextSchemaSource result) {
+                    public void onSuccess(final YangTextSource result) {
                         ret.set(result);
                     }
 
@@ -1348,7 +1369,7 @@ public abstract class RestconfStrategy {
             }
             return exportSource(modelContext(), src, YangCharSource::new, YangCharSource::new);
         }
-        if (YinTextSchemaSource.class.isAssignableFrom(representation)) {
+        if (YinTextSource.class.isAssignableFrom(representation)) {
             return exportSource(modelContext(), src, YinCharSource.OfModule::new, YinCharSource.OfSubmodule::new);
         }
         return RestconfFuture.failed(new RestconfDocumentedException(
@@ -1394,13 +1415,13 @@ public abstract class RestconfStrategy {
         } catch (RestconfDocumentedException e) {
             return RestconfFuture.failed(e);
         }
-        if (path instanceof DataPath dataPath) {
+        if (path instanceof Data dataPath) {
             try (var resourceBody = body.toResource()) {
                 return dataCreatePOST(new DataPostPath(databind, dataPath.inference(), dataPath.instance()),
                     resourceBody, queryParameters);
             }
         }
-        if (path instanceof OperationPath.Action actionPath) {
+        if (path instanceof Action actionPath) {
             try (var inputBody = body.toOperationInput()) {
                 return dataInvokePOST(actionPath, inputBody);
             }
@@ -1439,8 +1460,7 @@ public abstract class RestconfStrategy {
         return ret;
     }
 
-    private @NonNull RestconfFuture<InvokeOperation> dataInvokePOST(final OperationPath.Action path,
-            final OperationInputBody body) {
+    private @NonNull RestconfFuture<InvokeOperation> dataInvokePOST(final Action path, final OperationInputBody body) {
         final var inference = path.inference();
         final ContainerNode input;
         try {
@@ -1472,12 +1492,12 @@ public abstract class RestconfStrategy {
      * @return {@link DOMActionResult}
      */
     private static RestconfFuture<DOMActionResult> dataInvokePOST(final DOMActionService actionService,
-            final OperationPath.Action path, final @NonNull ContainerNode input) {
+            final Action path, final @NonNull ContainerNode input) {
         final var ret = new SettableRestconfFuture<DOMActionResult>();
 
         Futures.addCallback(actionService.invokeAction(
             path.inference().toSchemaInferenceStack().toSchemaNodeIdentifier(),
-            new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, path.instance()), input),
+            DOMDataTreeIdentifier.of(LogicalDatastoreType.OPERATIONAL, path.instance()), input),
             new FutureCallback<DOMActionResult>() {
                 @Override
                 public void onSuccess(final DOMActionResult result) {