Refactor modulesGET() methods
[netconf.git] / restconf / restconf-nb / src / main / java / org / opendaylight / restconf / nb / rfc8040 / legacy / InstanceIdentifierContext.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.restconf.nb.rfc8040.legacy;
9
10 import static com.google.common.base.Verify.verify;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.annotations.VisibleForTesting;
14 import com.google.common.collect.ImmutableList;
15 import org.eclipse.jdt.annotation.NonNull;
16 import org.eclipse.jdt.annotation.Nullable;
17 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
18 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
19 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
20 import org.opendaylight.restconf.api.ApiPath;
21 import org.opendaylight.restconf.api.ApiPath.Step;
22 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
23 import org.opendaylight.restconf.nb.rfc8040.utils.parser.IdentifierCodec;
24 import org.opendaylight.restconf.nb.rfc8040.utils.parser.YangInstanceIdentifierDeserializer;
25 import org.opendaylight.yangtools.yang.common.ErrorTag;
26 import org.opendaylight.yangtools.yang.common.ErrorType;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
28 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
29 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
30 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
31 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
32 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
33
34 public abstract class InstanceIdentifierContext {
35     private static final class Root extends InstanceIdentifierContext {
36         private final @NonNull EffectiveModelContext context;
37
38         Root(final EffectiveModelContext context, final DOMMountPoint mountPoint) {
39             super(context, mountPoint);
40             this.context = requireNonNull(context);
41         }
42
43         @Override
44         public EffectiveModelContext getSchemaContext() {
45             return context;
46         }
47
48         @Override
49         public YangInstanceIdentifier getInstanceIdentifier() {
50             return YangInstanceIdentifier.of();
51         }
52
53         @Override
54         public Inference inference() {
55             return SchemaInferenceStack.of(context).toInference();
56         }
57     }
58
59     private static final class DataPath extends InstanceIdentifierContext {
60         private final @NonNull YangInstanceIdentifier path;
61         private final @NonNull SchemaInferenceStack stack;
62
63         private DataPath(final SchemaNode schemaNode, final DOMMountPoint mountPoint,
64                 final SchemaInferenceStack stack, final YangInstanceIdentifier path) {
65             super(schemaNode, mountPoint);
66             this.stack = requireNonNull(stack);
67             this.path = requireNonNull(path);
68         }
69
70         static @NonNull DataPath of(final EffectiveModelContext context, final YangInstanceIdentifier path,
71                 final DOMMountPoint mountPoint) {
72             final var nodeAndStack = DataSchemaContextTree.from(context).enterPath(path).orElseThrow();
73             return new DataPath(nodeAndStack.node().dataSchemaNode(), mountPoint, nodeAndStack.stack(), path);
74         }
75
76         @Override
77         public YangInstanceIdentifier getInstanceIdentifier() {
78             return path;
79         }
80
81         @Override
82         public Inference inference() {
83             return stack.toInference();
84         }
85     }
86
87     private static final class WithoutDataPath extends InstanceIdentifierContext {
88         private final @NonNull SchemaInferenceStack stack;
89
90         private WithoutDataPath(final SchemaNode schemaNode, final DOMMountPoint mountPoint,
91                 final SchemaInferenceStack stack) {
92             super(schemaNode, mountPoint);
93             this.stack = requireNonNull(stack);
94         }
95
96         @Override
97         public Inference inference() {
98             return stack.toInference();
99         }
100
101         @Override
102         public @Nullable YangInstanceIdentifier getInstanceIdentifier() {
103             return null;
104         }
105     }
106
107     private final @NonNull SchemaNode schemaNode;
108     private final @Nullable DOMMountPoint mountPoint;
109
110     InstanceIdentifierContext(final SchemaNode schemaNode, final DOMMountPoint mountPoint) {
111         this.schemaNode = requireNonNull(schemaNode);
112         this.mountPoint = mountPoint;
113     }
114
115     // FIXME: NETCONF-773: this recursion should really live in MdsalRestconfServer
116     public static @NonNull InstanceIdentifierContext ofApiPath(final ApiPath path,
117             final EffectiveModelContext modelContext, final DOMMountPointService mountPointService) {
118         final var steps = path.steps();
119         final var limit = steps.size() - 1;
120
121         var prefix = 0;
122         DOMMountPoint currentMountPoint = null;
123         var currentModelContext = modelContext;
124         while (prefix <= limit) {
125             final var mount = indexOfMount(steps, prefix, limit);
126             if (mount == -1) {
127                 break;
128             }
129
130             final var mountService = currentMountPoint == null ? mountPointService
131                 : currentMountPoint.getService(DOMMountPointService.class).orElse(null);
132             if (mountService == null) {
133                 throw new RestconfDocumentedException("Mount point service is not available",
134                     ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED);
135             }
136
137             final var mountPath = IdentifierCodec.deserialize(path.subPath(prefix, mount), modelContext);
138             final var userPath = path.subPath(0, mount);
139             final var nextMountPoint = mountService.getMountPoint(mountPath)
140                 .orElseThrow(() -> new RestconfDocumentedException("Mount point '" + userPath + "' does not exist",
141                     ErrorType.PROTOCOL, ErrorTags.RESOURCE_DENIED_TRANSPORT));
142             final var nextModelContext = nextMountPoint.getService(DOMSchemaService.class)
143                 .orElseThrow(() -> new RestconfDocumentedException(
144                     "Mount point '" + userPath + "' does not expose DOMSchemaService",
145                     ErrorType.PROTOCOL, ErrorTags.RESOURCE_DENIED_TRANSPORT))
146                 .getGlobalContext();
147             if (nextModelContext == null) {
148                 throw new RestconfDocumentedException("Mount point '" + userPath + "' does not have any models",
149                     ErrorType.PROTOCOL, ErrorTags.RESOURCE_DENIED_TRANSPORT);
150             }
151
152             prefix = mount + 1;
153             currentModelContext = nextModelContext;
154             currentMountPoint = nextMountPoint;
155         }
156
157         final var result = YangInstanceIdentifierDeserializer.create(currentModelContext, path.subPath(prefix));
158         return InstanceIdentifierContext.ofPath(result.stack, result.node, result.path, currentMountPoint);
159     }
160
161     private static int indexOfMount(final ImmutableList<Step> steps, final int fromIndex, final int limit) {
162         for (int i = fromIndex; i <= limit; ++ i) {
163             final var step = steps.get(i);
164             if ("yang-ext".equals(step.module()) && "mount".equals(step.identifier().getLocalName())) {
165                 return i;
166             }
167         }
168         return -1;
169     }
170
171     public static @NonNull InstanceIdentifierContext ofLocalRoot(final EffectiveModelContext context) {
172         return new Root(context, null);
173     }
174
175     @VisibleForTesting
176     public static @NonNull InstanceIdentifierContext ofLocalPath(final EffectiveModelContext context,
177             final YangInstanceIdentifier path) {
178         return DataPath.of(context, path, null);
179     }
180
181     // Invocations of various identifier-less details
182     public static @NonNull InstanceIdentifierContext ofStack(final SchemaInferenceStack stack) {
183         return ofStack(stack, null);
184     }
185
186     // Invocations of various identifier-less details, potentially having a mount point
187     public static @NonNull InstanceIdentifierContext ofStack(final SchemaInferenceStack stack,
188             final @Nullable DOMMountPoint mountPoint) {
189         final SchemaNode schemaNode;
190         if (!stack.isEmpty()) {
191             final var stmt = stack.currentStatement();
192             verify(stmt instanceof SchemaNode, "Unexpected statement %s", stmt);
193             schemaNode = (SchemaNode) stmt;
194         } else {
195             schemaNode = stack.getEffectiveModelContext();
196         }
197
198         return new WithoutDataPath(schemaNode, mountPoint, stack);
199     }
200
201     public static @NonNull InstanceIdentifierContext ofPath(final SchemaInferenceStack stack,
202             final SchemaNode schemaNode, final YangInstanceIdentifier path,
203             final @Nullable DOMMountPoint mountPoint) {
204         return new DataPath(schemaNode, mountPoint, stack, path);
205     }
206
207     public static @NonNull InstanceIdentifierContext ofMountPointRoot(final DOMMountPoint mountPoint,
208             final EffectiveModelContext mountContext) {
209         return new Root(mountContext, requireNonNull(mountPoint));
210     }
211
212     @VisibleForTesting
213     public static @NonNull InstanceIdentifierContext ofMountPointPath(final DOMMountPoint mountPoint,
214             final EffectiveModelContext context, final YangInstanceIdentifier path) {
215         return DataPath.of(context, path, requireNonNull(mountPoint));
216     }
217
218     public final @NonNull SchemaNode getSchemaNode() {
219         return schemaNode;
220     }
221
222     public final @Nullable DOMMountPoint getMountPoint() {
223         return mountPoint;
224     }
225
226     public @NonNull EffectiveModelContext getSchemaContext() {
227         return inference().getEffectiveModelContext();
228     }
229
230     public abstract @NonNull Inference inference();
231
232     public abstract @Nullable YangInstanceIdentifier getInstanceIdentifier();
233 }