2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2024 Ericsson
4 * Modifications Copyright (C) 2024 OpenInfra Foundation Europe
5 * ================================================================================
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 * SPDX-License-Identifier: Apache-2.0
19 * ============LICENSE_END=========================================================
21 package org.oran.smo.yangtools.parser.model.schema;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.HashSet;
26 import java.util.List;
29 import org.oran.smo.yangtools.parser.ParserExecutionContext;
30 import org.oran.smo.yangtools.parser.findings.Finding;
31 import org.oran.smo.yangtools.parser.findings.ParserFindingType;
32 import org.oran.smo.yangtools.parser.model.ConformanceType;
33 import org.oran.smo.yangtools.parser.model.ModuleIdentity;
34 import org.oran.smo.yangtools.parser.model.YangModel;
35 import org.oran.smo.yangtools.parser.model.resolvers.Helper;
36 import org.oran.smo.yangtools.parser.model.statements.AbstractStatement;
37 import org.oran.smo.yangtools.parser.model.statements.StatementModuleAndName;
38 import org.oran.smo.yangtools.parser.model.statements.yang.CY;
39 import org.oran.smo.yangtools.parser.model.statements.yang.YCase;
40 import org.oran.smo.yangtools.parser.model.statements.yang.YChoice;
41 import org.oran.smo.yangtools.parser.model.statements.yang.YFeature;
42 import org.oran.smo.yangtools.parser.model.statements.yang.YIfFeature;
43 import org.oran.smo.yangtools.parser.model.statements.yang.YInput;
44 import org.oran.smo.yangtools.parser.model.statements.yang.YModule;
45 import org.oran.smo.yangtools.parser.model.statements.yang.YOutput;
46 import org.oran.smo.yangtools.parser.model.statements.yang.YStatus;
47 import org.oran.smo.yangtools.parser.model.statements.yang.YSubmodule;
48 import org.oran.smo.yangtools.parser.model.statements.yang.YIfFeature.Token;
49 import org.oran.smo.yangtools.parser.model.util.YangFeature;
50 import org.oran.smo.yangtools.parser.model.yangdom.YangDomElement;
51 import org.oran.smo.yangtools.parser.util.QNameHelper;
54 * Processes the schema.
56 * @author Mark Hollmann
58 public abstract class SchemaProcessor {
60 // =================================== SUBMODULE HANDLING =====================================
62 public static void resolveSubmodules(final Schema schema) {
64 for (final YangModel yangModelFile : schema.getModuleRegistry().getAllYangModels()) {
65 if (yangModelFile.getYangModelRoot().isSubmodule()) {
66 mergeInSubmodule(schema, yangModelFile);
72 * Merges the content of submodules into their owning modules. This makes processing later on
73 * considerably easier (e.g. much easier to find the target node for an augment or deviation).
75 * Only the statement tree is manipulated, the DOM tree remains unchanged.
77 private static void mergeInSubmodule(final Schema schema, final YangModel yangModel) {
79 final YSubmodule submodule = yangModel.getYangModelRoot().getSubmodule();
81 final String belongsToModuleName = submodule.getBelongsToValue();
82 if (belongsToModuleName == null) {
83 // No need for a finding, an invalid syntax finding would have previously issued.
87 final List<YangModel> moduleYangFiles = schema.getModuleRegistry().byModuleName(belongsToModuleName);
88 if (moduleYangFiles.size() != 1) {
89 // No need to record a finding - would have been caught previously.
94 * We are basically taking all the schema nodes and re-house them into the module. Note: The prefix-resolver
95 * of all of these statements will not be adjusted, i.e. they will keep the prefix resolver of the submodule.
96 * This is to handle a situation where the prefixes declared are different between those in the module and those
97 * in the submodule(s) - which is frequently the case.
99 final YModule owningModule = moduleYangFiles.get(0).getYangModelRoot().getModule();
100 if (owningModule == null) {
101 // submodule owned by submodule, which is wrong; no need to record a finding - would have been caught previously.
105 owningModule.addChildren(submodule.getAnydata());
106 owningModule.addChildren(submodule.getAnyxmls());
107 owningModule.addChildren(submodule.getAugments());
108 owningModule.addChildren(submodule.getChoices());
109 owningModule.addChildren(submodule.getContainers());
110 owningModule.addChildren(submodule.getDeviations());
111 owningModule.addChildren(submodule.getExtensions());
112 owningModule.addChildren(submodule.getFeatures());
113 owningModule.addChildren(submodule.getGroupings());
114 owningModule.addChildren(submodule.getIdentities());
115 owningModule.addChildren(submodule.getLeafLists());
116 owningModule.addChildren(submodule.getLeafs());
117 owningModule.addChildren(submodule.getLists());
118 owningModule.addChildren(submodule.getNotifications());
119 owningModule.addChildren(submodule.getRpcs());
120 owningModule.addChildren(submodule.getTypedefs());
121 owningModule.addChildren(submodule.getUses());
124 // =================================== CASE HANDLING =====================================
126 @SuppressWarnings("unchecked")
127 public static void fixupOmittedCaseStatements(final Schema schema) {
128 final List<YChoice> allChoices = (List<YChoice>) Helper.findStatementsInSchema(CY.STMT_CHOICE, schema);
129 for (final YChoice oneChoice : allChoices) {
130 injectCaseForShorthandedStatements(oneChoice);
134 private static final Set<StatementModuleAndName> POSSIBLY_SHORTHANDED_STATEMENTS = new HashSet<>(Arrays.asList(
135 CY.STMT_ANYDATA, CY.STMT_ANYXML, CY.STMT_CHOICE, CY.STMT_CONTAINER, CY.STMT_LEAF_LIST, CY.STMT_LEAF,
139 * Injects a 'case' statement above any data node-defining statement found directly under
140 * a 'choice'. In Yang, this is referred-to as "shorthand". Example:
143 * container option1 { ... }
144 * container option2 { ... }
147 * Usage of shorthand causes problems, as schema node paths referring to data nodes within
148 * the choice must also use the identity of the (omitted) case statement. To simplify
149 * navigation later on, we inject these missing case statements here. The above example is
150 * effectively modified to:
154 * container option1 { ... }
157 * container option2 { ... }
161 * Both the DOM tree and the statement tree are adjusted. The artificial 'case' statement
162 * will be given the same name and line number as the statement defining the data node.
164 * The parent statement is usually a 'choice', but could be an 'augment' or 'grouping',
165 * which inject into a 'choice'.
167 public static void injectCaseForShorthandedStatements(final AbstractStatement parentStatement) {
169 final List<AbstractStatement> shorthandedStatements = parentStatement.getChildren(POSSIBLY_SHORTHANDED_STATEMENTS);
171 for (final AbstractStatement shorthandedStatement : shorthandedStatements) {
173 * The identifier of the case statement is equal to the identifier of the short-handed statement.
175 final String caseIdentifier = shorthandedStatement.getStatementIdentifier();
177 * Create new DOM element for the 'case' statement first. It will sit below the parent
178 * statement and has no children (yet). As line number, we will give it the line number
179 * of the short-handed statement.
181 final YangDomElement caseDomElement = new YangDomElement(CY.CASE, caseIdentifier, parentStatement
182 .getDomElement(), shorthandedStatement.getDomElement().getLineNumber());
184 * Now create the 'case' statement under the parent statement, and then move
185 * the short-handed statement under the 'case'. That cleans up the statement tree.
187 final YCase newCase = new YCase(parentStatement, caseDomElement);
188 newCase.addChild(shorthandedStatement);
190 * We still need to clean up the DOM tree - we do something very similar here,
191 * basically re-parenting the short-handed DOM element under the 'case' DOM element.
193 caseDomElement.reparent(shorthandedStatement.getDomElement());
196 * The properties need to be copied over.
198 newCase.copyPropertiesFrom(shorthandedStatement);
201 * Any 'if-feature' statements that sit under the shorthand statement also need to
202 * be cloned to make the newly-created case just as much dependent on those.
204 final List<YIfFeature> ifFeaturesOfShorthand = shorthandedStatement.getChildren(CY.STMT_IF_FEATURE);
205 for (final YIfFeature ifFeature : ifFeaturesOfShorthand) {
206 final YIfFeature clonedIfFeature = new YIfFeature(newCase, ifFeature.getDomElement());
207 clonedIfFeature.cloneFrom(ifFeature);
210 Helper.addGeneralInfoAppData(newCase, "originally omitted 'case' statement inserted for readability.");
214 // =================================== INPUT / OUTPUT HANDLING =====================================
216 public static void fixupMissingInputOutputStatements(final Schema schema) {
217 fixupMissingInputOutputStatements(schema, CY.STMT_ACTION);
218 fixupMissingInputOutputStatements(schema, CY.STMT_RPC);
222 * Some RPCs or actions do not use any input or output. However, it is still possible for data nodes to
223 * be augmented-into RPCs and actions. For this to work, the targetNode path will list the input/output
224 * statement. We simplify our processing later on if we inject any missing input/output statements now.
226 @SuppressWarnings("unchecked")
227 private static <T extends AbstractStatement> void fixupMissingInputOutputStatements(final Schema schema,
228 final StatementModuleAndName actionOrRpcClazz) {
230 final List<T> statements = (List<T>) Helper.findStatementsInSchema(actionOrRpcClazz, schema);
231 for (final T actionOrRpc : statements) {
233 if (actionOrRpc.getChild(CY.STMT_INPUT) == null) {
235 * Create new DOM element for the 'input' statement. It will sit below the 'action' (or 'rpc')
236 * statement and has no children. As line number, we will give it the line number of the parent.
238 final YangDomElement inputDomElement = new YangDomElement(CY.INPUT, null, actionOrRpc.getDomElement(),
239 actionOrRpc.getDomElement().getLineNumber());
241 * And create the statement.
243 final YInput yInput = new YInput(actionOrRpc, inputDomElement);
245 * Copy over properties as well.
247 yInput.copyPropertiesFrom(actionOrRpc);
251 * Exact same now for the output.
253 if (actionOrRpc.getChild(CY.STMT_OUTPUT) == null) {
254 final YangDomElement outputDomElement = new YangDomElement(CY.OUTPUT, null, actionOrRpc.getDomElement(),
255 actionOrRpc.getDomElement().getLineNumber());
256 final YOutput yOutput = new YOutput(actionOrRpc, outputDomElement);
257 yOutput.copyPropertiesFrom(actionOrRpc);
262 // =================================== DATA NODE HANDLING FOR IMPORT-ONLY MODULES =====================================
264 public static void removeProtocolAccessibleObjects(final Schema schema) {
266 * According to RFC, any statement that "implements any protocol-accessible objects" cannot remain in the
267 * module, so we delete those. Such statements may have been augmented into a lower part of the tree, so we
268 * need to navigate down the tree.
270 for (final YangModel yangModelFile : schema.getModuleRegistry().getAllYangModels()) {
271 removeProtocolAccessibleObjects(yangModelFile.getYangModelRoot().getModuleOrSubmodule());
275 private static final Set<StatementModuleAndName> PROTOCOL_ACCESSIBLE_STATEMENTS = new HashSet<>(Arrays.asList(
276 CY.STMT_ANYDATA, CY.STMT_ANYXML, CY.STMT_AUGMENT, CY.STMT_CHOICE, CY.STMT_CONTAINER, CY.STMT_DEVIATION,
277 CY.STMT_LEAF, CY.STMT_LEAF_LIST, CY.STMT_LIST, CY.STMT_NOTIFICATION, CY.STMT_RPC, CY.STMT_USES));
279 private static void removeProtocolAccessibleObjects(final AbstractStatement statement) {
281 final List<AbstractStatement> children = statement.getNonExtensionChildStatements();
282 for (final AbstractStatement child : children) {
283 if (child.getEffectiveConformanceType() == ConformanceType.IMPORT && PROTOCOL_ACCESSIBLE_STATEMENTS.contains(
284 child.getStatementModuleAndName())) {
285 statement.removeChild(child);
286 } else if (child.definesSchemaNode()) {
287 removeProtocolAccessibleObjects(child);
292 // =================================== IF-FEATURE HANDLING =====================================
294 public static void removeDataNodesNotSatisfyingIfFeature(final ParserExecutionContext context, final Schema schema) {
297 * There is a special case that requires some up-front logic: it is possible for
298 * a feature itself to be constrained by if-feature. This does not mean that the
299 * feature shall be removed. From the RFC:
301 * "In order for a server to support a feature that is dependent on any other
302 * features (i.e., the feature has one or more "if-feature" substatements), the
303 * server MUST also support all the dependent features."
307 * feature feature-abc;
308 * feature feature-def;
309 * feature feature-xyz {
310 * if-feature "feature-abc and feature-def";
313 * What this means is that xyz can only be supported if both abc and def are
314 * supported. The client that has supplied the YANG library, however, could have
315 * gotten this wrong. So we need to check for that.
317 checkFeaturesConstrainedByIfFeatures(context, schema);
320 * Now that this is done, remove statements as required...
322 for (final YangModel yangModelFile : schema.getModuleRegistry().getAllYangModels()) {
323 removeFromModule(context, schema, yangModelFile);
327 private static void removeFromModule(final ParserExecutionContext context, final Schema schema,
328 final YangModel yangModelFile) {
330 final AbstractStatement moduleOrSubmodule = yangModelFile.getYangModelRoot().getModuleOrSubmodule();
333 * We go through the statement tree and check each statement whether it's
334 * if-feature is fulfilled or not.
336 handleStatementPossiblyConstrainedByIfFeature(context, schema, moduleOrSubmodule);
339 private static void handleStatementPossiblyConstrainedByIfFeature(final ParserExecutionContext context,
340 final Schema schema, final AbstractStatement parent) {
342 final List<AbstractStatement> children = new ArrayList<>(parent.getChildStatements()); // deep copy required
343 for (final AbstractStatement child : children) {
344 if (!child.is(CY.STMT_FEATURE) && !ifFeaturesUnderStatementAreSatisfied(context, schema, child)) {
346 * Remove the statement and keep a note of it!
348 addChildRemovedDueToIfFeatureAppData(parent, child.getDomElement().getNameValue());
349 if (child.getStatementIdentifier() != null && !child.getStatementIdentifier().isEmpty()) {
350 addRemovedChildIdentifierDueToIfFeature(parent, child.getStatementIdentifier());
353 parent.removeChild(child);
356 * Go into the sub-tree.
358 handleStatementPossiblyConstrainedByIfFeature(context, schema, child);
363 private static void checkFeaturesConstrainedByIfFeatures(final ParserExecutionContext context, final Schema schema) {
365 for (final YangModel yangModel : schema.getModuleRegistry().getAllYangModels()) {
367 final List<YFeature> featuresInYam = yangModel.getYangModelRoot().getModuleOrSubmodule().getChildren(
370 for (final YFeature feature : featuresInYam) {
371 if (ifFeaturesUnderStatementAreSatisfied(context, schema, feature)) {
376 * Some if-feature is not satisfied. This means that the feature itself MUST NOT
377 * be supported. Better check!
379 final String namespace = feature.getDomElement().getYangModel().getYangModelRoot().getNamespace();
380 final String moduleName = feature.getDomElement().getYangModel().getYangModelRoot().getOwningSchema()
381 .getModuleNamespaceResolver().getModuleForNamespace(namespace);
383 final YangFeature yangFeature = new YangFeature(namespace, moduleName, feature.getFeatureName());
384 if (context.getSupportedFeatures().contains(yangFeature)) {
385 context.addFinding(new Finding(feature, ParserFindingType.P086_FEATURE_CANNOT_BE_SUPPORTED,
386 "Feature '" + feature
387 .getFeatureName() + "' has been supplied as supported, but it's 'if-feature' statement evaluates to false."));
393 private static boolean ifFeaturesUnderStatementAreSatisfied(final ParserExecutionContext context, final Schema schema,
394 final AbstractStatement statement) {
396 if (!statement.hasAtLeastOneChildOf(CY.STMT_IF_FEATURE)) {
400 final List<YIfFeature> ifFeatures = statement.getChildren(CY.STMT_IF_FEATURE);
403 * There can be multiple if-feature statements under a statement. They all must
404 * be true for the overall result to be true.
406 for (final YIfFeature ifFeature : ifFeatures) {
407 if (!ifFeatureIsSatisfied(context, schema, ifFeature)) {
415 private static boolean ifFeatureIsSatisfied(final ParserExecutionContext context, final Schema schema,
416 final YIfFeature ifFeature) {
418 final List<Token> tokens = ifFeature.getTokens();
421 * If the tokens are not valid the logic further below where we simplify the
422 * expression until we get a result will throw errors, so catch this problem
425 if (!ifFeature.areTokensValid(context, tokens)) {
429 if (tokens.size() == 1) {
431 * Simple if-feature statement, just referring to a single feature, easy.
433 return isFeatureSupported(context, schema, ifFeature, tokens.get(0).name);
437 * More than one token in the if-feature. This means we need to apply boolean
438 * logic to the string We build a logical expression first, and then simplify it
441 final List<Operand> expression = buildExpressionFromTokens(context, schema, ifFeature, tokens);
443 final Operand result = simplify(expression);
444 return result == Operand.TRUE;
447 private static boolean isFeatureSupported(final ParserExecutionContext context, final Schema schema,
448 final YIfFeature ifFeature, final String possiblyPrefixedFeatureName) {
450 final String featureName = QNameHelper.extractName(possiblyPrefixedFeatureName);
451 String namespace = null;
453 if (QNameHelper.hasPrefix(possiblyPrefixedFeatureName)) {
454 final String prefix = QNameHelper.extractPrefix(possiblyPrefixedFeatureName);
455 final ModuleIdentity moduleIdentityForPrefix = ifFeature.getPrefixResolver().getModuleForPrefix(prefix);
456 if (moduleIdentityForPrefix == null) {
457 context.addFinding(new Finding(ifFeature, ParserFindingType.P033_UNRESOLVEABLE_PREFIX,
458 "Unresolvable prefix '" + prefix + "'."));
462 final YangModel yangModel = schema.getModuleRegistry().find(moduleIdentityForPrefix);
463 if (yangModel == null) {
464 context.addFinding(new Finding(ifFeature, ParserFindingType.P034_UNRESOLVABLE_IMPORT,
465 "Cannot find '" + moduleIdentityForPrefix + "' in input."));
469 namespace = yangModel.getYangModelRoot().getNamespace();
472 * No prefix, so refers to same module in which it was defined.
474 namespace = ifFeature.getDomElement().getYangModel().getYangModelRoot().getNamespace();
477 final String moduleName = ifFeature.getDomElement().getYangModel().getYangModelRoot().getOwningSchema()
478 .getModuleNamespaceResolver().getModuleForNamespace(namespace);
480 final YangFeature yangFeature = new YangFeature(namespace, moduleName, featureName);
481 return context.getSupportedFeatures().contains(yangFeature);
484 // -------------------------- All the expression handling stuff here -------------------------------
486 private enum Operand {
496 private static List<Operand> buildExpressionFromTokens(final ParserExecutionContext context, final Schema schema,
497 final YIfFeature ifFeature, final List<Token> tokens) {
499 final List<Operand> result = new ArrayList<>(tokens.size());
501 for (final Token token : tokens) {
503 switch (token.type) {
505 result.add(Operand.NOT);
508 result.add(Operand.AND);
511 result.add(Operand.OR);
513 case LEFT_PARENTHESIS:
514 result.add(Operand.LEFT_PARENTHESIS);
516 case RIGHT_PARENTHESIS:
517 result.add(Operand.RIGHT_PARENTHESIS);
520 if (isFeatureSupported(context, schema, ifFeature, token.name)) {
521 result.add(Operand.TRUE);
523 result.add(Operand.FALSE);
532 * A bit of magic here. We keep simplifying the expression until there are no
533 * more operators left.
535 private static Operand simplify(final List<Operand> input) {
537 final List<Operand> result = new ArrayList<>(input);
540 * The RFC gives as precedence: parenthesis, not, and, or. So that is the order in which we resolve things.
542 * To simplify the list, we simplify all sub-expressions (those in parenthesis) first.
544 while (result.contains(Operand.LEFT_PARENTHESIS)) {
546 * Find first occurrence of LEFT, and last occurrence or RIGHT. The contents of
547 * that will be simplified and replaces the sub-expression.
549 final int indexOfLeft = result.indexOf(Operand.LEFT_PARENTHESIS);
550 int parenthesisCount = 1;
551 int indexOfRight = indexOfLeft;
553 while (parenthesisCount > 0) {
555 if (result.get(indexOfRight) == Operand.LEFT_PARENTHESIS) {
557 } else if (result.get(indexOfRight) == Operand.RIGHT_PARENTHESIS) {
563 * Get the content that is between the parenthesis. That content will be
564 * simplified in a moment and replaces the sub-expression.
566 final List<Operand> sublist = new ArrayList<>(result.subList(indexOfLeft + 1, indexOfRight));
567 final Operand replaceSubExpressionWith = simplify(sublist);
570 * Remove the sub-expression and replace with the result of the simplification.
572 final int nrOfElementsToRemove = indexOfRight - indexOfLeft + 1;
573 for (int i = 0; i < nrOfElementsToRemove; ++i) {
574 result.remove(indexOfLeft);
576 result.add(indexOfLeft, replaceSubExpressionWith);
580 * Next precedence is "NOT"
582 while (result.contains(Operand.NOT)) {
584 final int indexOfNot = result.indexOf(Operand.NOT);
587 * We simply remove the 'NOT', and flip the next element.
589 result.remove(indexOfNot);
590 result.set(indexOfNot, result.get(indexOfNot) == Operand.TRUE ? Operand.FALSE : Operand.TRUE);
594 * Next precedence is "AND"
596 while (result.contains(Operand.AND)) {
598 final int indexOfAnd = result.indexOf(Operand.AND);
601 * We logically AND together the elements before and after. Then we remove all
602 * three elements and replace with the ANDed result.
604 final boolean resultOfAnd = (result.get(indexOfAnd - 1) == Operand.TRUE) && (result.get(
605 indexOfAnd + 1) == Operand.TRUE);
607 result.remove(indexOfAnd - 1);
608 result.remove(indexOfAnd - 1);
609 result.remove(indexOfAnd - 1);
610 result.add(indexOfAnd - 1, resultOfAnd ? Operand.TRUE : Operand.FALSE);
616 while (result.contains(Operand.OR)) {
618 final int indexOfOr = result.indexOf(Operand.OR);
621 * We logically OR together the elements before and after. Then we remove all
622 * three elements and replace with the ORed result.
624 final boolean resultOfOr = (result.get(indexOfOr - 1) == Operand.TRUE) || (result.get(
625 indexOfOr + 1) == Operand.TRUE);
627 result.remove(indexOfOr - 1);
628 result.remove(indexOfOr - 1);
629 result.remove(indexOfOr - 1);
630 result.add(indexOfOr - 1, resultOfOr ? Operand.TRUE : Operand.FALSE);
634 * At this point, there can only be a single entry left in the list.
637 return result.get(0);
640 private static final String IF_FEATURE_INFO = "IF_FEATURE_INFO";
641 private static final String IF_FEATURE_REMOVED_CHILD_IDENTIFIERS = "REMOVED_CHILD_IDENTIFIERS";
643 private static void addChildRemovedDueToIfFeatureAppData(final AbstractStatement parentStatement,
644 final String nameOfRemovedChild) {
645 Helper.addAppDataListInfo(parentStatement, IF_FEATURE_INFO,
646 "Child statement " + nameOfRemovedChild + " removed as it's if-feature condition evaluated to false.");
649 public static List<String> getChildRemovedDueToIfFeatureAppDataForStatement(final AbstractStatement statement) {
650 return Helper.getAppDataListInfo(statement, IF_FEATURE_INFO);
653 private static void addRemovedChildIdentifierDueToIfFeature(final AbstractStatement statement,
654 final String identifier) {
655 Helper.addAppDataListInfo(statement, IF_FEATURE_REMOVED_CHILD_IDENTIFIERS, identifier);
658 public static List<String> getRemovedChildIdentifiersDueToIfFeatureForStatement(final AbstractStatement statement) {
659 return Helper.getAppDataListInfo(statement, IF_FEATURE_REMOVED_CHILD_IDENTIFIERS);
662 // =================================== FINDING HANDLING ON UNUSED SCHEMA NODES =====================================
665 * Certain parts of the YAMs may not make it into the final schema:
670 * Also, the schema may be tweaked such that original schema nodes are not longer referenced when we do the following:
674 * - if-feature evaluating to false
676 * So what we do is we go through the final schema, and jump back to the DOM nodes and record which of these are still
677 * referenced; whatever is not referenced will then be looked-at in terms of findings and the findings removed. This
678 * cuts down on "noise"
680 public static void removeFindingsOnUnusedSchemaNodes(final ParserExecutionContext context, final Schema schema) {
682 final Set<YangDomElement> usedDomNodes = new HashSet<>(500000, 0.75f);
685 * Collect the DOM nodes first.
687 for (final YangModel yangModelFile : schema.getModuleRegistry().getAllYangModels()) {
688 collectUsedDomNodes(yangModelFile, usedDomNodes);
692 * Now go through the DOM and have a look.
694 for (final YangModel yangModelFile : schema.getModuleRegistry().getAllYangModels()) {
695 removeFindingsFromUnusedDomNodes(context, yangModelFile, usedDomNodes);
699 private static void collectUsedDomNodes(final YangModel yangModelFile, final Set<YangDomElement> usedDomNodes) {
702 * Start off with the YAM root, and then work recursively down.
704 final AbstractStatement moduleOrSubmodule = yangModelFile.getYangModelRoot().getModuleOrSubmodule();
705 usedDomNodes.add(moduleOrSubmodule.getDomElement());
707 collectUsedDomNodes(usedDomNodes, moduleOrSubmodule);
710 private final static Set<StatementModuleAndName> CHILDREN_OF_STATEMENTS_TO_IGNORE = new HashSet<>(Arrays.asList(
711 CY.STMT_GROUPING, CY.STMT_TYPEDEF));
713 private static void collectUsedDomNodes(final Set<YangDomElement> usedDomNodes,
714 final AbstractStatement parentStatement) {
716 for (final AbstractStatement child : parentStatement.getChildStatements()) {
718 usedDomNodes.add(child.getDomElement());
721 * Certain statements we happily ignore when we encounter them in the schema. They
722 * would have been resolved / merged-in by this stage and have no further role to
723 * play in the schema..
725 if (CHILDREN_OF_STATEMENTS_TO_IGNORE.contains(child.getStatementModuleAndName())) {
729 collectUsedDomNodes(usedDomNodes, child);
733 private static void removeFindingsFromUnusedDomNodes(final ParserExecutionContext context,
734 final YangModel yangModelFile, final Set<YangDomElement> usedDomNodes) {
735 final YangDomElement moduleOrSubmodule = yangModelFile.getYangModelRoot().getModuleOrSubmodule().getDomElement();
736 removeFindingsFromUnusedDomNodes(context, usedDomNodes, moduleOrSubmodule);
739 private static void removeFindingsFromUnusedDomNodes(final ParserExecutionContext context,
740 final Set<YangDomElement> usedDomNodes, final YangDomElement parentDomElement) {
742 for (final YangDomElement child : parentDomElement.getChildren()) {
744 if (!usedDomNodes.contains(child)) {
746 * We have encountered a DOM node that is not referenced by the statement tree. Remove any findings on it.
748 context.getFindingsManager().removeFindingsOnYangDomElement(child);
751 removeFindingsFromUnusedDomNodes(context, usedDomNodes, child);
755 // =================================== STATUS HANDLING =====================================
758 * Each statement will be assigned the effective status.
760 public static void assignStatus(final Schema schema) {
761 for (final YangModel yangModelFile : schema.getModuleRegistry().getAllYangModels()) {
762 final String implicitStatus = YStatus.CURRENT;
763 assignStatus(yangModelFile.getYangModelRoot().getModuleOrSubmodule(), implicitStatus);
767 private static void assignStatus(final AbstractStatement statement, final String statusOfParent) {
769 String statusToAssign = statusOfParent;
772 * If there is a status statement that makes the status "more severe"
773 * we use that instead. If the status is less restrictive we ignore that.
775 final YStatus statusChild = statement.getChild(CY.STMT_STATUS);
776 if (statusChild != null) {
777 if (statusChild.isObsolete()) {
778 statusToAssign = YStatus.OBSOLETE;
779 } else if (statusChild.isDeprecated() && statusOfParent.equals(YStatus.CURRENT)) {
780 statusToAssign = YStatus.DEPRECATED;
784 statement.setEffectiveStatus(statusToAssign);
786 for (final AbstractStatement child : statement.getChildStatements()) {
787 assignStatus(child, statusToAssign);
791 // =================================== NAMESPACE HANDLING =====================================
794 * Each statement within a YAM will get the namespace of the module.
796 public static void assignEffectiveNamespaces(final Schema schema) {
797 for (final YangModel yangModel : schema.getModuleRegistry().getAllYangModels()) {
798 final String namespace = yangModel.getYangModelRoot().getNamespace();
799 yangModel.getYangModelRoot().assignEffectiveNamespaceToStatementTree(namespace);
803 // =================================== CONFORMANCE TYPE HANDLING =====================================
806 * The ConformanceType is applied to all statements within a YAM.
808 public static void assignEffectiveConformanceType(final Schema schema) {
809 for (final YangModel yangModelFile : schema.getModuleRegistry().getAllYangModels()) {
810 final ConformanceType conformanceType = yangModelFile.getConformanceType();
811 yangModelFile.getYangModelRoot().assignEffectiveConformanceTypeToStatementTree(conformanceType);
816 * The effective config is applied to all statements within a YAM.
818 public static void assignConfig(final Schema schema) {
819 for (final YangModel yangModelFile : schema.getModuleRegistry().getAllYangModels()) {
820 yangModelFile.getYangModelRoot().assignEffectiveConfigToStatementTree(true);