-
- public static YangContext parseYangSource(final InputStream stream) throws IOException, YangSyntaxErrorException {
- final YangLexer lexer = new YangLexer(new ANTLRInputStream(stream));
- final CommonTokenStream tokens = new CommonTokenStream(lexer);
- final YangParser parser = new YangParser(tokens);
- parser.removeErrorListeners();
-
- final YangErrorListener errorListener = new YangErrorListener();
- parser.addErrorListener(errorListener);
-
- final YangContext result = parser.yang();
- errorListener.validate();
-
- return result;
- }
-
- /**
- * Mini parser: This parsing context does not validate full YANG module,
- * only parses header up to the revisions and imports.
- *
- * @see org.opendaylight.yangtools.yang.parser.impl.util.YangModelDependencyInfo
- */
- public static YangContext parseStreamWithoutErrorListeners(final InputStream yangStream) {
- YangContext result = null;
- try {
- final ANTLRInputStream input = new ANTLRInputStream(yangStream);
- final YangLexer lexer = new YangLexer(input);
- final CommonTokenStream tokens = new CommonTokenStream(lexer);
- final YangParser parser = new YangParser(tokens);
- parser.removeErrorListeners();
- result = parser.yang();
- } catch (IOException e) {
- LOG.warn("Exception while reading yang file: " + yangStream, e);
- }
- return result;
- }
-
- /**
- * Creates builder-to-module map based on given modules. Method first
- * resolve unresolved type references, instantiate groupings through uses
- * statements and perform augmentation.
- *
- * Node resolving must be performed in following order:
- * <ol>
- * <li>
- * unresolved type references</li>
- * <li>
- * uses in groupings</li>
- * <li>
- * uses in other nodes</li>
- * <li>
- * augments</li>
- * </ol>
- *
- * @param modules
- * all loaded modules
- * @return modules mapped on their builders
- */
- private Map<ModuleBuilder, Module> build(final Map<URI, TreeMap<Date, ModuleBuilder>> modules) {
- resolveDirtyNodes(modules);
- resolveAugmentsTargetPath(modules);
- resolveUsesTargetGrouping(modules);
- resolveUsesForGroupings(modules);
- resolveUsesForNodes(modules);
- resolveAugments(modules);
- resolveIdentities(modules);
-
- // build
- final Map<ModuleBuilder, Module> result = new LinkedHashMap<>();
- for (Map.Entry<URI, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
- for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
- final ModuleBuilder moduleBuilder = childEntry.getValue();
- final Module module = moduleBuilder.build();
- result.put(moduleBuilder, module);
- }
- }
- return result;
- }
-
- /**
- * Resolve all unresolved type references.
- *
- * @param modules
- * all loaded modules
- */
- private void resolveDirtyNodes(final Map<URI, TreeMap<Date, ModuleBuilder>> modules) {
- for (Map.Entry<URI, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
- for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
- final ModuleBuilder module = childEntry.getValue();
- resolveUnknownNodes(modules, module);
- resolveDirtyNodes(modules, module);
- }
- }
- }
-
- /**
- * Search for dirty nodes (node which contains UnknownType) and resolve
- * unknown types.
- *
- * @param modules
- * all available modules
- * @param module
- * current module
- */
- private void resolveDirtyNodes(final Map<URI, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
- final Set<TypeAwareBuilder> dirtyNodes = module.getDirtyNodes();
- if (!dirtyNodes.isEmpty()) {
- for (TypeAwareBuilder nodeToResolve : dirtyNodes) {
- if (nodeToResolve instanceof UnionTypeBuilder) {
- // special handling for union types
- resolveTypeUnion((UnionTypeBuilder) nodeToResolve, modules, module);
- } else if (nodeToResolve.getTypedef() instanceof IdentityrefTypeBuilder) {
- // special handling for identityref types
- IdentityrefTypeBuilder idref = (IdentityrefTypeBuilder) nodeToResolve.getTypedef();
- IdentitySchemaNodeBuilder identity = findBaseIdentity(module, idref.getBaseString(),
- idref.getLine());
- if (identity == null) {
- throw new YangParseException(module.getName(), idref.getLine(), "Failed to find base identity");
- }
- idref.setBaseIdentity(identity);
- nodeToResolve.setType(idref.build());
- } else {
- resolveType(nodeToResolve, modules, module);
- }
- }
- }
- }
-
- /**
- * Traverse through augmentations of modules and fix their child nodes
- * schema path.
- *
- * @param modules
- * all loaded modules
- */
- private void resolveAugmentsTargetPath(final Map<URI, TreeMap<Date, ModuleBuilder>> modules) {
- // collect augments from all loaded modules
- final List<AugmentationSchemaBuilder> allAugments = new ArrayList<>();
- for (Map.Entry<URI, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
- for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
- allAugments.addAll(inner.getValue().getAllAugments());
- }
- }
-
- for (AugmentationSchemaBuilder augment : allAugments) {
- setCorrectAugmentTargetPath(augment);
- }
- }
-
- /**
- * Find augment target and set correct schema path for all its child nodes.
- *
- * @param augment
- * augment to resolve
- */
- private void setCorrectAugmentTargetPath(final AugmentationSchemaBuilder augment) {
- Builder parent = augment.getParent();
- final SchemaPath targetNodeSchemaPath;
-
- if (parent instanceof UsesNodeBuilder) {
- targetNodeSchemaPath = findUsesAugmentTargetNodePath(((UsesNodeBuilder) parent).getParent(), augment);
- } else {
- targetNodeSchemaPath = augment.getTargetPath();
- }
-
- for (DataSchemaNodeBuilder childNode : augment.getChildNodeBuilders()) {
- correctPathForAugmentNodes(childNode, targetNodeSchemaPath);
- }
- }
-
- private SchemaPath findUsesAugmentTargetNodePath(DataNodeContainerBuilder usesParent,
- AugmentationSchemaBuilder augment) {
- QName parentQName = usesParent.getQName();
- final QNameModule qnm;
- if (parentQName == null) {
- ModuleBuilder m = BuilderUtils.getParentModule(usesParent);
- qnm = m.getQNameModule();
- } else {
- qnm = parentQName.getModule();
- }
-
- SchemaPath path = usesParent.getPath();
- for (QName qname : augment.getTargetPath().getPathFromRoot()) {
- path = path.createChild(QName.create(qnm, qname.getLocalName()));
- }
-
- return path;
- }
-
- /**
- * Set new schema path to node and all its child nodes based on given parent
- * path. This method do not change the namespace.
- *
- * @param node
- * node which schema path should be updated
- * @param parentPath
- * schema path of parent node
- */
- private void correctPathForAugmentNodes(final DataSchemaNodeBuilder node, final SchemaPath parentPath) {
- SchemaPath newPath = parentPath.createChild(node.getQName());
- node.setPath(newPath);
- if (node instanceof DataNodeContainerBuilder) {
- for (DataSchemaNodeBuilder child : ((DataNodeContainerBuilder) node).getChildNodeBuilders()) {
- correctPathForAugmentNodes(child, node.getPath());
- }
- }
- if (node instanceof ChoiceBuilder) {
- for (ChoiceCaseBuilder child : ((ChoiceBuilder) node).getCases()) {
- correctPathForAugmentNodes(child, node.getPath());
- }
- }
- }
-
- /**
- * Check augments for mandatory nodes. If the target node is in another
- * module, then nodes added by the augmentation MUST NOT be mandatory nodes.
- * If mandatory node is found, throw an exception.
- *
- * @param augments
- * augments to check
- */
- private void checkAugmentMandatoryNodes(final Collection<AugmentationSchemaBuilder> augments) {
- for (AugmentationSchemaBuilder augment : augments) {
- URI augmentTargetNs = augment.getTargetPath().getPathFromRoot().iterator().next().getNamespace();
- Date augmentTargetRev = augment.getTargetPath().getPathFromRoot().iterator().next().getRevision();
- ModuleBuilder module = BuilderUtils.getParentModule(augment);
-
- if (augmentTargetNs.equals(module.getNamespace()) && augmentTargetRev.equals(module.getRevision())) {
- continue;
- }
-
- for (DataSchemaNodeBuilder childNode : augment.getChildNodeBuilders()) {
- if (childNode.getConstraints().isMandatory()) {
- throw new YangParseException(augment.getModuleName(), augment.getLine(),
- "Error in augment parsing: cannot augment mandatory node "
- + childNode.getQName().getLocalName());
- }
- }
- }
- }
-
- /**
- * Go through all augment definitions and resolve them.
- *
- * @param modules
- * all loaded modules topologically sorted (based on dependencies
- * between each other)
- */
- private void resolveAugments(final Map<URI, TreeMap<Date, ModuleBuilder>> modules) {
- List<ModuleBuilder> all = new ArrayList<>();
- for (Map.Entry<URI, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
- for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
- all.add(inner.getValue());
- }
- }
-
- for (ModuleBuilder mb : all) {
- if (mb != null) {
- List<AugmentationSchemaBuilder> augments = mb.getAllAugments();
- checkAugmentMandatoryNodes(augments);
- Collections.sort(augments, Comparators.AUGMENT_BUILDER_COMP);
- for (AugmentationSchemaBuilder augment : augments) {
- if (!(augment.isResolved())) {
- boolean resolved = resolveAugment(augment, mb, modules);
- if (!resolved) {
- throw new YangParseException(augment.getModuleName(), augment.getLine(),
- "Error in augment parsing: failed to find augment target: " + augment);
- }
- }
- }
- }
- }
- }
-
- /**
- * Perform augmentation defined under uses statement.
- *
- * @param augment
- * augment to resolve
- * @param module
- * current module
- * @param modules
- * all loaded modules
- * @return true if augment process succeed
- */
- private boolean resolveUsesAugment(final AugmentationSchemaBuilder augment, final ModuleBuilder module,
- final Map<URI, TreeMap<Date, ModuleBuilder>> modules) {
- if (augment.isResolved()) {
- return true;
- }
-
- UsesNodeBuilder usesNode = (UsesNodeBuilder) augment.getParent();
- DataNodeContainerBuilder parentNode = usesNode.getParent();
- Optional<SchemaNodeBuilder> potentialTargetNode;
- SchemaPath resolvedTargetPath = findUsesAugmentTargetNodePath(parentNode, augment);
- if (parentNode instanceof ModuleBuilder && resolvedTargetPath.isAbsolute()) {
- // Uses is directly used in module body, we lookup
- // We lookup in data namespace to find correct augmentation target
- potentialTargetNode = findSchemaNodeInModule(resolvedTargetPath, (ModuleBuilder) parentNode);
- } else {
- // Uses is used in local context (be it data namespace or grouping
- // namespace,
- // since all nodes via uses are imported to localName, it is safe to
- // to proceed only with local names.
- //
- // Conflicting elements in other namespaces are still not present
- // since resolveUsesAugment occurs before augmenting from external
- // modules.
- potentialTargetNode = Optional.<SchemaNodeBuilder> fromNullable(findSchemaNode(augment.getTargetPath()
- .getPathFromRoot(), (SchemaNodeBuilder) parentNode));
- }
-
- if (potentialTargetNode.isPresent()) {
- SchemaNodeBuilder targetNode = potentialTargetNode.get();
- if (targetNode instanceof AugmentationTargetBuilder) {
- fillAugmentTarget(augment, targetNode);
- ((AugmentationTargetBuilder) targetNode).addAugmentation(augment);
- augment.setResolved(true);
- return true;
- } else {
- LOG.warn(
- "Error in module {} at line {}: Unsupported augment target: {}. Augmentation process skipped.",
- module.getName(), augment.getLine(), potentialTargetNode);
- augment.setResolved(true);
- return true;
- }
- } else {
- throw new YangParseException(module.getName(), augment.getLine(), String.format(
- "Failed to resolve augment in uses. Invalid augment target path: %s", augment.getTargetPath()));
- }
-
- }
-
- /**
- * Find augment target module and perform augmentation.
- *
- * @param augment
- * augment to resolve
- * @param module
- * current module
- * @param modules
- * all loaded modules
- * @return true if augment process succeed
- */
- private boolean resolveAugment(final AugmentationSchemaBuilder augment, final ModuleBuilder module,
- final Map<URI, TreeMap<Date, ModuleBuilder>> modules) {
- if (augment.isResolved()) {
- return true;
- }
-
- QName targetModuleName = augment.getTargetPath().getPathFromRoot().iterator().next();
- ModuleBuilder targetModule = BuilderUtils.findModule(targetModuleName, modules);
- if (targetModule == null) {
- throw new YangParseException(module.getModuleName(), augment.getLine(), "Failed to resolve augment "
- + augment);
- }
-
- return processAugmentation(augment, targetModule);
- }
-
- /**
- * Go through identity statements defined in current module and resolve
- * their 'base' statement.
- *
- * @param modules
- * all loaded modules
- */
- private void resolveIdentities(final Map<URI, TreeMap<Date, ModuleBuilder>> modules) {
- for (Map.Entry<URI, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
- for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
- ModuleBuilder module = inner.getValue();
- final Set<IdentitySchemaNodeBuilder> identities = module.getAddedIdentities();
- for (IdentitySchemaNodeBuilder identity : identities) {
- resolveIdentity(module, identity);
- }
- }
- }
- }
-
- private void resolveIdentity(final ModuleBuilder module, final IdentitySchemaNodeBuilder identity) {
- final String baseIdentityName = identity.getBaseIdentityName();
- if (baseIdentityName != null) {
- IdentitySchemaNodeBuilder result = null;
- if (baseIdentityName.contains(":")) {
- final int line = identity.getLine();
- String[] splittedBase = baseIdentityName.split(":");
- if (splittedBase.length > 2) {
- throw new YangParseException(module.getName(), line, "Failed to parse identityref base: "
- + baseIdentityName);
- }
- String prefix = splittedBase[0];
- String name = splittedBase[1];
- ModuleBuilder dependentModule = BuilderUtils.getModuleByPrefix(module, prefix);
- result = BuilderUtils.findIdentity(dependentModule.getAddedIdentities(), name);
- } else {
- result = BuilderUtils.findIdentity(module.getAddedIdentities(), baseIdentityName);
- }
- identity.setBaseIdentity(result);
- }
- }
-
- /**
- * Find and add reference of uses target grouping.
- *
- * @param modules
- * all loaded modules
- */
- private void resolveUsesTargetGrouping(final Map<URI, TreeMap<Date, ModuleBuilder>> modules) {
- final List<UsesNodeBuilder> allUses = new ArrayList<>();
- for (Map.Entry<URI, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
- for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
- allUses.addAll(inner.getValue().getAllUsesNodes());
- }
- }
- for (UsesNodeBuilder usesNode : allUses) {
- ModuleBuilder module = BuilderUtils.getParentModule(usesNode);
- final GroupingBuilder targetGroupingBuilder = GroupingUtils.getTargetGroupingFromModules(usesNode, modules,
- module);
- usesNode.setGrouping(targetGroupingBuilder);
- }
- }
-
- /**
- * Resolve uses statements defined in groupings.
- *
- * @param modules
- * all loaded modules
- */
- private void resolveUsesForGroupings(final Map<URI, TreeMap<Date, ModuleBuilder>> modules) {
- final Set<GroupingBuilder> allGroupings = new HashSet<>();
- for (Map.Entry<URI, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
- for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
- ModuleBuilder module = inner.getValue();
- allGroupings.addAll(module.getAllGroupings());
- }
- }
- final List<GroupingBuilder> sorted = GroupingSort.sort(allGroupings);
- for (GroupingBuilder gb : sorted) {
- List<UsesNodeBuilder> usesNodes = new ArrayList<>(GroupingSort.getAllUsesNodes(gb));
- Collections.sort(usesNodes, new GroupingUtils.UsesComparator());
- for (UsesNodeBuilder usesNode : usesNodes) {
- resolveUses(usesNode, modules);
- }
- }
- }
-
- /**
- * Resolve uses statements.
- *
- * @param modules
- * all loaded modules
- */
- private void resolveUsesForNodes(final Map<URI, TreeMap<Date, ModuleBuilder>> modules) {
- for (Map.Entry<URI, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
- for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
- ModuleBuilder module = inner.getValue();
- List<UsesNodeBuilder> usesNodes = module.getAllUsesNodes();
- Collections.sort(usesNodes, new GroupingUtils.UsesComparator());
- for (UsesNodeBuilder usesNode : usesNodes) {
- resolveUses(usesNode, modules);
- }
- }
- }
- }
-
- /**
- * Find target grouping and copy its child nodes to current location with
- * new namespace.
- *
- * @param usesNode
- * uses node to resolve
- * @param modules
- * all loaded modules
- */
- private void resolveUses(final UsesNodeBuilder usesNode, final Map<URI, TreeMap<Date, ModuleBuilder>> modules) {
- if (!usesNode.isResolved()) {
- DataNodeContainerBuilder parent = usesNode.getParent();
- ModuleBuilder module = BuilderUtils.getParentModule(parent);
- GroupingBuilder target = GroupingUtils.getTargetGroupingFromModules(usesNode, modules, module);
-
- int index = nodeAfterUsesIndex(usesNode);
- List<DataSchemaNodeBuilder> targetNodes = target.instantiateChildNodes(parent);
- for (DataSchemaNodeBuilder targetNode : targetNodes) {
- parent.addChildNode(index++, targetNode);
- }
- parent.getTypeDefinitionBuilders().addAll(target.instantiateTypedefs(parent));
- parent.getGroupingBuilders().addAll(target.instantiateGroupings(parent));
- parent.getUnknownNodes().addAll(target.instantiateUnknownNodes(parent));
- usesNode.setResolved(true);
- for (AugmentationSchemaBuilder augment : usesNode.getAugmentations()) {
- resolveUsesAugment(augment, module, modules);
- }
-
- GroupingUtils.performRefine(usesNode);
- }
- }
-
- private int nodeAfterUsesIndex(final UsesNodeBuilder usesNode) {
- DataNodeContainerBuilder parent = usesNode.getParent();
- int usesLine = usesNode.getLine();
-
- List<DataSchemaNodeBuilder> childNodes = parent.getChildNodeBuilders();
- if (childNodes.isEmpty()) {
- return 0;
- }
-
- DataSchemaNodeBuilder nextNodeAfterUses = null;
- for (DataSchemaNodeBuilder childNode : childNodes) {
- if (!(childNode.isAddedByUses()) && !(childNode.isAugmenting())) {
- if (childNode.getLine() > usesLine) {
- nextNodeAfterUses = childNode;
- break;
- }
- }
- }
-
- // uses is declared after child nodes
- if (nextNodeAfterUses == null) {
- return childNodes.size();
- }
-
- return parent.getChildNodeBuilders().indexOf(nextNodeAfterUses);
- }
-
- /**
- * Try to find extension describing this unknown node and assign it to
- * unknown node builder.
- *
- * @param modules
- * all loaded modules
- * @param module
- * current module
- */
- private void resolveUnknownNodes(final Map<URI, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
- for (UnknownSchemaNodeBuilder usnb : module.getAllUnknownNodes()) {
- QName nodeType = usnb.getNodeType();
- String localName = usnb.getNodeType().getLocalName();
- ModuleBuilder dependentModule = BuilderUtils.findModule(nodeType, modules);
-
- if (dependentModule == null) {
- LOG.warn(
- "Error in module {} at line {}: Failed to resolve node {}: no such extension definition found.",
- module.getName(), usnb.getLine(), usnb);
- continue;
- }
-
- ExtensionBuilder extBuilder = findExtBuilder(localName, dependentModule.getAddedExtensions());
- if (extBuilder == null) {
- ExtensionDefinition extDef = findExtDef(localName, dependentModule.getExtensions());
- if (extDef == null) {
- LOG.warn(
- "Error in module {} at line {}: Failed to resolve node {}: no such extension definition found.",
- module.getName(), usnb.getLine(), usnb);
- } else {
- usnb.setExtensionDefinition(extDef);
- }
- } else {
- usnb.setExtensionBuilder(extBuilder);
- }
- }
- }
-
- private ExtensionBuilder findExtBuilder(final String name, final Collection<ExtensionBuilder> extensions) {
- for (ExtensionBuilder extension : extensions) {
- if (extension.getQName().getLocalName().equals(name)) {
- return extension;
- }
- }
- return null;
- }
-
- private ExtensionDefinition findExtDef(final String name, final Collection<ExtensionDefinition> extensions) {
- for (ExtensionDefinition extension : extensions) {
- if (extension.getQName().getLocalName().equals(name)) {
- return extension;
- }
- }
- return null;
- }
-