diff --git a/yangkit-data-impl/src/main/java/org/yangcentral/yangkit/data/impl/model/YangAbstractDataContainer.java b/yangkit-data-impl/src/main/java/org/yangcentral/yangkit/data/impl/model/YangAbstractDataContainer.java index 5f384cc7..8186cdd2 100644 --- a/yangkit-data-impl/src/main/java/org/yangcentral/yangkit/data/impl/model/YangAbstractDataContainer.java +++ b/yangkit-data-impl/src/main/java/org/yangcentral/yangkit/data/impl/model/YangAbstractDataContainer.java @@ -25,16 +25,16 @@ public class YangAbstractDataContainer implements YangDataContainer { private YangDataContainer self; private SchemaNodeContainer schemaNodeContainer; - private Map> children = new ConcurrentHashMap<>(); + private Map> children = new ConcurrentHashMap<>(); private List> childrenList = new ArrayList<>(); private boolean isRoot = false; public YangAbstractDataContainer(YangDataContainer yangDataContainer) { this.self = yangDataContainer; - if(self instanceof YangDataDocument){ + if (self instanceof YangDataDocument) { schemaNodeContainer = ((YangDataDocument) self).getSchemaContext(); - } else if ( self instanceof YangData){ + } else if (self instanceof YangData) { schemaNodeContainer = (SchemaNodeContainer) ((YangData) self).getSchemaNode(); } isRoot = (self instanceof YangDataDocument); @@ -61,10 +61,10 @@ public YangData getChild(DataIdentifier identifier) { @Override public List> getChildren(QName qName) { List> childrenList = new ArrayList<>(); - Iterator>> entries = children.entrySet().iterator(); - while(entries.hasNext()){ - Map.Entry> entry = entries.next(); - if(entry.getKey().getQName().equals(qName)){ + Iterator>> entries = children.entrySet().iterator(); + while (entries.hasNext()) { + Map.Entry> entry = entries.next(); + if (entry.getKey().getQName().equals(qName)) { childrenList.add(entry.getValue()); } } @@ -74,9 +74,9 @@ public List> getChildren(QName qName) { @Override public List> getDataChildren() { List> list = new ArrayList<>(); - for(YangData value: childrenList){ - if(value.isVirtual()){ - if(value instanceof YangDataContainer){ + for (YangData value : childrenList) { + if (value.isVirtual()) { + if (value instanceof YangDataContainer) { list.addAll(((YangDataContainer) value).getDataChildren()); } } else { @@ -89,18 +89,18 @@ public List> getDataChildren() { @Override public YangData getDataChild(DataIdentifier identifier) { YangData value = children.get(identifier); - if(value != null && !value.isVirtual()){ + if (value != null && !value.isVirtual()) { return value; } - Iterator>> entries = children.entrySet().iterator(); - while(entries.hasNext()){ - Map.Entry> entry = entries.next(); + Iterator>> entries = children.entrySet().iterator(); + while (entries.hasNext()) { + Map.Entry> entry = entries.next(); value = entry.getValue(); - if(value.isVirtual()){ - if(value instanceof YangDataContainer){ + if (value.isVirtual()) { + if (value instanceof YangDataContainer) { YangData dataChild = ((YangDataContainer) value).getDataChild(identifier); - if(dataChild != null){ + if (dataChild != null) { return dataChild; } } @@ -113,12 +113,12 @@ public YangData getDataChild(DataIdentifier identifier) { @Override public List> getDataChildren(QName qName) { List> list = new ArrayList<>(); - for(YangData child:childrenList){ - if(child.isVirtual()){ - if(child instanceof YangDataContainer){ + for (YangData child : childrenList) { + if (child.isVirtual()) { + if (child instanceof YangDataContainer) { list.addAll(((YangDataContainer) child).getDataChildren(qName)); } - } else if (child.getIdentifier().getQName().equals(qName)){ + } else if (child.getIdentifier().getQName().equals(qName)) { list.add(child); } } @@ -128,12 +128,12 @@ public List> getDataChildren(QName qName) { @Override public List> getDataChildren(String name) { List> list = new ArrayList<>(); - for(YangData value:childrenList){ - if(value.isVirtual()){ - if(value instanceof YangDataContainer){ + for (YangData value : childrenList) { + if (value.isVirtual()) { + if (value instanceof YangDataContainer) { list.addAll(((YangDataContainer) value).getDataChildren(name)); } - } else if (value.getIdentifier().getQName().getLocalName().equals(name)){ + } else if (value.getIdentifier().getQName().getLocalName().equals(name)) { list.add(value); } } @@ -143,7 +143,7 @@ public List> getDataChildren(String name) { @Override public List> getDataChildren(String name, String namespace) { - return getDataChildren(new QName(namespace,name)); + return getDataChildren(new QName(namespace, name)); } @Override @@ -156,29 +156,29 @@ public YangData removeChild(DataIdentifier identifier) { @Override public void addDataChild(YangData child, boolean autoDelete) throws YangDataException { YangData original = getDataChild(child.getIdentifier()); - if(original != null){ - if(!original.isDummyNode()){ - throw new YangDataException(ErrorTag.DATA_EXISTS,original.getPath(), - new ErrorMessage("the child:"+child.getIdentifier() + " is exists.")); + if (original != null) { + if (!original.isDummyNode()) { + throw new YangDataException(ErrorTag.DATA_EXISTS, original.getPath(), + new ErrorMessage("the child:" + child.getIdentifier() + " is exists.")); } } SchemaNode childSchema = schemaNodeContainer.getTreeNodeChild(child.getSchemaNode().getIdentifier()); - if(null == childSchema){ + if (null == childSchema) { AbsolutePath errorPath = new AbsolutePath(); - if(self instanceof YangData){ + if (self instanceof YangData) { errorPath = ((YangData) self).getPath(); } throw new YangDataException(ErrorTag.BAD_ELEMENT, errorPath, - new ErrorMessage("unknown data child:"+ child.getSchemaNode().toString())); + new ErrorMessage("unknown data child:" + child.getSchemaNode().toString())); } Stack descendants = new Stack(); SchemaNode childParentSchemaNode = childSchema; - while(childParentSchemaNode != schemaNodeContainer){ + while (childParentSchemaNode != schemaNodeContainer) { descendants.push(childParentSchemaNode); SchemaNodeContainer parentContainer = childParentSchemaNode.getParentSchemaNode(); - if(!(parentContainer instanceof SchemaNode)){ + if (!(parentContainer instanceof SchemaNode)) { break; } childParentSchemaNode = (SchemaNode) parentContainer; @@ -186,18 +186,18 @@ public void addDataChild(YangData child, boolean autoDelete) throws YangDataExce YangDataContainer yangDataContainer = this.self; - while( !descendants.isEmpty()) { + while (!descendants.isEmpty()) { SchemaNode descendant = descendants.pop(); YangData descendantData = null; - if(descendant.equals(child.getSchemaNode())){ - yangDataContainer.addChild(child,autoDelete); + if (descendant.equals(child.getSchemaNode())) { + yangDataContainer.addChild(child, autoDelete); break; } else { descendantData = yangDataContainer.getChild( new SingleInstanceDataIdentifier(descendant.getIdentifier())); - if(null == descendantData){ - descendantData = new YangDataBuilder().getYangData(descendant,null); - yangDataContainer.addChild(descendantData,autoDelete); + if (null == descendantData) { + descendantData = new YangDataBuilder().getYangData(descendant, null); + yangDataContainer.addChild(descendantData, autoDelete); } } yangDataContainer = (YangDataContainer) descendantData; @@ -208,36 +208,37 @@ public void addDataChild(YangData child, boolean autoDelete) throws YangDataExce @Override public void addChild(YangData child, boolean autoDelete) throws YangDataException { - if( schemaNodeContainer == null){ + if (schemaNodeContainer == null) { return; } SchemaNode childSchemaNode = schemaNodeContainer.getSchemaNodeChild(child.getSchemaNode().getIdentifier()); - if (childSchemaNode == null){ + if (childSchemaNode == null) { AbsolutePath path = new AbsolutePath(); - if (self instanceof YangData){ + if (self instanceof YangData) { path = ((YangData) self).getPath(); } - throw new YangDataException(ErrorTag.BAD_ELEMENT,path, + throw new YangDataException(ErrorTag.BAD_ELEMENT, path, new ErrorMessage("Incompatible child occurs. The child's schema node:" + child.getSchemaNode().toString() + " is not the data child of this schema node:" - + ((schemaNodeContainer instanceof YangSchemaContext)?"root":schemaNodeContainer.toString()))); + + ((schemaNodeContainer instanceof YangSchemaContext) ? "root" + : schemaNodeContainer.toString()))); } YangData oldChild = getChild(child.getIdentifier()); - if(oldChild != null) { -// if(oldChild.isDummyNode()){ -// self.removeChild(child.getIdentifier()); -// children.put(child.getIdentifier(),child); -// return; -// } - throw new YangDataException(ErrorTag.DATA_EXISTS,oldChild.getPath(), - new ErrorMessage("the child:"+child.getIdentifier() + " is exists.")); + if (oldChild != null) { + // if(oldChild.isDummyNode()){ + // self.removeChild(child.getIdentifier()); + // children.put(child.getIdentifier(),child); + // return; + // } + throw new YangDataException(ErrorTag.DATA_EXISTS, oldChild.getPath(), + new ErrorMessage("the child:" + child.getIdentifier() + " is exists.")); } childrenList.add(child); - children.put(child.getIdentifier(),child); + children.put(child.getIdentifier(), child); child.getContext().setParent(self); - if(self instanceof YangDataDocument){ + if (self instanceof YangDataDocument) { child.getContext().setDocument((YangDataDocument) self); } else { YangData yangData = (YangData) self; @@ -245,12 +246,11 @@ public void addChild(YangData child, boolean autoDelete) throws YangDataExceptio } } - @Override public YangData removeDataChild(DataIdentifier identifier) { YangData dataChild = getDataChild(identifier); - if(dataChild == null){ + if (dataChild == null) { return null; } YangDataContainer parent = dataChild.getContext().getParent(); @@ -258,62 +258,61 @@ public YangData removeDataChild(DataIdentifier identifier) { return dataChild; } - private boolean matchUnique(Unique unique,List> uniqueData,ListData listData){ + private boolean matchUnique(Unique unique, List> uniqueData, ListData listData) { List> matchedUniqueData = new ArrayList<>(); - for(Leaf leaf:unique.getUniqueNodes()){ + for (Leaf leaf : unique.getUniqueNodes()) { List steps = listData.getSchemaNode().getSchemaPath().getRelativeSchemaPath(leaf.getSchemaPath()); - SchemaPath.Descendant descendant = new DescendantSchemaPath(steps,listData.getSchemaNode()); - List> matched = YangDataUtil.search(listData,descendant); - if(matched.isEmpty()){ + SchemaPath.Descendant descendant = new DescendantSchemaPath(steps, listData.getSchemaNode()); + List> matched = YangDataUtil.search(listData, descendant); + if (matched.isEmpty()) { return false; } - if(matched.size() > 1){ + if (matched.size() > 1) { return false; } matchedUniqueData.add(matched.get(0)); } - if(uniqueData.isEmpty()){ + if (uniqueData.isEmpty()) { uniqueData.addAll(matchedUniqueData); } else { - if(!YangDataUtil.equals(uniqueData,matchedUniqueData)){ + if (!YangDataUtil.equals(uniqueData, matchedUniqueData)) { return false; } } return true; } - private ValidatorResult checkUniques(YangList list, List> matchedData){ + private ValidatorResult checkUniques(YangList list, List> matchedData) { ValidatorResultBuilder validatorResultBuilder = new ValidatorResultBuilder(); - if(list.getUniques().isEmpty() || matchedData.isEmpty()){ + if (list.getUniques().isEmpty() || matchedData.isEmpty()) { return validatorResultBuilder.build(); } List uniques = list.getUniques(); - for(Unique unique:uniques){ + for (Unique unique : uniques) { List> uniqueData = new ArrayList<>(); int matchCount = 0; YangData previous = null; - for( YangData dataItem:matchedData){ - if(dataItem instanceof ListData){ - if(matchUnique(unique,uniqueData, (ListData) dataItem)){ + for (YangData dataItem : matchedData) { + if (dataItem instanceof ListData) { + if (matchUnique(unique, uniqueData, (ListData) dataItem)) { matchCount++; } - if(matchCount == 1){ + if (matchCount == 1) { previous = dataItem; } - if(matchCount >1){ - ValidatorRecordBuilder validatorRecordBuilder = - new ValidatorRecordBuilder<>(); + if (matchCount > 1) { + ValidatorRecordBuilder validatorRecordBuilder = new ValidatorRecordBuilder<>(); validatorRecordBuilder.setErrorTag(ErrorTag.OPERATION_FAILED); validatorRecordBuilder.setErrorAppTag(ErrorAppTag.DATA_NOT_UNIQUE.getName()); validatorRecordBuilder.setErrorPath(dataItem.getPath()); - validatorRecordBuilder.setErrorMessage(new ErrorMessage("data is not unique."+ - " previous:"+ previous.getPath())); + validatorRecordBuilder.setErrorMessage(new ErrorMessage("data is not unique." + + " previous:" + previous.getPath())); validatorResultBuilder.addRecord(validatorRecordBuilder.build()); break; } } } - if(matchCount > 1){ + if (matchCount > 1) { break; } } @@ -322,20 +321,19 @@ private ValidatorResult checkUniques(YangList list, List> matchedDat private ValidatorResult checkMandatory(SchemaNode schemaNode, List> matchedData) { ValidatorResultBuilder validatorResultBuilder = new ValidatorResultBuilder(); - if(schemaNode.isMandatory()){ - if(matchedData.isEmpty()){ - //if have when condition, valuate this when condition,if true, report error - YangData dummyNode = new YangDataBuilder().getYangData(schemaNode,null); + if (schemaNode.isMandatory()) { + if (matchedData.isEmpty()) { + // if have when condition, valuate this when condition,if true, report error + YangData dummyNode = new YangDataBuilder().getYangData(schemaNode, null); dummyNode.setDummyNode(true); try { self.addChild(dummyNode); boolean result = dummyNode.checkWhen(); - if(result){ - ValidatorRecordBuilder> validatorRecordBuilder = - new ValidatorRecordBuilder<>(); + if (result) { + ValidatorRecordBuilder> validatorRecordBuilder = new ValidatorRecordBuilder<>(); validatorRecordBuilder.setErrorTag(ErrorTag.DATA_MISSING); - validatorRecordBuilder.setErrorPath((self instanceof YangDataDocument)?new AbsolutePath(): - ((YangData)self).getPath()); + validatorRecordBuilder.setErrorPath( + (self instanceof YangDataDocument) ? new AbsolutePath() : ((YangData) self).getPath()); validatorRecordBuilder.setErrorMessage(new ErrorMessage("missing mandatory schema node:" + schemaNode.getIdentifier().getQualifiedName())); validatorResultBuilder.addRecord(validatorRecordBuilder.build()); @@ -345,37 +343,36 @@ private ValidatorResult checkMandatory(SchemaNode schemaNode, List> self.removeChild(dummyNode.getIdentifier()); e.printStackTrace(); } - } - else { - //check whether match the min-elements and max-elements for multi-instance schema node - if(schemaNode instanceof MultiInstancesDataNode){ + } else { + // check whether match the min-elements and max-elements for multi-instance + // schema node + if (schemaNode instanceof MultiInstancesDataNode) { MultiInstancesDataNode multiInstancesDataNode = (MultiInstancesDataNode) schemaNode; - int minElements =0; + int minElements = 0; int maxElements = Integer.MAX_VALUE; - if(multiInstancesDataNode.getMinElements() != null){ + if (multiInstancesDataNode.getMinElements() != null) { minElements = multiInstancesDataNode.getMinElements().getValue(); } - if(multiInstancesDataNode.getMaxElements() != null - && !multiInstancesDataNode.getMaxElements().isUnbounded()){ + if (multiInstancesDataNode.getMaxElements() != null + && !multiInstancesDataNode.getMaxElements().isUnbounded()) { maxElements = multiInstancesDataNode.getMaxElements().getValue(); } int size = matchedData.size(); - if(size < minElements || size > maxElements){ - ValidatorRecordBuilder> validatorRecordBuilder = - new ValidatorRecordBuilder<>(); + if (size < minElements || size > maxElements) { + ValidatorRecordBuilder> validatorRecordBuilder = new ValidatorRecordBuilder<>(); validatorRecordBuilder.setErrorTag(ErrorTag.OPERATION_FAILED); - validatorRecordBuilder.setErrorPath((self instanceof YangDataDocument)?new AbsolutePath(): - ((YangData)self).getPath()); - if(size < minElements){ + validatorRecordBuilder.setErrorPath( + (self instanceof YangDataDocument) ? new AbsolutePath() : ((YangData) self).getPath()); + if (size < minElements) { validatorRecordBuilder.setErrorAppTag(ErrorAppTag.TOO_FEW_ELEMENTS.getName()); validatorRecordBuilder.setErrorMessage(new ErrorMessage("too few elements for node:" - + schemaNode.getIdentifier().getQualifiedName() + " min-elements:"+ minElements)); + + schemaNode.getIdentifier().getQualifiedName() + " min-elements:" + minElements)); } else { validatorRecordBuilder.setErrorAppTag(ErrorAppTag.TOO_MANY_ELEMENTS.getName()); validatorRecordBuilder.setErrorMessage(new ErrorMessage("too many elements for node:" - + schemaNode.getIdentifier().getQualifiedName() + " max-elements:"+ maxElements)); + + schemaNode.getIdentifier().getQualifiedName() + " max-elements:" + maxElements)); } validatorResultBuilder.addRecord(validatorRecordBuilder.build()); } @@ -389,26 +386,29 @@ private ValidatorResult checkMandatory(SchemaNode schemaNode, List> @Override public ValidatorResult validateChildren() { ValidatorResultBuilder validatorResultBuilder = new ValidatorResultBuilder(); - //build schema children match record map - Map>> matchRecord = new ConcurrentHashMap<>(); + // build schema children match record map + Map>> matchRecord = new ConcurrentHashMap<>(); Set presentModuleQNames = new HashSet<>(); - for(SchemaNode schemaNode: schemaNodeContainer.getSchemaNodeChildren()){ - if(YangDataUtil.onlyConfig(self)){ - if(!schemaNode.isConfig()){ + for (SchemaNode schemaNode : schemaNodeContainer.getSchemaNodeChildren()) { + if (YangDataUtil.onlyConfig(self)) { + if (!schemaNode.isConfig()) { continue; } } - matchRecord.put(schemaNode.getIdentifier(),new ArrayList>()); + // Skip inactive schema nodes to prevent validation of disabled features + if (!schemaNode.isActive()) { + continue; + } + matchRecord.put(schemaNode.getIdentifier(), new ArrayList>()); } - for(YangData child:self.getChildren()){ + for (YangData child : self.getChildren()) { SchemaNode schemaNode = child.getSchemaNode(); - if(!matchRecord.containsKey(schemaNode.getIdentifier()) || !schemaNode.isActive()){ - //inactive or unknown schema node, report error - ValidatorRecordBuilder> validatorRecordBuilder = - new ValidatorRecordBuilder<>(); + if (!matchRecord.containsKey(schemaNode.getIdentifier()) || !schemaNode.isActive()) { + // inactive or unknown schema node, report error + ValidatorRecordBuilder> validatorRecordBuilder = new ValidatorRecordBuilder<>(); validatorRecordBuilder.setErrorTag(ErrorTag.UNKNOWN_ELEMENT); - validatorRecordBuilder.setErrorPath((self instanceof YangDataDocument)?new AbsolutePath(): - ((YangData)self).getPath()); + validatorRecordBuilder.setErrorPath( + (self instanceof YangDataDocument) ? new AbsolutePath() : ((YangData) self).getPath()); validatorRecordBuilder.setErrorMessage(new ErrorMessage("unknown schema node:" + schemaNode.getArgStr())); validatorResultBuilder.addRecord(validatorRecordBuilder.build()); @@ -419,19 +419,19 @@ public ValidatorResult validateChildren() { presentModuleQNames.add(schemaNode.getIdentifier()); } - for(Map.Entry>> entry :matchRecord.entrySet()){ + for (Map.Entry>> entry : matchRecord.entrySet()) { SchemaNode schemaNode = schemaNodeContainer.getSchemaNodeChild(entry.getKey()); if (isRoot && !presentModuleQNames.contains(schemaNode.getIdentifier())) { continue; } - //check mandatory - validatorResultBuilder.merge(checkMandatory(schemaNode,entry.getValue())); - //check unique - if(schemaNode instanceof YangList){ - validatorResultBuilder.merge(checkUniques((YangList) schemaNode,entry.getValue())); + // check mandatory + validatorResultBuilder.merge(checkMandatory(schemaNode, entry.getValue())); + // check unique + if (schemaNode instanceof YangList) { + validatorResultBuilder.merge(checkUniques((YangList) schemaNode, entry.getValue())); } } - for(YangData child:self.getChildren()){ + for (YangData child : self.getChildren()) { validatorResultBuilder.merge(child.validate()); } return validatorResultBuilder.build(); @@ -442,30 +442,29 @@ public List compareChildren(YangDataContainer another) { List results = new ArrayList<>(); List> children = self.getDataChildren(); List> oChildren = another.getDataChildren(); - Map> map = new ConcurrentHashMap<>(); + Map> map = new ConcurrentHashMap<>(); AbsolutePath path = new AbsolutePath(); - if(!(self instanceof YangDataDocument)){ - path = ((YangData)self).getPath(); + if (!(self instanceof YangDataDocument)) { + path = ((YangData) self).getPath(); } - for(YangData child: children){ + for (YangData child : children) { YangData oChild = another.getDataChild(child.getIdentifier()); - if(oChild == null){ - //delete - results.add(new YangCompareResultImpl(path,DifferenceType.NONE,child)); + if (oChild == null) { + // delete + results.add(new YangCompareResultImpl(path, DifferenceType.NONE, child)); } else { - //change? + // change? results.addAll(child.compare(oChild)); - map.put(child.getIdentifier(),oChild); + map.put(child.getIdentifier(), oChild); } } - for(YangData oChild:another.getDataChildren()){ - if(map.get(oChild.getIdentifier())== null){ - //new - results.add(new YangCompareResultImpl(path,DifferenceType.NEW,oChild)); + for (YangData oChild : another.getDataChildren()) { + if (map.get(oChild.getIdentifier()) == null) { + // new + results.add(new YangCompareResultImpl(path, DifferenceType.NEW, oChild)); } } return results; } - } diff --git a/yangkit-examples/src/main/java/com/insa/app/FeatureExample.java b/yangkit-examples/src/main/java/com/insa/app/FeatureExample.java new file mode 100644 index 00000000..f4cddb1d --- /dev/null +++ b/yangkit-examples/src/main/java/com/insa/app/FeatureExample.java @@ -0,0 +1,185 @@ +/* + * Copyright 2023 INSA Lyon. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.insa.app; + +import org.dom4j.DocumentException; +import org.yangcentral.yangkit.common.api.exception.Severity; +import org.yangcentral.yangkit.common.api.validate.ValidatorRecord; +import org.yangcentral.yangkit.common.api.validate.ValidatorResult; +import org.yangcentral.yangkit.common.api.validate.ValidatorResultBuilder; +import org.yangcentral.yangkit.data.api.model.ContainerData; +import org.yangcentral.yangkit.data.api.model.YangDataDocument; +import org.yangcentral.yangkit.data.codec.json.ContainerDataJsonCodec; +import org.yangcentral.yangkit.data.codec.json.YangDataDocumentJsonParser; +import org.yangcentral.yangkit.model.api.schema.YangSchema; +import org.yangcentral.yangkit.model.api.schema.YangSchemaContext; +import org.yangcentral.yangkit.model.api.schema.ModuleSet; +import org.yangcentral.yangkit.model.api.schema.YangModuleDescription; +import org.yangcentral.yangkit.model.api.stmt.Container; +import org.yangcentral.yangkit.model.api.stmt.DataDefinition; +import org.yangcentral.yangkit.model.api.stmt.Feature; +import org.yangcentral.yangkit.model.api.stmt.Module; +import org.yangcentral.yangkit.model.api.stmt.SchemaNode; +import org.yangcentral.yangkit.parser.YangParserException; +import org.yangcentral.yangkit.parser.YangYinParser; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Optional; + +/** + * Simple example demonstrating YANG feature concepts with Yangkit + */ +public class FeatureExample { + + public static void main(String[] args) throws IOException, YangParserException, DocumentException { + + System.out.println("=== Simple YANG Feature Example ===\n"); + + // Parse our example YANG module with features + URL yangUrl = FeatureExample.class.getClassLoader().getResource("FeatureExample/yang"); + String yangDir = yangUrl.getFile(); + + YangSchemaContext schemaContext = YangYinParser.parse(yangDir); + ValidatorResult result = schemaContext.validate(); + + if (!result.isOk()) { + System.out.println("Schema validation failed:"); + for (ValidatorRecord record : result.getRecords()) { + System.out.println(" Error: " + record.getErrorMsg().getMessage()); + } + return; + } + + // Find our example module + Optional moduleOpt = schemaContext.getModule("example-features", null); + if (!moduleOpt.isPresent()) { + System.out.println("example-features module not found"); + return; + } + + Module exampleModule = moduleOpt.get(); + boolean valid = false; + + // Test 1: Enable no features + System.out.println("1. With no features enabled:"); + YangSchema noFeatureSchema = createSchemaWithFeature(exampleModule, null); + schemaContext.setYangSchema(noFeatureSchema); + showElementActivity(exampleModule); + valid = validateJson("basic-config.json", schemaContext); + System.out.println("Validation result: " + (valid ? "valid" : "invalid")); + + // Test 2: Enable advanced-config feature + System.out.println("\n2. With 'advanced-config' feature enabled:"); + YangSchema advancedSchema = createSchemaWithFeature(exampleModule, "advanced-config"); + schemaContext.setYangSchema(advancedSchema); + showElementActivity(exampleModule); + valid = validateJson("advanced-config.json", schemaContext); + System.out.println("Validation result: " + (valid ? "valid" : "invalid")); + + // Test 3: Enable monitoring feature + System.out.println("\n3. With 'monitoring' feature enabled:"); + YangSchema monitoringSchema = createSchemaWithFeature(exampleModule, "monitoring"); + schemaContext.setYangSchema(monitoringSchema); + showElementActivity(exampleModule); + valid = validateJson("monitoring-config.json", schemaContext); + System.out.println("Validation result: " + (valid ? "valid" : "invalid")); + + // Test 4: All features enabled + System.out.println("\n4. With all features enabled:"); + // Apparently, if no schema is specified, all features are enabled by default. + // This is the same in Libyang. + schemaContext.setYangSchema(null); + showElementActivity(exampleModule); + valid = validateJson("all-features.json", schemaContext); + System.out.println("Validation result: " + (valid ? "valid" : "invalid")); + + } + + private static void showElementActivity(Module module) { + for (DataDefinition dataDefinition : module.getDataDefChildren()) { + showElementStatus(dataDefinition, " "); + } + } + + private static void showElementStatus(DataDefinition element, String indent) { + if (element instanceof SchemaNode) { + SchemaNode schemaNode = (SchemaNode) element; + boolean isActive = schemaNode.isActive(); + String status = isActive ? "✅" : "🚫"; + System.out.println(indent + element.getArgStr() + " - " + status); + } + + // Recursively show child elements + if (element instanceof org.yangcentral.yangkit.model.api.stmt.DataDefContainer) { + var container = (org.yangcentral.yangkit.model.api.stmt.DataDefContainer) element; + for (DataDefinition child : container.getDataDefChildren()) { + showElementStatus(child, indent + " "); + } + } + } + + private static YangSchema createSchemaWithFeature(Module module, String featureName) { + return createSchemaWithFeatures(module, featureName); + } + + private static YangSchema createSchemaWithFeatures(Module module, String... featureNames) { + YangSchema schema = new YangSchema(); + schema.setName("test-schema"); + + ModuleSet moduleSet = new ModuleSet(); + moduleSet.setName("test-module-set"); + + YangModuleDescription moduleDesc = new YangModuleDescription(module.getModuleId()); + + for (String featureName : featureNames) { + moduleDesc.addFeature(featureName); + } + + moduleSet.addModule(moduleDesc); + schema.addModuleSet(moduleSet); + + return schema; + } + + private static boolean validateJson(String jsonFile, YangSchemaContext schemaContext) throws IOException { + // My validation + JsonNode jsonNode = new ObjectMapper().readTree(new File( + FeatureExample.class.getClassLoader().getResource("FeatureExample/json/" + jsonFile).getFile())); + ValidatorResultBuilder validatorResultBuilder = new ValidatorResultBuilder(); + YangDataDocument doc = new YangDataDocumentJsonParser(schemaContext).parse(jsonNode, validatorResultBuilder); + + ValidatorResult validatorResult = validatorResultBuilder.build(); + if (!validatorResult.isOk()) { + System.out.println("Built-in print: " + validatorResult.print(Severity.ERROR)); + } + doc.update(); + + ValidatorResult validatorResult1 = doc.validate(); + if (!validatorResult1.isOk()) { + System.out.println("Built-in print: " + validatorResult1.print(Severity.ERROR)); + } + + return validatorResult.isOk() && validatorResult1.isOk(); + } + +} diff --git a/yangkit-examples/src/main/resources/FeatureExample/json/advanced-config.json b/yangkit-examples/src/main/resources/FeatureExample/json/advanced-config.json new file mode 100644 index 00000000..b7100578 --- /dev/null +++ b/yangkit-examples/src/main/resources/FeatureExample/json/advanced-config.json @@ -0,0 +1,7 @@ +{ + "example-features:system": { + "advanced": { + "debug-level": "debug" + } + } +} \ No newline at end of file diff --git a/yangkit-examples/src/main/resources/FeatureExample/json/all-features.json b/yangkit-examples/src/main/resources/FeatureExample/json/all-features.json new file mode 100644 index 00000000..bb0c965e --- /dev/null +++ b/yangkit-examples/src/main/resources/FeatureExample/json/all-features.json @@ -0,0 +1,13 @@ +{ + "example-features:system": { + "advanced": { + "debug-level": "debug" + }, + "statistics": { + "uptime": 86400 + }, + "complex-settings": { + "setting-a": "value1" + } + } +} \ No newline at end of file diff --git a/yangkit-examples/src/main/resources/FeatureExample/json/basic-config.json b/yangkit-examples/src/main/resources/FeatureExample/json/basic-config.json new file mode 100644 index 00000000..6e48f8bd --- /dev/null +++ b/yangkit-examples/src/main/resources/FeatureExample/json/basic-config.json @@ -0,0 +1,3 @@ +{ + "example-feature:system": {} +} \ No newline at end of file diff --git a/yangkit-examples/src/main/resources/FeatureExample/json/monitoring-config.json b/yangkit-examples/src/main/resources/FeatureExample/json/monitoring-config.json new file mode 100644 index 00000000..75ad5d33 --- /dev/null +++ b/yangkit-examples/src/main/resources/FeatureExample/json/monitoring-config.json @@ -0,0 +1,7 @@ +{ + "example-features:system": { + "statistics": { + "uptime": 86400 + } + } +} \ No newline at end of file