package org.dcache.services.info.base; import java.util.Map; /** * The MalleableStateTransition extends StateTransition by providing some * additional methods for establishing StateTransition without first * establishing a given state and walking that state with a StateUpdate. * <p> * It is intended to be used for unit testing. */ public class MalleableStateTransition extends StateTransition { /** * Update a MalleableStateTransition so that a single metric is added. * This routine may be called multiple times to configure the * StateTransition so multiple new metrics may be added. * <p> * The method takes a parameter {@code existing}. This is the number of * path elements that already exist in the state when processing this new * metric. For example, when adding two metrics with some common parent * to an empty state the first call has {@code existing} of zero and the * second has the length of the common parent path. The following code * illustrates this. * * <pre> * StatePath commonPath = StatePath.parsePath( "aa.bb"); * StatePath path1 = commonPath.newChildPath( "metric1"); * StatePath path2 = commonPath.newChildPath( "metric2"); * // ...etc.. * transition.updateTransitionForNewMetric( path1, metric1, 0); * transition.updateTransitionForNewMetric( path2, metric2, * commonPath._elements.size()); * </pre> * * This method may be used for updating a metric by specifying an {@code * existing} value equal to the metric's StatePath length. * * @param path * the StatePath of the metric * @param metricValue * the StateValue of the new metric * @param existing * number of path elements that already exist in the state */ public void updateTransitionForNewMetric( StatePath path, StateValue metricValue, int existing) { StatePath parentPath = path.parentPath(); updateTransitionForNewBranch( parentPath, existing); /** * Since updateTransitionForNewBranch won't iterate down beyond the * last element, we must add the final iteration ourselves. */ if( parentPath != null && !parentPath.isSimplePath()) { getStateChangeSet(parentPath.parentPath()).recordChildItr( parentPath.getLastElement()); } String metricName = path.getLastElement(); StateChangeSet scs = getOrCreateChangeSet( parentPath); if( existing < path._elements.size()) { scs.recordNewChild(metricName, metricValue); } else { scs.recordUpdatedChild(metricName, metricValue); } } /** * Update the StateTransition so a new branch is created. * * @param path * the StatePath of the branch * @param existing * number of path elements already existing. * @see #updateTransitionForNewMetric(StatePath, StateValue, int) */ public void updateTransitionForNewBranch( StatePath path, int existing) { StatePath currentPath = null; int remainingExistingPathElements = existing; for( StatePath remainingPath = path; remainingPath != null; remainingPath = remainingPath.childPath()) { String childName = remainingPath.getFirstElement(); StateChangeSet scs = getOrCreateChangeSet( currentPath); if( remainingExistingPathElements == 0 && !scs.childIsNew( childName)) { scs.recordNewChild(childName, new StateComposite()); } else { remainingExistingPathElements--; } if( !remainingPath.isSimplePath()) { scs.recordChildItr(childName); } currentPath = currentPath == null ? new StatePath( childName) : currentPath.newChild( childName); } } public void updateTransitionChangingMetricToBranch( StatePath path, int existing) { updateTransitionForNewBranch( path, existing); StatePath parentPath = path.parentPath(); StateChangeSet scs = getOrCreateChangeSet( parentPath); String metricName = path.getLastElement(); scs.recordUpdatedChild( metricName, new StateComposite()); } /** * Update a StateTransition so the metric at StatePath is to be removed. * * @param path * the StatePath for the metric to be removed. */ public void updateTransitionForRemovingElement( StatePath path) { StatePath currentPath = null; for( StatePath remainingPath = path; remainingPath != null; remainingPath = remainingPath.childPath()) { String childName = remainingPath.getFirstElement(); StateChangeSet scs = getOrCreateChangeSet( currentPath); if( remainingPath.isSimplePath()) { scs.recordRemovedChild(childName); } else { scs.recordChildItr(childName); } currentPath = currentPath == null ? new StatePath( childName) : currentPath.newChild( childName); } } /** * Provide a cryptic but human-readable String describing the * MalleableStateTransition. This is intended only for debugging * purposes. * * @return a description of this StateTransition. */ public String debugInfo() { StringBuilder sb = new StringBuilder(); sb.append( " == MalleableStateTransition ==\n"); for( Map.Entry<StatePath, StateChangeSet> entry : _allChanges.entrySet()) { StatePath path = entry.getKey(); sb.append(" [").append((path != null) ? path.toString() : "(root)") .append("]\n"); StateChangeSet scs = entry.getValue(); sb.append( scs.dumpContents()); sb.append( "\n"); } return sb.toString(); } }