Skip to content

Commit

Permalink
yang-tool: added deviation validation
Browse files Browse the repository at this point in the history
  • Loading branch information
dhuebner committed Dec 19, 2023
1 parent 7c15f14 commit 6a596ec
Show file tree
Hide file tree
Showing 7 changed files with 249 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import io.typefox.yang.resource.YangCrossReferenceSerializer
import io.typefox.yang.resource.YangResource
import io.typefox.yang.resource.YangSerializer
import io.typefox.yang.resource.YangTokenUtil
import io.typefox.yang.scoping.QualifiedNameConverter
import io.typefox.yang.scoping.ResourceDescriptionStrategy
import io.typefox.yang.scoping.YangResourceDescriptionManager
import io.typefox.yang.scoping.YangSerializerScopeProvider
Expand Down Expand Up @@ -43,6 +42,7 @@ import org.eclipse.xtext.workspace.IProjectConfigProvider
import org.eclipse.xtext.workspace.ProjectConfigProvider
import org.eclipse.xtext.serializer.tokens.IValueSerializer
import io.typefox.yang.resource.YangValueSerializer
import io.typefox.yang.scoping.YangQualifiedNameConverter

/**
* Use this class to register components to be used at runtime / without the Equinox extension registry.
Expand All @@ -69,7 +69,7 @@ class YangRuntimeModule extends AbstractYangRuntimeModule {
}

def Class<? extends IQualifiedNameConverter> bindIQualifiedNameConverter() {
QualifiedNameConverter
YangQualifiedNameConverter
}

override bindIValueConverterService() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
import java.util.List;
import java.util.Set;

import javax.annotation.Nullable;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;

import com.google.common.base.Objects;
Expand All @@ -30,17 +34,45 @@
public class ProcessedDataModel {

private List<ModuleData> modules;
private List<MessageEntry> messages = new ArrayList<>();

public void addModule(ModuleData moduleData) {
if (modules == null)
modules = newArrayList();
modules.add(moduleData);
}

@Nullable
public List<ModuleData> getModules() {
return modules;
}

@Nullable
public ModuleData getEntryModule() {
// TODO pick module by file name
if (modules != null && modules.size() > 0) {
return modules.get(0);
}
return null;
}

public void addError(String moduleFile, EObject source, String message) {
var msgEntry = new MessageEntry();
msgEntry.moduleFile = moduleFile == null ? "<unknown>" : moduleFile;
msgEntry.line = -1;
msgEntry.severity = Severity.Error;
msgEntry.message = message;
ICompositeNode node = NodeModelUtils.getNode(source);
if (node != null) {
msgEntry.line = node.getStartLine();
}
messages.add(msgEntry);
}

public List<MessageEntry> getMessages() {
return messages;
}

public static class HasStatements extends Named {

public HasStatements(ElementIdentifier id) {
Expand Down Expand Up @@ -409,4 +441,19 @@ public void addToChildren(HasStatements child) {
}
}

public static class MessageEntry {
String moduleFile;
int line;
Severity severity;
String message;

@Override
public String toString() {
return moduleFile + ":" + line + ": " + severity + ": " + message;
}
}

public static enum Severity {
Error, Warning
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.xtext.naming.DefaultDeclarativeQualifiedNameProvider;

import com.google.common.base.Objects;
import com.google.common.collect.Lists;
Expand All @@ -21,6 +22,7 @@
import io.typefox.yang.processor.ProcessedDataModel.HasStatements;
import io.typefox.yang.processor.ProcessedDataModel.ListData;
import io.typefox.yang.processor.ProcessedDataModel.ModuleData;
import io.typefox.yang.utils.YangNameUtils;
import io.typefox.yang.yang.AbstractModule;
import io.typefox.yang.yang.Action;
import io.typefox.yang.yang.Augment;
Expand All @@ -43,6 +45,7 @@
import io.typefox.yang.yang.SchemaNode;
import io.typefox.yang.yang.Statement;
import io.typefox.yang.yang.Uses;
import io.typefox.yang.yang.YangPackage;

public class YangProcessor {

Expand Down Expand Up @@ -73,8 +76,7 @@ public ProcessedDataModel process(List<AbstractModule> modules, List<String> inc
* @param output target
*/
public void serialize(ProcessedDataModel processedData, Format format, StringBuilder output) {
// TODO pick module by file name
ModuleData moduleData = processedData.getModules().get(0);
ModuleData moduleData = processedData.getEntryModule();
switch (format) {
case json: {
new JsonSerializer().serialize(moduleData, output);
Expand All @@ -90,10 +92,10 @@ public void serialize(ProcessedDataModel processedData, Format format, StringBui
protected ProcessedDataModel processInternal(List<AbstractModule> modules, List<String> includedFeatures,
List<String> excludedFeatures) {
var evalCtx = new FeatureEvaluationContext(includedFeatures, excludedFeatures);
ProcessedDataModel processedDataTree = new ProcessedDataModel();
ProcessedDataModel processedModel = new ProcessedDataModel();
modules.forEach((module) -> module.eAllContents().forEachRemaining((ele) -> {
if (ele instanceof Deviate) {
processDeviate((Deviate) ele);
processDeviate((Deviate) ele, module, processedModel);
} else if (ele instanceof Augment) {
processAugment((Augment) ele, module, evalCtx);
}
Expand All @@ -105,24 +107,40 @@ protected ProcessedDataModel processInternal(List<AbstractModule> modules, List<
if (!prefixStatements.isEmpty())
prefix = prefixStatements.get(0).getPrefix();
var moduleData = new ModuleData(new ElementIdentifier(module.getName(), prefix));
processedDataTree.addModule(moduleData);
processedModel.addModule(moduleData);
processChildren(module, moduleData, evalCtx);
});
return processedDataTree;
return processedModel;
}

/*
* The deviates's Substatements: config, default, mandatory, max-elements,
* min-elements, must, type,unique,units
* min-elements, must, type, unique, units. Properties 'must' and 'unique' are
* 0..n
*/
protected void processDeviate(Deviate deviate) {
protected void processDeviate(Deviate deviate, AbstractModule module, ProcessedDataModel processedModel) {
var deviation = (Deviation) deviate.eContainer();
SchemaNode targetNode = deviation.getReference().getSchemaNode();

switch (deviate.getArgument()) {
case "add": {
for (Statement statement : deviate.getSubstatements()) {
var copy = ProcessorUtility.copyEObject(statement);
targetNode.getSubstatements().add(copy);
boolean error = false;
if (statement.eClass() != YangPackage.Literals.MUST && statement.eClass() != YangPackage.Literals.UNIQUE
&& statement.eClass() != YangPackage.Literals.UNKNOWN) {
var existingProperty = targetNode.getSubstatements().stream()
.filter(child -> child.eClass() == statement.eClass()).findFirst();
if (existingProperty.isPresent()) {
error = true;
processedModel.addError(moduleFileName(module), statement,
"the \"" + YangNameUtils.getYangName(statement)
+ "\" property already exists in node \"" + nodeQName(targetNode) + "\"");
}
}
if (!error) {
var copy = ProcessorUtility.copyEObject(statement);
targetNode.getSubstatements().add(copy);
}
}
break;
}
Expand All @@ -132,6 +150,10 @@ protected void processDeviate(Deviate deviate) {
.filter(child -> matchingArguments(child, statement)).findFirst();
if (existingProperty.isPresent()) {
targetNode.getSubstatements().remove(existingProperty.get());
} else {
processedModel.addError(moduleFileName(module), statement,
"the \"" + YangNameUtils.getYangName(statement) + "\" property does not exist in node \""
+ nodeQName(targetNode) + "\"");
}
}
break;
Expand All @@ -143,6 +165,10 @@ protected void processDeviate(Deviate deviate) {
targetNode.getSubstatements().remove(existingProperty.get());
var copy = ProcessorUtility.copyEObject(statement);
targetNode.getSubstatements().add(copy);
} else {
processedModel.addError(moduleFileName(module), statement,
"the \"" + YangNameUtils.getYangName(statement) + "\" property does not exist in node \""
+ nodeQName(targetNode) + "\"");
}
}
break;
Expand All @@ -155,6 +181,16 @@ protected void processDeviate(Deviate deviate) {
}
}

protected String moduleFileName(AbstractModule module) {
return module.eResource().getURI().lastSegment();
}

protected String nodeQName(SchemaNode node) {
// TODO use injected version
var qName = new DefaultDeclarativeQualifiedNameProvider().getFullyQualifiedName(node);
return String.join(":", qName.getSegments());
}

/**
* The properties to delete are identified by substatements to the "delete"
* statement. The substatement's keyword MUST match a corresponding keyword in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public static class Args {
public String deviationModule;

@Parameter(names = { "-f", "--format" }, description = "Output format.")
public Format format = Format.tree;
public Format format;

@Parameter(names = { "-p",
"--path" }, description = "A colon (:) separated list of directories to search for imported modules. Default is the current directory.")
Expand Down Expand Up @@ -91,7 +91,11 @@ public static void main(String[] args) {
}

var output = new StringBuilder();
yangProcessor.serialize(processedData, cliArgs.format, output);
if(cliArgs.format != null) {
yangProcessor.serialize(processedData, cliArgs.format, output);
} else {
processedData.getMessages().forEach(msg -> output.append(msg.toString()).append(System.lineSeparator()));
}
System.out.println(output.toString());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package io.typefox.yang.scoping

import org.eclipse.xtext.naming.IQualifiedNameConverter

class QualifiedNameConverter extends IQualifiedNameConverter.DefaultImpl {
class YangQualifiedNameConverter extends IQualifiedNameConverter.DefaultImpl {

override getDelimiter() {
':'
Expand Down
Loading

0 comments on commit 6a596ec

Please sign in to comment.