/*
* 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 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;
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{
void 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 final A argument;
private LinkedHashMap > 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;
StatementContextBase(@Nonnull ContextBuilder builder) throws SourceException {
this.definition = builder.getDefinition();
this.identifier = builder.getIdentifier();
this.statementDeclSource = builder.getStamementSource();
this.argument = definition.parseArgumentValue(this, this.rawStatementArgument());
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 A getStatementArgument() {
return argument;
}
@Override
public Collection extends StmtContext, ?, ?>> declaredSubstatements() {
return Collections.unmodifiableCollection(declared);
}
@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.StatementLocal;
}
@Override
public D buildDeclared() {
Preconditions.checkArgument(completedPhase == ModelProcessingPhase.FullDeclaration || completedPhase == ModelProcessingPhase.EffectiveModel);
if (declaredInstance == null) {
declaredInstance = definition().getFactory().createDeclared(this);
}
return declaredInstance;
}
@Override
public E buildEffective() {
Preconditions.checkArgument(completedPhase == ModelProcessingPhase.EffectiveModel);
if (effectiveInstance == null) {
effectiveInstance = definition().getFactory().createEffective(this);
}
return effectiveInstance;
}
void resetLists() {
declared.clear();
}
boolean tryToCompletePhase(ModelProcessingPhase phase) throws SourceException {
if(phase.equals(completedPhase)) {
return true;
}
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);
}
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()) {
listener.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 namepsace, KT key, StmtContext, ?, ?> stmt) {
addContextToNamespace(namepsace,(K) key, stmt);
}
}