package org.dcache.services.info.base;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* The PostTransitionStateExhibitor provides a StateExhibitor that allows a
* visitor (a class implementing StateVisitor) to visit what the state will be
* after a StateTransition has been applied.
*/
public class PostTransitionStateExhibitor implements StateExhibitor
{
private static final Logger LOGGER =
LoggerFactory.getLogger(PostTransitionStateExhibitor.class);
/**
* The PreTransitionVisitor class visits the current state on behalf of
* some other StateVisitor. The results are relayed to this other visitor
* after being adjusted to take into account the StateTransition.
*/
private class PreTransitionVisitor implements StateVisitor
{
private final StateVisitor _postTransitionVisitor;
private final Set<StatePath> _bannedSubtrees = new HashSet<>();
public PreTransitionVisitor(StateVisitor postTransitionVisitor)
{
_postTransitionVisitor = postTransitionVisitor;
}
@Override
public void visitBoolean(StatePath path, BooleanStateValue value)
{
if (isMetricUpdatedOrDeleted(path)) {
visitUpdatedOrDeletedMetric(path, value);
} else {
_postTransitionVisitor.visitBoolean(path, value);
}
}
@Override
public void visitFloatingPoint(StatePath path, FloatingPointStateValue value)
{
if (isMetricUpdatedOrDeleted(path)) {
visitUpdatedOrDeletedMetric(path, value);
} else {
_postTransitionVisitor.visitFloatingPoint(path, value);
}
}
@Override
public void visitInteger(StatePath path, IntegerStateValue value)
{
if (isMetricUpdatedOrDeleted(path)) {
visitUpdatedOrDeletedMetric(path, value);
} else {
_postTransitionVisitor.visitInteger(path, value);
}
}
@Override
public void visitString(StatePath path, StringStateValue value)
{
if (isMetricUpdatedOrDeleted(path)) {
visitUpdatedOrDeletedMetric(path, value);
} else {
_postTransitionVisitor.visitString(path, value);
}
}
private boolean isMetricUpdatedOrDeleted(StatePath metricPath)
{
StatePath parentPath = metricPath.parentPath();
String metricName = metricPath.getLastElement();
StateChangeSet scs = _transition.getStateChangeSet(parentPath);
boolean isRemoved = scs != null && scs.childIsRemoved(metricName);
boolean isUpdated = scs != null && scs.childIsUpdated(metricName);
return isRemoved || isUpdated || hasBannedParent(metricPath);
}
private void visitUpdatedOrDeletedMetric(StatePath path, StateValue value)
{
LOGGER.trace("path={} value={}", path, value);
StatePath parentPath = path.parentPath();
String name = path.getLastElement();
StateChangeSet scs = _transition.getStateChangeSet(parentPath);
if (scs != null && scs.childIsUpdated(name)) {
StateComponent updatedComponent =
scs.getUpdatedChildValue(name);
visitUpdatedMetricValue(path, updatedComponent);
} else {
// don't visit child as it is to be removed.
}
}
private void visitUpdatedMetricValue(StatePath path, StateComponent component)
{
LOGGER.trace("path={} component={}", path, component);
if (component instanceof StateComposite) {
// This is when a metric has become a branch.
component.acceptVisitor(path, this);
} else {
component.acceptVisitor(path, _postTransitionVisitor);
}
}
@Override
public void visitCompositePreDescend(StatePath path,
Map<String, String> metadata)
{
if (path == null) {
_postTransitionVisitor.visitCompositePreDescend(null, metadata);
return;
}
StatePath parentPath = path.parentPath();
String componentName = path.getLastElement();
StateChangeSet scs = _transition.getStateChangeSet(parentPath);
if (scs != null && scs.childIsRemoved(componentName)) {
banChildrenOf(path);
return;
}
if (scs != null && scs.childIsUpdated(componentName)) {
StateComponent updatedComponent =
scs.getUpdatedChildValue(componentName);
visitUpdatedStateComponentPreDescend(path, updatedComponent,
metadata);
} else {
_postTransitionVisitor.visitCompositePreDescend(path, metadata);
}
}
private void visitUpdatedStateComponentPreDescend(StatePath path,
StateComponent updatedComponent, Map<String, String> metadata)
{
if (updatedComponent instanceof StateComposite) {
_postTransitionVisitor.visitCompositePreDescend(path, metadata);
} else {
// This is a StateComposite that has become a metric.
updatedComponent.acceptVisitor(path, _postTransitionVisitor);
banChildrenOf(path);
}
}
private void banChildrenOf(StatePath parentPath)
{
_bannedSubtrees.add(parentPath);
}
private boolean hasBannedParent(StatePath path)
{
for (StatePath bannedParent : _bannedSubtrees) {
if (bannedParent.isParentOf(path)) {
return true;
}
}
return false;
}
@Override
public void visitCompositePostDescend(StatePath path,
Map<String, String> metadata)
{
if (!shouldPostVisitComposite(path)) {
return;
}
visitNewChildren(path);
_postTransitionVisitor.visitCompositePostDescend(path, metadata);
}
private boolean shouldPostVisitComposite(StatePath path)
{
if (path == null) {
return true;
}
StatePath parentPath = path.parentPath();
StateChangeSet parentScs =
_transition.getStateChangeSet(parentPath);
if (parentScs != null) {
String branchName = path.getLastElement();
if (parentScs.childIsRemoved(branchName)) {
return false;
}
if (parentScs.childIsUpdated(branchName)) {
StateComponent updatedComponent =
parentScs.getUpdatedChildValue(branchName);
if (updatedComponent instanceof StateValue) {
// This is when a StateComponent has turned into a metric
return false;
}
}
}
return true;
}
private void visitNewChildren(StatePath compositePath)
{
StateChangeSet thisBranchScs =
_transition.getStateChangeSet(compositePath);
if (thisBranchScs == null) {
return;
}
for (String newChildName : thisBranchScs.getNewChildren()) {
StatePath childPath;
if (compositePath != null) {
childPath = compositePath.newChild(newChildName);
} else {
childPath = new StatePath(newChildName);
}
StateComponent newComponent =
thisBranchScs.getNewChildValue(newChildName);
visitNewChild(childPath, newComponent);
}
}
private void visitNewChild(StatePath path, StateComponent component)
{
LOGGER.trace("Visiting new child: {}", path);
if (component instanceof StateComposite) {
component.acceptVisitor(path, this);
} else {
component.acceptVisitor(path, _postTransitionVisitor);
}
}
@Override
public boolean isVisitable(StatePath path)
{
if (hasBannedParent(path)) {
return false;
}
return _postTransitionVisitor.isVisitable(path);
}
}
private final StateTransition _transition;
private final StateExhibitor _currentStateExhibitor;
public PostTransitionStateExhibitor(StateExhibitor exhibitor,
StateTransition transition)
{
_currentStateExhibitor = exhibitor;
_transition = transition;
}
@Override
public void visitState(StateVisitor visitor)
{
StateVisitor preTransitionVisitor = new PreTransitionVisitor(visitor);
_currentStateExhibitor.visitState(preTransitionVisitor);
}
}