Define a feature-parent
[yangtools.git] / data / yang-data-tree-ri / src / main / java / org / opendaylight / yangtools / yang / data / tree / impl / di / InMemoryDataTreeFactory.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.data.tree.impl.di;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11
12 import javax.inject.Inject;
13 import javax.inject.Singleton;
14 import org.eclipse.jdt.annotation.NonNull;
15 import org.kohsuke.MetaInfServices;
16 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
17 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
18 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
19 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
20 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
21 import org.opendaylight.yangtools.yang.data.api.schema.DistinctNodeContainer;
22 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
23 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode.BuilderFactory;
24 import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes;
25 import org.opendaylight.yangtools.yang.data.tree.api.DataTree;
26 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidate;
27 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeConfiguration;
28 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeFactory;
29 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeModification;
30 import org.opendaylight.yangtools.yang.data.tree.api.DataValidationFailedException;
31 import org.opendaylight.yangtools.yang.data.tree.impl.InMemoryDataTree;
32 import org.opendaylight.yangtools.yang.data.tree.impl.node.TreeNode;
33 import org.opendaylight.yangtools.yang.data.tree.impl.node.Version;
34 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
35 import org.opendaylight.yangtools.yang.model.api.ContainerLike;
36 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
38 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
39 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
40 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
41 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
42 import org.osgi.service.component.annotations.Activate;
43 import org.osgi.service.component.annotations.Component;
44 import org.osgi.service.component.annotations.Deactivate;
45 import org.osgi.service.component.annotations.RequireServiceComponentRuntime;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49 /**
50  * A factory for creating in-memory data trees.
51  */
52 @Singleton
53 @Component
54 @MetaInfServices
55 @RequireServiceComponentRuntime
56 public final class InMemoryDataTreeFactory implements DataTreeFactory {
57     private static final Logger LOG = LoggerFactory.getLogger(InMemoryDataTreeFactory.class);
58     private static final BuilderFactory BUILDER_FACTORY = ImmutableNodes.builderFactory();
59     // FIXME: YANGTOOLS-1074: we do not want this name
60     private static final @NonNull ContainerNode ROOT_CONTAINER = BUILDER_FACTORY.newContainerBuilder(0)
61         .withNodeIdentifier(NodeIdentifier.create(SchemaContext.NAME))
62         .build();
63
64     @Inject
65     public InMemoryDataTreeFactory() {
66         // Exposed for DI
67     }
68
69     @Override
70     public DataTree create(final DataTreeConfiguration treeConfig) {
71         return new InMemoryDataTree(TreeNode.of(createRoot(treeConfig.getRootPath()),
72             Version.initial()), treeConfig, null);
73     }
74
75     @Override
76     public DataTree create(final DataTreeConfiguration treeConfig, final EffectiveModelContext initialSchemaContext) {
77         return createDataTree(treeConfig, initialSchemaContext, true);
78     }
79
80     @Override
81     public DataTree create(final DataTreeConfiguration treeConfig, final EffectiveModelContext initialSchemaContext,
82             final DistinctNodeContainer<?, ?> initialRoot) throws DataValidationFailedException {
83         final DataTree ret = createDataTree(treeConfig, initialSchemaContext, false);
84
85         final DataTreeModification mod = ret.takeSnapshot().newModification();
86         mod.write(YangInstanceIdentifier.of(), initialRoot);
87         mod.ready();
88
89         ret.validate(mod);
90         final DataTreeCandidate candidate = ret.prepare(mod);
91         ret.commit(candidate);
92         return ret;
93     }
94
95     @Activate
96     @SuppressWarnings("static-method")
97     void activate() {
98         LOG.debug("In-memory Data Tree activated");
99     }
100
101     @Deactivate
102     @SuppressWarnings("static-method")
103     void deactivate() {
104         LOG.debug("In-memory Data Tree deactivated");
105     }
106
107     private static @NonNull DataTree createDataTree(final DataTreeConfiguration treeConfig,
108             final EffectiveModelContext initialSchemaContext, final boolean maskMandatory) {
109         final DataSchemaNode rootSchemaNode = getRootSchemaNode(initialSchemaContext, treeConfig.getRootPath());
110         final NormalizedNode rootDataNode = createRoot((DataNodeContainer)rootSchemaNode,
111             treeConfig.getRootPath());
112         return new InMemoryDataTree(TreeNode.of(rootDataNode, Version.initial()), treeConfig,
113             initialSchemaContext, rootSchemaNode, maskMandatory);
114     }
115
116     private static @NonNull NormalizedNode createRoot(final DataNodeContainer schemaNode,
117             final YangInstanceIdentifier path) {
118         if (path.isEmpty()) {
119             checkArgument(schemaNode instanceof ContainerLike,
120                 "Conceptual tree root has to be a container, not %s", schemaNode);
121             return ROOT_CONTAINER;
122         }
123
124         final PathArgument arg = path.getLastPathArgument();
125         if (schemaNode instanceof ContainerSchemaNode) {
126             checkArgument(arg instanceof NodeIdentifier, "Mismatched container %s path %s", schemaNode, path);
127             return BUILDER_FACTORY.newContainerBuilder().withNodeIdentifier((NodeIdentifier) arg).build();
128         } else if (schemaNode instanceof ListSchemaNode listSchema) {
129             // This can either be a top-level list or its individual entry
130             if (arg instanceof NodeIdentifierWithPredicates nip) {
131                 return BUILDER_FACTORY.newMapEntryBuilder().withNodeIdentifier(nip).build();
132             }
133             checkArgument(arg instanceof NodeIdentifier, "Mismatched list %s path %s", schemaNode, path);
134             final var builder = listSchema.isUserOrdered() ? BUILDER_FACTORY.newUserMapBuilder()
135                 : BUILDER_FACTORY.newSystemMapBuilder();
136             return builder.withNodeIdentifier((NodeIdentifier) arg).build();
137         } else {
138             throw new IllegalArgumentException("Unsupported root schema " + schemaNode);
139         }
140     }
141
142     private static @NonNull NormalizedNode createRoot(final YangInstanceIdentifier path) {
143         if (path.isEmpty()) {
144             return ROOT_CONTAINER;
145         }
146
147         final PathArgument arg = path.getLastPathArgument();
148         if (arg instanceof NodeIdentifier nodeId) {
149             return BUILDER_FACTORY.newContainerBuilder().withNodeIdentifier(nodeId).build();
150         }
151         if (arg instanceof NodeIdentifierWithPredicates nip) {
152             return BUILDER_FACTORY.newMapEntryBuilder().withNodeIdentifier(nip).build();
153         }
154
155         // FIXME: implement augmentations and leaf-lists
156         throw new IllegalArgumentException("Unsupported root node " + arg);
157     }
158
159     private static DataSchemaNode getRootSchemaNode(final EffectiveModelContext schemaContext,
160             final YangInstanceIdentifier rootPath) {
161         final var contextTree = DataSchemaContextTree.from(schemaContext);
162         final var rootContextNode = contextTree.childByPath(rootPath);
163         checkArgument(rootContextNode != null, "Failed to find root %s in schema context", rootPath);
164
165         final var rootSchemaNode = rootContextNode.dataSchemaNode();
166         checkArgument(rootSchemaNode instanceof DataNodeContainer, "Root %s resolves to non-container type %s",
167             rootPath, rootSchemaNode);
168         return rootSchemaNode;
169     }
170 }