X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=yang%2Fyang-parser-impl%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fyangtools%2Fyang%2Fparser%2Fstmt%2Freactor%2FSourceSpecificContext.java;h=c02909a7a1ae38b138947655a0a912705228c194;hb=ee2a8704ba51ccff4f8411689f4e55371aba71a0;hp=ce245436ad51bffaee55452fda422e4578a6068f;hpb=37acd81e00d4597b67aaea92bd2b413afd01d941;p=yangtools.git diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/SourceSpecificContext.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/SourceSpecificContext.java index ce245436ad..c02909a7a1 100644 --- a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/SourceSpecificContext.java +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/SourceSpecificContext.java @@ -8,32 +8,48 @@ package org.opendaylight.yangtools.yang.parser.stmt.reactor; import com.google.common.base.Preconditions; +import com.google.common.base.Verify; import com.google.common.collect.HashMultimap; -import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Multimap; +import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; +import java.util.Set; import javax.annotation.Nullable; import org.opendaylight.yangtools.concepts.Mutable; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.QNameModule; -import org.opendaylight.yangtools.yang.common.YangConstants; +import org.opendaylight.yangtools.yang.common.YangVersion; +import org.opendaylight.yangtools.yang.model.api.ModuleIdentifier; import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement; import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace; -import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition; -import org.opendaylight.yangtools.yang.parser.spi.ExtensionNamespace; -import org.opendaylight.yangtools.yang.parser.spi.meta.ImportedNamespaceContext; +import org.opendaylight.yangtools.yang.model.api.meta.StatementSource; +import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException; import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder; import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase; +import org.opendaylight.yangtools.yang.parser.spi.meta.MutableStatement; import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour; import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.NamespaceStorageNode; import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.StorageNodeType; +import org.opendaylight.yangtools.yang.parser.spi.meta.StatementDefinitionNamespace; import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport; +import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupportBundle; +import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext; +import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils; +import org.opendaylight.yangtools.yang.parser.spi.source.BelongsToModuleContext; +import org.opendaylight.yangtools.yang.parser.spi.source.BelongsToPrefixToModuleCtx; +import org.opendaylight.yangtools.yang.parser.spi.source.ImpPrefixToNamespace; +import org.opendaylight.yangtools.yang.parser.spi.source.ImportPrefixToModuleCtx; +import org.opendaylight.yangtools.yang.parser.spi.source.ImportedModuleContext; +import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName; import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule; import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModuleMap; import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinition; @@ -41,9 +57,8 @@ import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinit import org.opendaylight.yangtools.yang.parser.spi.source.SourceException; import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference; import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource; -import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase.ContextBuilder; -import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.UnknownStatementImpl; -import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.Utils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class SourceSpecificContext implements NamespaceStorageNode, NamespaceBehaviour.Registry, Mutable { @@ -53,88 +68,177 @@ public class SourceSpecificContext implements NamespaceStorageNode, NamespaceBeh FINISHED } - private final StatementStreamSource source; - private final BuildGlobalContext currentContext; - private final Collection importedNamespaces = new ArrayList<>(); - private final Multimap modifiers = HashMultimap.create(); + private static final Logger LOG = LoggerFactory.getLogger(SourceSpecificContext.class); - private RootStatementContext root; + private final Multimap modifiers = HashMultimap.create(); + private final QNameToStatementDefinitionMap qnameToStmtDefMap = new QNameToStatementDefinitionMap(); + private final PrefixToModuleMap prefixToModuleMap = new PrefixToModuleMap(); + private final BuildGlobalContext currentContext; + private final StatementStreamSource source; + /* + * "imported" namespaces in this source -- this points to RootStatementContexts of + * - modules imported via 'import' statement + * - parent module, declared via 'belongs-to' statement + */ + private Collection> importedNamespaces = ImmutableList.of(); + private ModelProcessingPhase finishedPhase = ModelProcessingPhase.INIT; private ModelProcessingPhase inProgressPhase; - private ModelProcessingPhase finishedPhase; - private final QNameToStatementDefinitionMap qNameToStmtDefMap = new QNameToStatementDefinitionMap(); - private final PrefixToModuleMap prefixToModuleMap = new PrefixToModuleMap(); + private RootStatementContext root; + SourceSpecificContext(final BuildGlobalContext currentContext, final StatementStreamSource source) { + this.currentContext = Preconditions.checkNotNull(currentContext); + this.source = Preconditions.checkNotNull(source); + } - SourceSpecificContext(final BuildGlobalContext currentContext,final StatementStreamSource source) { - this.source = source; - this.currentContext = currentContext; + boolean isEnabledSemanticVersioning() { + return currentContext.isEnabledSemanticVersioning(); } - StatementDefinitionContext getDefinition(final QName name) { - return currentContext.getStatementDefinition(name); + ModelProcessingPhase getInProgressPhase() { + return inProgressPhase; } - ContextBuilder createDeclaredChild(final StatementContextBase current, final QName name, final StatementSourceReference ref) { - StatementDefinitionContext def = getDefinition(name); + StatementContextBase createDeclaredChild(final StatementContextBase current, final int childId, + final QName name, final String argument, final StatementSourceReference ref) { + if (current != null) { + // Fast path: we are entering a statement which was emitted in previous phase + StatementContextBase existing = current.lookupSubstatement(childId); + while (existing != null && StatementSource.CONTEXT == existing.getStatementSource()) { + existing = existing.lookupSubstatement(childId); + } + if (existing != null) { + return existing; + } + } - //extensions + StatementDefinitionContext def = currentContext.getStatementDefinition(getRootVersion(), name); if (def == null) { - if (Utils.isValidStatementDefinition(prefixToModuleMap, qNameToStmtDefMap, name)) { - def = new StatementDefinitionContext<>(new UnknownStatementImpl.Definition(qNameToStmtDefMap.get(Utils.trimPrefix(name)))); + def = currentContext.getModelDefinedStatementDefinition(name); + if (def == null) { + final StatementSupport extension = qnameToStmtDefMap.get(name); + if (extension != null) { + def = new StatementDefinitionContext<>(extension); + currentContext.putModelDefinedStatementDefinition(name, def); + } } + } else if (current != null && StmtContextUtils.isUnrecognizedStatement(current)) { + /* + * This code wraps statements encountered inside an extension so + * they do not get confused with regular statements. + */ + def = Preconditions.checkNotNull(current.definition().getAsUnknownStatementDefinition(def), + "Unable to create unknown statement definition of yang statement %s in unknown statement %s", def, + current); } - Preconditions.checkArgument(def != null, "Statement %s does not have type mapping defined.", name); - if(current == null) { - return createDeclaredRoot(def,ref); + InferenceException.throwIfNull(def, ref, "Statement %s does not have type mapping defined.", name); + if (def.hasArgument()) { + SourceException.throwIfNull(argument, ref, "Statement %s requires an argument", name); + } else { + SourceException.throwIf(argument != null, ref, "Statement %s does not take argument", name); } - return current.substatementBuilder(def, ref); - } - @SuppressWarnings({ "rawtypes", "unchecked" }) - private ContextBuilder createDeclaredRoot(final StatementDefinitionContext def, final StatementSourceReference ref) { - return new ContextBuilder(def,ref) { + /* + * If the current statement definition has argument specific + * sub-definitions, get argument specific sub-definition based on given + * argument (e.g. type statement need to be specialized based on its + * argument). + */ + if (def.hasArgumentSpecificSubDefinitions()) { + def = def.getSubDefinitionSpecificForArgument(argument); + } - @Override - public StatementContextBase build() throws SourceException { - if(root == null) { - root = new RootStatementContext(this, SourceSpecificContext.this); - } else { - Preconditions.checkState(root.getIdentifier().equals(getIdentifier()), "Root statement was already defined as %s.", root.getIdentifier()); - } - root.resetLists(); - return root; - } + if (current != null) { + return current.createSubstatement(childId, def, ref, argument); + } - }; + /* + * If root is null or root version is other than default, + * we need to create new root. + */ + if (root == null) { + root = new RootStatementContext<>(this, def, ref, argument); + } else if (!RootStatementContext.DEFAULT_VERSION.equals(root.getRootVersion()) + && inProgressPhase == ModelProcessingPhase.SOURCE_LINKAGE) { + root = new RootStatementContext<>(this, def, ref, argument, root.getRootVersion(), + root.getRootIdentifier()); + } else { + final QName rootStatement = root.definition().getStatementName(); + final String rootArgument = root.rawStatementArgument(); + + Preconditions.checkState(Objects.equals(def.getStatementName(), rootStatement) + && Objects.equals(argument, rootArgument), "Root statement was already defined as '%s %s'.", + rootStatement, rootArgument); + } + return root; } - RootStatementContext getRoot() { + RootStatementContext getRoot() { return root; } + /** + * Return version of root statement context. + * + * @return version of root statement context + */ + YangVersion getRootVersion() { + return root != null ? root.getRootVersion() : RootStatementContext.DEFAULT_VERSION; + } + DeclaredStatement buildDeclared() { return root.buildDeclared(); } - EffectiveStatement buildEffective() { + EffectiveStatement buildEffective() { return root.buildEffective(); } void startPhase(final ModelProcessingPhase phase) { - @Nullable ModelProcessingPhase previousPhase = phase.getPreviousPhase(); - Preconditions.checkState(Objects.equals(previousPhase, finishedPhase)); - Preconditions.checkState(modifiers.get(previousPhase).isEmpty()); + @Nullable final ModelProcessingPhase previousPhase = phase.getPreviousPhase(); + Verify.verify(Objects.equals(previousPhase, finishedPhase), + "Phase sequencing violation: previous phase should be %s, source %s has %s", previousPhase, source, + finishedPhase); + + final Collection previousModifiers = modifiers.get(previousPhase); + Preconditions.checkState(previousModifiers.isEmpty(), + "Previous phase %s has unresolved modifiers %s in source %s", previousPhase, previousModifiers, source); + inProgressPhase = phase; + LOG.debug("Source {} started phase {}", source, phase); + } + + private void updateImportedNamespaces(final Class type, final Object value) { + if (BelongsToModuleContext.class.isAssignableFrom(type) || ImportedModuleContext.class.isAssignableFrom(type)) { + if (importedNamespaces.isEmpty()) { + importedNamespaces = new ArrayList<>(1); + } + + Verify.verify(value instanceof RootStatementContext); + importedNamespaces.add((RootStatementContext) value); + } } @Override - public > void addToLocalStorage(final Class type, final K key, final V value) { - if(ImportedNamespaceContext.class.isAssignableFrom(type)) { - importedNamespaces.add((NamespaceStorageNode) value); + public > V putToLocalStorage(final Class type, final K key, + final V value) { + // RootStatementContext takes care of IncludedModuleContext and the rest... + final V ret = getRoot().putToLocalStorage(type, key, value); + // FIXME: what about duplicates? + updateImportedNamespaces(type, value); + return ret; + } + + @Override + public > V putToLocalStorageIfAbsent(final Class type, final K key, + final V value) { + // RootStatementContext takes care of IncludedModuleContext and the rest... + final V ret = getRoot().putToLocalStorageIfAbsent(type, key, value); + if (ret == null) { + updateImportedNamespaces(type, value); } - getRoot().addToLocalStorage(type, key, value); + return ret; } @Override @@ -145,12 +249,31 @@ public class SourceSpecificContext implements NamespaceStorageNode, NamespaceBeh @Override public > V getFromLocalStorage(final Class type, final K key) { final V potentialLocal = getRoot().getFromLocalStorage(type, key); - if(potentialLocal != null) { + if (potentialLocal != null) { + return potentialLocal; + } + + for (final NamespaceStorageNode importedSource : importedNamespaces) { + final V potential = importedSource.getFromLocalStorage(type, key); + if (potential != null) { + return potential; + } + } + return null; + } + + @Nullable + @Override + public > Map getAllFromLocalStorage(final Class type) { + final Map potentialLocal = getRoot().getAllFromLocalStorage(type); + if (potentialLocal != null) { return potentialLocal; } - for(NamespaceStorageNode importedSource : importedNamespaces) { - V potential = importedSource.getFromLocalStorage(type, key); - if(potential != null) { + + for (final NamespaceStorageNode importedSource : importedNamespaces) { + final Map potential = importedSource.getAllFromLocalStorage(type); + + if (potential != null) { return potential; } } @@ -158,7 +281,8 @@ public class SourceSpecificContext implements NamespaceStorageNode, NamespaceBeh } @Override - public > NamespaceBehaviour getNamespaceBehaviour(final Class type) { + public > NamespaceBehaviour getNamespaceBehaviour( + final Class type) { return currentContext.getNamespaceBehaviour(type); } @@ -168,43 +292,42 @@ public class SourceSpecificContext implements NamespaceStorageNode, NamespaceBeh } PhaseCompletionProgress tryToCompletePhase(final ModelProcessingPhase phase) throws SourceException { - Collection currentPhaseModifiers = modifiers.get(phase); + final Collection currentPhaseModifiers = modifiers.get(phase); - boolean hasProgressed = hasProgress(currentPhaseModifiers); + boolean hasProgressed = tryToProgress(currentPhaseModifiers); - boolean phaseCompleted = root.tryToCompletePhase(phase); + Preconditions.checkNotNull(this.root, "Malformed source. Valid root element is missing."); + final boolean phaseCompleted = root.tryToCompletePhase(phase); - hasProgressed = (hasProgress(currentPhaseModifiers) | hasProgressed); + hasProgressed |= tryToProgress(currentPhaseModifiers); - if(phaseCompleted && (currentPhaseModifiers.isEmpty())) { + if (phaseCompleted && currentPhaseModifiers.isEmpty()) { finishedPhase = phase; + LOG.debug("Source {} finished phase {}", source, phase); return PhaseCompletionProgress.FINISHED; } - if(hasProgressed) { - return PhaseCompletionProgress.PROGRESS; - } - return PhaseCompletionProgress.NO_PROGRESS; - } - - private static boolean hasProgress(final Collection currentPhaseModifiers) { + return hasProgressed ? PhaseCompletionProgress.PROGRESS : PhaseCompletionProgress.NO_PROGRESS; + } - Iterator modifier = currentPhaseModifiers.iterator(); + private static boolean tryToProgress(final Collection currentPhaseModifiers) { boolean hasProgressed = false; - while(modifier.hasNext()) { - if(modifier.next().isApplied()) { + + // Try making forward progress ... + final Iterator modifier = currentPhaseModifiers.iterator(); + while (modifier.hasNext()) { + if (modifier.next().tryApply()) { modifier.remove(); hasProgressed = true; } } return hasProgressed; - } ModelActionBuilder newInferenceAction(final ModelProcessingPhase phase) { - ModifierImpl action = new ModifierImpl(phase); + final ModifierImpl action = new ModifierImpl(); modifiers.put(phase, action); return action; } @@ -215,61 +338,123 @@ public class SourceSpecificContext implements NamespaceStorageNode, NamespaceBeh + finishedPhase + "]"; } - SourceException failModifiers(final ModelProcessingPhase identifier) { - InferenceException sourceEx = new InferenceException("Fail to infer source relationships", root.getStatementSourceReference()); - - - for(ModifierImpl mod : modifiers.get(identifier)) { + Optional failModifiers(final ModelProcessingPhase identifier) { + final List exceptions = new ArrayList<>(); + for (final ModifierImpl mod : modifiers.get(identifier)) { try { mod.failModifier(); - } catch (SourceException e) { - sourceEx.addSuppressed(e); + } catch (final SourceException e) { + exceptions.add(e); } } - return sourceEx; + + if (exceptions.isEmpty()) { + return Optional.empty(); + } + + final String message = String.format("Yang model processing phase %s failed", identifier); + final InferenceException e = new InferenceException(message, root.getStatementSourceReference(), + exceptions.get(0)); + exceptions.listIterator(1).forEachRemaining(e::addSuppressed); + + return Optional.of(e); } void loadStatements() throws SourceException { + LOG.trace("Source {} loading statements for phase {}", source, inProgressPhase); + switch (inProgressPhase) { + case SOURCE_PRE_LINKAGE: + source.writePreLinkage(new StatementContextWriter(this, inProgressPhase), stmtDef()); + break; case SOURCE_LINKAGE: - source.writeLinkage(new StatementContextWriter(this, inProgressPhase),stmtDef()); - break; + source.writeLinkage(new StatementContextWriter(this, inProgressPhase), stmtDef(), preLinkagePrefixes(), + getRootVersion()); + break; case STATEMENT_DEFINITION: - source.writeLinkageAndStatementDefinitions(new StatementContextWriter(this, inProgressPhase), stmtDef(), prefixes()); - break; + source.writeLinkageAndStatementDefinitions(new StatementContextWriter(this, inProgressPhase), stmtDef(), + prefixes(), getRootVersion()); + break; case FULL_DECLARATION: - source.writeFull(new StatementContextWriter(this, inProgressPhase), stmtDef(), prefixes()); - break; - default: - break; + source.writeFull(new StatementContextWriter(this, inProgressPhase), stmtDef(), prefixes(), + getRootVersion()); + break; + default: + break; } } + private PrefixToModule preLinkagePrefixes() { + final PrefixToModuleMap preLinkagePrefixes = new PrefixToModuleMap(true); + final Map prefixToNamespaceMap = getAllFromLocalStorage(ImpPrefixToNamespace.class); + if (prefixToNamespaceMap == null) { + //:FIXME if it is a submodule without any import, the map is null. Handle also submodules and includes... + return null; + } + + prefixToNamespaceMap.forEach((key, value) -> preLinkagePrefixes.put(key, QNameModule.create(value, null))); + return preLinkagePrefixes; + } + private PrefixToModule prefixes() { - Map prefixes = (Map) currentContext.getAllFromNamespace(PrefixToModule.class); - for (Map.Entry prefix : prefixes.entrySet()) { - prefixToModuleMap.put(prefix.getKey(), prefix.getValue()); + final Map> allImports = getRoot().getAllFromNamespace( + ImportPrefixToModuleCtx.class); + if (allImports != null) { + allImports.forEach((key, value) -> + prefixToModuleMap.put(key, getRoot().getFromNamespace(ModuleCtxToModuleQName.class, value))); } + + final Map> allBelongsTo = getRoot().getAllFromNamespace( + BelongsToPrefixToModuleCtx.class); + if (allBelongsTo != null) { + allBelongsTo.forEach((key, value) -> + prefixToModuleMap.put(key, getRoot().getFromNamespace(ModuleCtxToModuleQName.class, value))); + } + return prefixToModuleMap; } private QNameToStatementDefinition stmtDef() { - //regular YANG statements added - ImmutableMap> definitions = currentContext.getSupportsForPhase( - inProgressPhase).getDefinitions(); - for (Map.Entry> entry : definitions.entrySet()) { - qNameToStmtDefMap.put(entry.getKey(), entry.getValue()); + // regular YANG statements and extension supports added + final StatementSupportBundle supportsForPhase = currentContext.getSupportsForPhase(inProgressPhase); + qnameToStmtDefMap.putAll(supportsForPhase.getCommonDefinitions()); + qnameToStmtDefMap.putAll(supportsForPhase.getDefinitionsSpecificForVersion(getRootVersion())); + + // No further actions needed + if (inProgressPhase != ModelProcessingPhase.FULL_DECLARATION) { + return qnameToStmtDefMap; } - //extensions added - if (inProgressPhase.equals(ModelProcessingPhase.FULL_DECLARATION)) { - Map> extensions = (Map>) currentContext.getAllFromNamespace(ExtensionNamespace.class); - if (extensions != null) { - for (Map.Entry> extension : extensions.entrySet()) { - qNameToStmtDefMap.put(new QName(YangConstants.RFC6020_YIN_NAMESPACE, extension.getKey().getLocalName()), (StatementDefinition) extension.getValue().definition().getFactory()); + // We need to any and all extension statements which have been declared in the context + final Map> extensions = currentContext.getAllFromNamespace( + StatementDefinitionNamespace.class); + if (extensions != null) { + extensions.forEach((qname, support) -> { + final StatementSupport existing = qnameToStmtDefMap.putIfAbsent(qname, support); + if (existing != null) { + LOG.debug("Source {} already defines statement {} as {}", source, qname, existing); + } else { + LOG.debug("Source {} defined statement {} as {}", source, qname, support); } - } + }); } - return qNameToStmtDefMap; + + return qnameToStmtDefMap; + } + + public Set getSupportedVersions() { + return currentContext.getSupportedVersions(); + } + + void addMutableStmtToSeal(final MutableStatement mutableStatement) { + currentContext.addMutableStmtToSeal(mutableStatement); + } + + Collection getRequiredSources() { + return root.getRequiredSources(); + } + + ModuleIdentifier getRootIdentifier() { + return root.getRootIdentifier(); } }