/*
* 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 extends IdentifierNamespace, ?>> 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);
}
}