/* * 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.base.Throwables; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.EventListener; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import javax.annotation.Nonnull; import org.opendaylight.yangtools.concepts.Identifiable; 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.meta.StatementSource; 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.StorageNodeType; import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace; import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext; import org.opendaylight.yangtools.yang.parser.spi.source.SourceException; import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference; import org.opendaylight.yangtools.yang.parser.stmt.reactor.NamespaceBehaviourWithListeners.ValueAddedListener; public abstract class StatementContextBase, E extends EffectiveStatement> extends NamespaceStorageSupport implements StmtContext.Mutable, Identifiable { interface OnNamespaceItemAdded extends EventListener { void namespaceItemAdded(StatementContextBase context, Class namespace, Object key, Object value) throws SourceException; } interface OnPhaseFinished extends EventListener { boolean phaseFinished(StatementContextBase context, ModelProcessingPhase phase) throws SourceException; } interface ContextMutation { boolean isFinished(); } abstract static class ContextBuilder, E extends EffectiveStatement> { private final StatementDefinitionContext definition; private final StatementSourceReference stmtRef; private String rawArg; private StatementSourceReference argRef; public ContextBuilder(StatementDefinitionContext def, StatementSourceReference sourceRef) { this.definition = def; this.stmtRef = sourceRef; } public void setArgument(@Nonnull String argument, @Nonnull StatementSourceReference argumentSource) { Preconditions.checkArgument(definition.hasArgument(), "Statement does not take argument."); this.rawArg = Preconditions.checkNotNull(argument); this.argRef = Preconditions.checkNotNull(argumentSource); } public String getRawArgument() { return rawArg; } public StatementSourceReference getStamementSource() { return stmtRef; } public StatementSourceReference getArgumentSource() { return argRef; } public StatementDefinitionContext getDefinition() { return definition; } public StatementIdentifier getIdentifier() { return new StatementIdentifier(definition.getStatementName(), rawArg); } public abstract StatementContextBase build() throws SourceException; } private final StatementDefinitionContext definition; private final StatementIdentifier identifier; private final StatementSourceReference statementDeclSource; private Map> substatements = new LinkedHashMap<>(); private Collection> declared = new ArrayList<>(); private Collection> effective = new ArrayList<>(); private ModelProcessingPhase completedPhase; private Multimap phaseListeners = HashMultimap .create(); private Multimap phaseMutation = HashMultimap .create(); private D declaredInstance; private E effectiveInstance; private StatementContextBase originalCtx; private TypeOfCopy typeOfCopy = TypeOfCopy.ORIGINAL; @Override public TypeOfCopy getTypeOfCopy() { return typeOfCopy; } @Override public void setTypeOfCopy(TypeOfCopy typeOfCopy) { this.typeOfCopy = typeOfCopy; } @Override public StatementContextBase getOriginalCtx() { return originalCtx; } @Override public void setOriginalCtx(StatementContextBase originalCtx) { this.originalCtx = originalCtx; } @Override public ModelProcessingPhase getCompletedPhase() { return completedPhase; } @Override public void setCompletedPhase(ModelProcessingPhase completedPhase) { this.completedPhase = completedPhase; } StatementContextBase(@Nonnull ContextBuilder builder) throws SourceException { this.definition = builder.getDefinition(); this.identifier = builder.getIdentifier(); this.statementDeclSource = builder.getStamementSource(); this.completedPhase = null; } StatementContextBase(StatementContextBase original) { this.definition = original.definition; this.identifier = original.identifier; this.statementDeclSource = original.statementDeclSource; this.completedPhase = null; } @Override public abstract StatementContextBase getParentContext(); @Override public abstract RootStatementContext getRoot(); @Override public StatementIdentifier getIdentifier() { return identifier; } @Override public StatementSource getStatementSource() { return statementDeclSource.getStatementSource(); } @Override public StatementSourceReference getStatementSourceReference() { return statementDeclSource; } @Override public String rawStatementArgument() { return identifier.getArgument(); } @Override public Collection> declaredSubstatements() { return Collections.unmodifiableCollection(declared); } @Override public Collection> effectiveSubstatements() { return Collections.unmodifiableCollection(effective); } public void addEffectiveSubstatement( StatementContextBase substatement) { effective.add(substatement); } public void addDeclaredSubstatement( StatementContextBase substatement) { declared.add(substatement); } @SuppressWarnings({ "rawtypes", "unchecked" }) public ContextBuilder substatementBuilder( StatementDefinitionContext def, StatementSourceReference ref) { return new ContextBuilder(def, ref) { @Override public StatementContextBase build() throws SourceException { StatementContextBase potential = substatements .get(getIdentifier()); if (potential == null) { potential = new SubstatementContext( StatementContextBase.this, this); substatements.put(getIdentifier(), potential); } potential.resetLists(); switch (this.getStamementSource().getStatementSource()) { case DECLARATION: declared.add(potential); break; case CONTEXT: effective.add(potential); break; } return potential; } }; } @Override public StorageNodeType getStorageNodeType() { return StorageNodeType.STATEMENT_LOCAL; } @Override public D buildDeclared() { Preconditions .checkArgument(completedPhase == ModelProcessingPhase.FULL_DECLARATION || completedPhase == ModelProcessingPhase.EFFECTIVE_MODEL); if (declaredInstance == null) { declaredInstance = definition().getFactory().createDeclared(this); } return declaredInstance; } @Override public E buildEffective() { if (effectiveInstance == null) { effectiveInstance = definition().getFactory().createEffective(this); } return effectiveInstance; } void resetLists() { declared.clear(); } boolean tryToCompletePhase(ModelProcessingPhase phase) throws SourceException { Iterator openMutations = phaseMutation.get(phase) .iterator(); boolean finished = true; while (openMutations.hasNext()) { ContextMutation current = openMutations.next(); if (current.isFinished()) { openMutations.remove(); } else { finished = false; } } for (StatementContextBase child : declared) { finished &= child.tryToCompletePhase(phase); } for (StatementContextBase child : effective) { finished &= child.tryToCompletePhase(phase); } if (finished) { onPhaseCompleted(phase); return true; } return false; } private void onPhaseCompleted(ModelProcessingPhase phase) throws SourceException { completedPhase = phase; Iterator listener = phaseListeners.get(completedPhase) .iterator(); while (listener.hasNext()) { OnPhaseFinished next = listener.next(); if(next.phaseFinished(this, phase)) { listener.remove(); } } } /** * * Ends declared section of current node. * * @param ref * @throws SourceException * */ void endDeclared(StatementSourceReference ref, ModelProcessingPhase phase) throws SourceException { definition().onDeclarationFinished(this, phase); } protected final StatementDefinitionContext definition() { return definition; } @Override protected void checkLocalNamespaceAllowed( Class> type) { definition().checkNamespaceAllowed(type); } @Override protected > void onNamespaceElementAdded( Class type, K key, V value) { // definition().onNamespaceElementAdded(this, type, key, value); } > void onNamespaceItemAddedAction( final Class type, K key, final OnNamespaceItemAdded listener) throws SourceException { Object potential = getFromNamespace(type, key); if (potential != null) { listener.namespaceItemAdded(this, type, key, potential); return; } NamespaceBehaviour behaviour = getBehaviourRegistry() .getNamespaceBehaviour(type); if (behaviour instanceof NamespaceBehaviourWithListeners) { NamespaceBehaviourWithListeners casted = (NamespaceBehaviourWithListeners) behaviour; casted.addValueListener(key, new ValueAddedListener(this) { @Override void onValueAdded(Object key, Object value) { try { listener.namespaceItemAdded(StatementContextBase.this, type, key, value); } catch (SourceException e) { throw Throwables.propagate(e); } } }); } } @Override public StatementDefinition getPublicDefinition() { return definition().getPublicView(); } @Override public ModelActionBuilder newInferenceAction(ModelProcessingPhase phase) { return getRoot().getSourceContext().newInferenceAction(phase); } void addPhaseCompletedListener(ModelProcessingPhase phase, OnPhaseFinished listener) throws SourceException { ModelProcessingPhase finishedPhase = completedPhase; while (finishedPhase != null) { if (phase.equals(finishedPhase)) { listener.phaseFinished(this, finishedPhase); return; } finishedPhase = finishedPhase.getPreviousPhase(); } phaseListeners.put(phase, listener); } void addMutation(ModelProcessingPhase phase, ContextMutation mutation) { ModelProcessingPhase finishedPhase = completedPhase; while (finishedPhase != null) { if (phase.equals(finishedPhase)) { throw new IllegalStateException( "Mutation registered after phase was completed."); } finishedPhase = finishedPhase.getPreviousPhase(); } phaseMutation.put(phase, mutation); } @Override public > void addContext( Class namespace, KT key, StmtContext stmt) { addContextToNamespace(namespace, (K) key, stmt); } }