import com.google.common.collect.Iterables;
import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Deque;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;
+import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.yangtools.yang.common.AbstractQName;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DerivableSchemaNode;
import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
-import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.ModuleImport;
import org.opendaylight.yangtools.yang.model.api.NotificationNodeContainer;
import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
import org.opendaylight.yangtools.yang.model.api.PathExpression;
+import org.opendaylight.yangtools.yang.model.api.PathExpression.DerefSteps;
import org.opendaylight.yangtools.yang.model.api.PathExpression.LocationPathSteps;
+import org.opendaylight.yangtools.yang.model.api.PathExpression.Steps;
import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaNode;
private static final CharMatcher SPACE = CharMatcher.is(' ');
private SchemaContextUtil() {
+ // Hidden on purpose
}
/**
- * Method attempts to find DataSchemaNode in Schema Context via specified
- * Schema Path. The returned DataSchemaNode from method will be the node at
- * the end of the SchemaPath. If the DataSchemaNode is not present in the
- * Schema Context the method will return <code>null</code>. <br>
- * In case that Schema Context or Schema Path are not specified correctly
- * (i.e. contains <code>null</code> values) the method will throw
- * IllegalArgumentException.
+ * Method attempts to find DataSchemaNode in Schema Context via specified Schema Path. The returned DataSchemaNode
+ * from method will be the node at the end of the SchemaPath. If the DataSchemaNode is not present in the Schema
+ * Context the method will return {@code null}.
*
- * @param context
- * Schema Context
- * @param schemaPath
- * Schema Path to search for
- * @return SchemaNode from the end of the Schema Path or <code>null</code>
- * if the Node is not present.
+ * <p>
+ * In case that Schema Context or Schema Path are not specified correctly (i.e. contains {@code null} values) the
+ * method will throw IllegalArgumentException.
+ *
+ * @param context Schema Context
+ * @param schemaPath Schema Path to search for
+ * @return SchemaNode from the end of the Schema Path or {@code null} if the Node is not present.
* @throws NullPointerException if context or schemaPath is null
*/
public static SchemaNode findDataSchemaNode(final SchemaContext context, final SchemaPath schemaPath) {
return findNodeInSchemaContext(context, prefixedPath);
}
+ /**
+ * Attempt to find a DataSchemaNode based on its path from root, similar to
+ * {@link #findDataSchemaNode(SchemaContext, Module, PathExpression)} without requiring an expression.
+ *
+ * @param context Schema Context
+ * @param path Path to search for
+ * @return SchemaNode from the end of the Schema Path or {@code null} if the Node is not present.
+ * @throws NullPointerException if a any argument is null or if the path contains a null element
+ */
+ @Beta
+ public static SchemaNode findDataSchemaNode(final SchemaContext context, final List<QName> path) {
+ return findTargetNode(context, null, YangLocationPath.absolute(
+ path.stream().map(YangXPathAxis.CHILD::asStep).collect(Collectors.toList())));
+ }
+
+ /**
+ * Attempt to find a DataSchemaNode based on its path from root, similar to
+ * {@link #findDataSchemaNode(SchemaContext, Module, PathExpression)} without requiring an expression.
+ *
+ * @param context Schema Context
+ * @param path Path to search for
+ * @return SchemaNode from the end of the Schema Path or {@code null} if the Node is not present.
+ * @throws NullPointerException if a any argument is null or if the path contains a null element
+ */
+ @Beta
+ public static SchemaNode findDataSchemaNode(final SchemaContext context, final QName... path) {
+ return findDataSchemaNode(context, Arrays.asList(path));
+ }
+
/**
* Method attempts to find DataSchemaNode inside of provided Schema Context
* and Yang Module accordingly to Non-conditional Revision Aware XPath. The
* Non-conditional Revision Aware XPath, or <code>null</code> if the
* DataSchemaNode is not present in Schema Context.
* @throws NullPointerException if any of the arguments is null
+ * @deprecated Use {@link #findDataTreeSchemaNode(SchemaContext, QNameModule, YangLocationPath)} or
+ * {@link #findDataTreeSchemaNode(SchemaContext, QNameModule, PathExpression)} instead.
*/
// FIXME: This entire method is ill-defined, as the resolution process depends on where the XPath is defined --
// notably RPCs, actions and notifications modify the data tree temporarily. See sections 6.4.1 and 9.9.2
//
// which would then be passed in to a method similar to this one. In static contexts, like MD-SAL codegen,
// that feels like an overkill.
+ @Deprecated
public static SchemaNode findDataSchemaNode(final SchemaContext context, final Module module,
final PathExpression nonCondXPath) {
requireNonNull(context, "context");
final String strXPath = nonCondXPath.getOriginalString();
checkArgument(strXPath.indexOf('[') == -1, "Revision Aware XPath may not contain a condition");
if (nonCondXPath.isAbsolute()) {
- final List<QName> path = xpathToQNamePath(context, module, strXPath);
-
- // We do not have enough information about resolution context, hence cannot account for actions, RPCs
- // and notifications. We therefore attempt to make a best estimate, but this can still fail.
- final Optional<DataSchemaNode> pureData = context.findDataTreeChild(path);
- return pureData.isPresent() ? pureData.get() : findNodeInSchemaContext(context, path);
+ return findTargetNode(context, xpathToQNamePath(context, module, strXPath));
}
return null;
}
+ @Beta
+ public static SchemaNode findDataTreeSchemaNode(final SchemaContext ctx, final QNameModule localModule,
+ final YangLocationPath absPath) {
+ checkArgument(absPath.isAbsolute(), "Unsupported relative path %s", absPath);
+ return findTargetNode(ctx, localModule, absPath);
+ }
+
+ @Beta
+ public static SchemaNode findDataTreeSchemaNode(final SchemaContext ctx, final QNameModule localModule,
+ final PathExpression absPath) {
+ final Steps pathSteps = absPath.getSteps();
+ if (pathSteps instanceof LocationPathSteps) {
+ return findDataTreeSchemaNode(ctx, localModule, ((LocationPathSteps) pathSteps).getLocationPath());
+ }
+
+ // We would need a reference schema node and no, we do not want to use SchemaContext in its SchemaNode capacity
+ checkArgument(!(pathSteps instanceof DerefSteps), "No reference node for steps %s", pathSteps);
+
+ // We are missing proper API alignment, if this ever triggers
+ throw new IllegalStateException("Unsupported path " + pathSteps);
+ }
+
/**
* Method attempts to find DataSchemaNode inside of provided Schema Context
* and Yang Module accordingly to Non-conditional relative Revision Aware
//
// which would then be passed in to a method similar to this one. In static contexts, like MD-SAL codegen,
// that feels like an overkill.
- // FIXME: YANGTOOLS-1052: this is a static analysis util, move it to yang-model-sa
+ // FIXME: YANGTOOLS-1052: this is a static analysis util, move it to a dedicated class
public static SchemaNode findDataSchemaNodeForRelativeXPath(final SchemaContext context, final Module module,
final SchemaNode actualSchemaNode, final PathExpression relativeXPath) {
checkState(!relativeXPath.isAbsolute(), "Revision Aware XPath MUST be relative i.e. MUST contains ../, "
final int paren = xpath.indexOf(')', 6);
checkArgument(paren != -1, "Cannot find matching parentheses in %s", xpath);
- final String derefArg = xpath.substring(6, paren);
+ final String derefArg = SPACE.trimFrom(xpath.substring(6, paren));
// Look up the node which we need to reference
final SchemaNode derefTarget = findTargetNode(context, resolveRelativePath(context, module, actualSchemaNode,
doSplitXPath(derefArg)));
LOG.debug("Derefencing path {}", targetPath);
final SchemaNode deref = targetPath.isAbsolute()
- ? findTargetNode(context, actualSchemaNode,
+ ? findTargetNode(context, actualSchemaNode.getQName().getModule(),
((LocationPathSteps) targetPath.getSteps()).getLocationPath())
: findDataSchemaNodeForRelativeXPath(context, module, actualSchemaNode, targetPath);
if (deref == null) {
return findTargetNode(context, resolveRelativePath(context, module, deref, qnames));
}
- private static @Nullable SchemaNode findTargetNode(final SchemaContext context, final SchemaNode actualSchemaNode,
+ private static @Nullable SchemaNode findTargetNode(final SchemaContext context, final QNameModule defaultModule,
final YangLocationPath path) {
- final QNameModule defaultModule = actualSchemaNode.getQName().getModule();
final Deque<QName> ret = new ArrayDeque<>();
for (Step step : path.getSteps()) {
if (step instanceof AxisStep) {
}
Module parentModule = findParentModuleOfReferencingType(schemaContext, baseSchema);
- dataSchemaNode = (DataSchemaNode) SchemaContextUtil.findDataSchemaNode(schemaContext, parentModule,
- pathStatement);
+ dataSchemaNode = (DataSchemaNode) findDataSchemaNode(schemaContext, parentModule, pathStatement);
} else {
Module parentModule = findParentModule(schemaContext, schema);
- dataSchemaNode = (DataSchemaNode) SchemaContextUtil.findDataSchemaNodeForRelativeXPath(schemaContext,
- parentModule, schema, pathStatement);
+ dataSchemaNode = (DataSchemaNode) findDataSchemaNodeForRelativeXPath(schemaContext, parentModule, schema,
+ pathStatement);
}
// FIXME this is just to preserve backwards compatibility since yangtools do not mind wrong leafref xpaths
public static TypeDefinition<?> getBaseTypeForLeafRef(final LeafrefTypeDefinition typeDefinition,
final SchemaContext schemaContext, final QName qname) {
final PathExpression pathStatement = typeDefinition.getPathStatement();
- final PathExpression strippedPathStatement = new PathExpressionImpl(
- stripConditionsFromXPathString(pathStatement), pathStatement.isAbsolute());
- if (!strippedPathStatement.isAbsolute()) {
+ if (!pathStatement.isAbsolute()) {
return null;
}
+ final PathExpression strippedPathStatement = new PathExpressionImpl(
+ stripConditionsFromXPathString(pathStatement), true);
+
final Optional<Module> parentModule = schemaContext.findModule(qname.getModule());
checkArgument(parentModule.isPresent(), "Failed to find parent module for %s", qname);
- final DataSchemaNode dataSchemaNode = (DataSchemaNode) SchemaContextUtil.findDataSchemaNode(schemaContext,
- parentModule.get(), strippedPathStatement);
+ final DataSchemaNode dataSchemaNode = (DataSchemaNode) findDataSchemaNode(schemaContext, parentModule.get(),
+ strippedPathStatement);
final TypeDefinition<?> targetTypeDefinition = typeDefinition(dataSchemaNode);
if (targetTypeDefinition instanceof LeafrefTypeDefinition) {
return getBaseTypeForLeafRef((LeafrefTypeDefinition) targetTypeDefinition, schemaContext, dataSchemaNode);
return schemaContext.findModule(nodeType.getQName().getModule()).orElse(null);
}
- return SchemaContextUtil.findParentModule(schemaContext, schemaNode);
+ return findParentModule(schemaContext, schemaNode);
}
private static final Pattern STRIP_PATTERN = Pattern.compile("\\[[^\\[\\]]*\\]");
return STRIP_PATTERN.matcher(pathStatement.getOriginalString()).replaceAll("");
}
- /**
- * Extracts the base type of leaf schema node until it reach concrete type of TypeDefinition.
- *
- * @param node
- * a node representing LeafSchemaNode
- * @return concrete type definition of node value
- */
- private static TypeDefinition<?> typeDefinition(final LeafSchemaNode node) {
- TypeDefinition<?> baseType = node.getType();
- while (baseType.getBaseType() != null) {
- baseType = baseType.getBaseType();
- }
- return baseType;
- }
-
- /**
- * Extracts the base type of leaf schema node until it reach concrete type of TypeDefinition.
- *
- * @param node
- * a node representing LeafListSchemaNode
- * @return concrete type definition of node value
- */
- private static TypeDefinition<?> typeDefinition(final LeafListSchemaNode node) {
- TypeDefinition<?> baseType = node.getType();
- while (baseType.getBaseType() != null) {
- baseType = baseType.getBaseType();
- }
- return baseType;
- }
-
/**
* Gets the base type of DataSchemaNode value.
*
* @return concrete type definition of node value
*/
private static TypeDefinition<?> typeDefinition(final DataSchemaNode node) {
- if (node instanceof LeafListSchemaNode) {
- return typeDefinition((LeafListSchemaNode) node);
- } else if (node instanceof LeafSchemaNode) {
- return typeDefinition((LeafSchemaNode) node);
- } else {
- throw new IllegalArgumentException("Unhandled parameter type: " + node);
+ checkArgument(node instanceof TypedDataSchemaNode, "Unhandled parameter type %s", node);
+
+ TypeDefinition<?> current = ((TypedDataSchemaNode) node).getType();
+ // TODO: don't we have a utility for this?
+ TypeDefinition<?> base = current.getBaseType();
+ while (base != null) {
+ current = base;
+ base = current.getBaseType();
}
+ return current;
}
}