/* * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.yangtools.yang.parser.stmt.reactor; import com.google.common.base.Preconditions; import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Multimap; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; 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.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.model.api.stmt.ExtensionStatement; import org.opendaylight.yangtools.yang.parser.spi.ExtensionNamespace; import org.opendaylight.yangtools.yang.parser.spi.meta.ImportedNamespaceContext; 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.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.QNameCacheNamespace; import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport; import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext; import org.opendaylight.yangtools.yang.parser.spi.source.BelongsToPrefixToModuleIdentifier; import org.opendaylight.yangtools.yang.parser.spi.source.ImpPrefixToModuleIdentifier; import org.opendaylight.yangtools.yang.parser.spi.source.ModuleIdentifierToModuleQName; 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; import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinitionMap; 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.rfc6020.BitsSpecificationImpl; import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.Decimal64SpecificationImpl; import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.EnumSpecificationImpl; import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.IdentityRefSpecificationImpl; import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.InstanceIdentifierSpecificationImpl; import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.LeafrefSpecificationImpl; import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.TypeUtils; import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.UnionSpecificationImpl; import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.UnknownStatementImpl; import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.Utils; public class SourceSpecificContext implements NamespaceStorageNode, NamespaceBehaviour.Registry, Mutable { public enum PhaseCompletionProgress { NO_PROGRESS, PROGRESS, FINISHED } private final StatementStreamSource source; private final BuildGlobalContext currentContext; private final Collection importedNamespaces = new ArrayList<>(); private final Multimap modifiers = HashMultimap.create(); private RootStatementContext root; private ModelProcessingPhase inProgressPhase; private ModelProcessingPhase finishedPhase = ModelProcessingPhase.INIT; private final QNameToStatementDefinitionMap qNameToStmtDefMap = new QNameToStatementDefinitionMap(); private final PrefixToModuleMap prefixToModuleMap = new PrefixToModuleMap(); SourceSpecificContext(final BuildGlobalContext currentContext, final StatementStreamSource source) { this.source = source; this.currentContext = currentContext; } ModelProcessingPhase getInProgressPhase() { return inProgressPhase; } StatementDefinitionContext getDefinition(final QName name) { return currentContext.getStatementDefinition(name); } ContextBuilder createDeclaredChild(final StatementContextBase current, final QName name, final StatementSourceReference ref) { StatementDefinitionContext def = getDefinition(name); if (def == null) { //unknown-stmts (from import, include or local-scope) if (qNameToStmtDefMap.get(name) != null) { final StatementContextBase extension = (StatementContextBase) currentContext .getAllFromNamespace(ExtensionNamespace.class).get(name); if (extension != null) { final QName arg = (QName) extension.getStatementArgument(); final QName qName = current.getFromNamespace(QNameCacheNamespace.class, QName.create(arg, extension.getIdentifier().getArgument())); def = new StatementDefinitionContext<>(new UnknownStatementImpl.Definition( getNewStatementDefinition(qName))); } else { throw new IllegalArgumentException("Not found unknown statement: " + name); } } else { // type-body-stmts def = resolveTypeBodyStmts(name.getLocalName()); } } else if (current != null && current.definition().getRepresentingClass().equals(UnknownStatementImpl.class)) { QName qName = Utils.qNameFromArgument(current, name.getLocalName()); def = new StatementDefinitionContext<>(new UnknownStatementImpl.Definition (getNewStatementDefinition(qName))); } Preconditions.checkArgument(def != null, "Statement %s does not have type mapping defined.", name); if (current == null) { return createDeclaredRoot(def, ref); } return current.substatementBuilder(def, ref); } // FIXME: This should be populated differently StatementDefinition getNewStatementDefinition(final QName qName) { return new ModelDefinedStatementDefinition(qName); } @SuppressWarnings({"rawtypes", "unchecked"}) private ContextBuilder createDeclaredRoot(final StatementDefinitionContext def, final StatementSourceReference ref) { return new ContextBuilder(def, ref) { @Override public StatementContextBase build() throws SourceException { if (root == null) { root = new RootStatementContext(this, SourceSpecificContext.this); } else { Preconditions.checkState(root.getIdentifier().equals(createIdentifier()), "Root statement was already defined as %s.", root.getIdentifier()); } root.resetLists(); return root; } }; } RootStatementContext getRoot() { return root; } DeclaredStatement buildDeclared() { return root.buildDeclared(); } 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()); inProgressPhase = phase; } @Override public > void addToLocalStorage(final Class type, final K key, final V value) { if (ImportedNamespaceContext.class.isAssignableFrom(type)) { importedNamespaces.add((NamespaceStorageNode) value); } getRoot().addToLocalStorage(type, key, value); } @Override public StorageNodeType getStorageNodeType() { return StorageNodeType.SOURCE_LOCAL_SPECIAL; } @Override public > V getFromLocalStorage(final Class type, final K key) { final V potentialLocal = getRoot().getFromLocalStorage(type, key); if (potentialLocal != null) { return potentialLocal; } for (NamespaceStorageNode importedSource : importedNamespaces) { 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 (final NamespaceStorageNode importedSource : importedNamespaces) { final Map potential = importedSource.getAllFromLocalStorage(type); if (potential != null) { return potential; } } return null; } @Override public > NamespaceBehaviour getNamespaceBehaviour(final Class type) { return currentContext.getNamespaceBehaviour(type); } @Override public NamespaceStorageNode getParentNamespaceStorage() { return currentContext; } PhaseCompletionProgress tryToCompletePhase(final ModelProcessingPhase phase) throws SourceException { Collection currentPhaseModifiers = modifiers.get(phase); boolean hasProgressed = tryToProgress(currentPhaseModifiers); boolean phaseCompleted = root.tryToCompletePhase(phase); hasProgressed = (tryToProgress(currentPhaseModifiers) | hasProgressed); if (phaseCompleted && (currentPhaseModifiers.isEmpty())) { finishedPhase = phase; return PhaseCompletionProgress.FINISHED; } if (hasProgressed) { return PhaseCompletionProgress.PROGRESS; } return PhaseCompletionProgress.NO_PROGRESS; } private static boolean tryToProgress(final Collection currentPhaseModifiers) { Iterator modifier = currentPhaseModifiers.iterator(); boolean hasProgressed = false; while (modifier.hasNext()) { if (modifier.next().tryApply()) { modifier.remove(); hasProgressed = true; } } return hasProgressed; } ModelActionBuilder newInferenceAction(final ModelProcessingPhase phase) { ModifierImpl action = new ModifierImpl(phase); modifiers.put(phase, action); return action; } @Override public String toString() { return "SourceSpecificContext [source=" + source + ", current=" + inProgressPhase + ", finished=" + finishedPhase + "]"; } SourceException failModifiers(final ModelProcessingPhase identifier) { final List exceptions = new ArrayList<>(); for (ModifierImpl mod : modifiers.get(identifier)) { try { mod.failModifier(); } catch (SourceException e) { exceptions.add(e); } } final String message = String.format("Yang model processing phase %s failed", identifier); if (exceptions.isEmpty()) { return new InferenceException(message, root.getStatementSourceReference()); } final InferenceException e = new InferenceException(message, root.getStatementSourceReference(), exceptions.get(0)); final Iterator it = exceptions.listIterator(1); while (it.hasNext()) { e.addSuppressed(it.next()); } return e; } void loadStatements() throws SourceException { switch (inProgressPhase) { case SOURCE_LINKAGE: source.writeLinkage(new StatementContextWriter(this, inProgressPhase), stmtDef()); break; case STATEMENT_DEFINITION: source.writeLinkageAndStatementDefinitions(new StatementContextWriter(this, inProgressPhase), stmtDef(), prefixes()); break; case FULL_DECLARATION: source.writeFull(new StatementContextWriter(this, inProgressPhase), stmtDef(), prefixes()); break; default: break; } } private static StatementDefinitionContext resolveTypeBodyStmts(final String typeArgument) { switch (typeArgument) { case TypeUtils.DECIMAL64: return new StatementDefinitionContext<>(new Decimal64SpecificationImpl.Definition()); case TypeUtils.UNION: return new StatementDefinitionContext<>(new UnionSpecificationImpl.Definition()); case TypeUtils.ENUMERATION: return new StatementDefinitionContext<>(new EnumSpecificationImpl.Definition()); case TypeUtils.LEAF_REF: return new StatementDefinitionContext<>(new LeafrefSpecificationImpl.Definition()); case TypeUtils.BITS: return new StatementDefinitionContext<>(new BitsSpecificationImpl.Definition()); case TypeUtils.IDENTITY_REF: return new StatementDefinitionContext<>(new IdentityRefSpecificationImpl.Definition()); case TypeUtils.INSTANCE_IDENTIFIER: return new StatementDefinitionContext<>(new InstanceIdentifierSpecificationImpl.Definition()); default: return null; } } private PrefixToModule prefixes() { final Map allPrefixes = getRoot().getAllFromNamespace(ImpPrefixToModuleIdentifier .class); final Map belongsToPrefixes = getRoot().getAllFromNamespace (BelongsToPrefixToModuleIdentifier.class); if (belongsToPrefixes != null) allPrefixes.putAll(belongsToPrefixes); for (Entry stringModuleIdentifierEntry : allPrefixes.entrySet()) { final QNameModule namespace = getRoot().getFromNamespace(ModuleIdentifierToModuleQName.class, stringModuleIdentifierEntry.getValue()); prefixToModuleMap.put(stringModuleIdentifierEntry.getKey(), namespace); } 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()); } //extensions added if (inProgressPhase.equals(ModelProcessingPhase.FULL_DECLARATION)) { Map>> extensions = currentContext .getAllFromNamespace(ExtensionNamespace.class); if (extensions != null) { for (Map.Entry>> extension : extensions .entrySet()) { qNameToStmtDefMap .put((extension.getKey()), (StatementDefinition) ((StatementContextBase) extension .getValue()).definition() .getFactory()); } } } return qNameToStmtDefMap; } }