blob: 8b2e3205a985235bd8746591e26000a1865632b2 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of 'inspect.dart';
/// A node in the [Inspect] tree that can have associated key-values (KVs).
class Node {
/// The VMO index of this node.
/// @nodoc
@visibleForTesting
final int index;
/// The writer for the underlying VMO.
///
/// Will be set to null if the Metric has been deleted or could not be
/// created in the VMO.
/// If so, all actions on this Metric should be no-ops and not throw.
VmoWriter _writer;
final _properties = <String, _Property>{};
final _metrics = <String, _Metric>{};
final _children = <String, Node>{};
/// Creates a [Node] with [name] under the [parentIndex].
///
/// Private as an implementation detail to code that understands VMO indices.
/// Client code that wishes to create [Node]s should use [createChild].
Node._(String name, int parentIndex, this._writer)
: index = _writer.createNode(parentIndex, name) {
if (index == invalidIndex) {
_writer = null;
}
}
/// Wraps the special root node.
Node._root(this._writer) : index = _writer.rootNode;
/// Creates a Node that never does anything.
///
/// These are returned when calling createChild on a deleted [Node].
Node._deleted()
: _writer = null,
index = invalidIndex;
bool get _isDeleted => _writer == null;
/// Creates a child [Node] with [name].
///
/// If a child with [name] already exists, this
/// method returns it. Otherwise, it creates a new [Node].
Node createChild(String name) {
if (_writer == null) {
return Node._deleted();
}
// TODO(cphoenix): Tell parents when deleted, instead of asking children.
// Asking children allows the map to grow without limit as children are
// created and deleted (e.g. ring buffer).
if (_children.containsKey(name) && !_children[name]._isDeleted) {
return _children[name];
}
return _children[name] = Node._(name, index, _writer);
}
/// Delete this node and any children from underlying storage.
///
/// After a node has been deleted, all calls on it and its children will have
/// no effect, but will not result in an error.
void delete() {
if (_writer == null) {
return;
}
_properties
..forEach((_, property) => property.delete())
..clear();
_metrics
..forEach((_, metric) => metric.delete())
..clear();
_children
..forEach((_, node) => node.delete())
..clear();
_writer.deleteNode(index);
_writer = null;
}
/// Creates a [StringProperty] with [name] on this node.
///
/// If a [StringProperty] with [name] already exists and is not deleted,
/// this method returns it.
///
/// Otherwise, it creates a new property initialized to the empty string.
///
/// Throws [StateError] if a non-deleted property with [name] already exists
/// but it is not a [StringProperty].
StringProperty createStringProperty(String name) {
if (_writer == null) {
return StringProperty._deleted();
}
if (_properties.containsKey(name) && !_properties[name]._isDeleted) {
if (_properties[name] is! StringProperty) {
throw InspectStateError("Can't create StringProperty named $name;"
' a different type exists.');
}
return _properties[name];
}
return _properties[name] = StringProperty._(name, index, _writer);
}
/// Creates a [ByteDataProperty] with [name] on this node.
///
/// If a [ByteDataProperty] with [name] already exists and is not deleted,
/// this method returns it.
///
/// Otherwise, it creates a new property initialized to the empty
/// byte data container.
///
/// Throws [StateError] if a non-deleted property with [name] already exists
/// but it is not a [ByteDataProperty].
ByteDataProperty createByteDataProperty(String name) {
if (_writer == null) {
return ByteDataProperty._deleted();
}
if (_properties.containsKey(name) && !_properties[name]._isDeleted) {
if (_properties[name] is! ByteDataProperty) {
throw InspectStateError("Can't create ByteDataProperty named $name;"
' a different type exists.');
}
return _properties[name];
}
return _properties[name] = ByteDataProperty._(name, index, _writer);
}
/// Creates an [IntMetric] with [name] on this node.
///
/// If an [IntMetric] with [name] already exists and is not
/// deleted, this method returns it.
///
/// Otherwise, it creates a new metric initialized to 0.
///
/// Throws [StateError] if a non-deleted metric with [name]
/// already exists but it is not an [IntMetric].
IntMetric createIntMetric(String name) {
if (_writer == null) {
return IntMetric._deleted();
}
if (_metrics.containsKey(name) && !_metrics[name]._isDeleted) {
if (_metrics[name] is! IntMetric) {
throw InspectStateError(
"Can't create IntMetric named $name; a different type exists.");
}
return _metrics[name];
}
return _metrics[name] = IntMetric._(name, index, _writer);
}
/// Creates a [DoubleMetric] with [name] on this node.
///
/// If a [DoubleMetric] with [name] already exists and is not
/// deleted, this method returns it.
///
/// Otherwise, it creates a new metric initialized to 0.0.
///
/// Throws [StateError] if a non-deleted metric with [name]
/// already exists but it is not a [DoubleMetric].
DoubleMetric createDoubleMetric(String name) {
if (_writer == null) {
return DoubleMetric._deleted();
}
if (_metrics.containsKey(name) && !_metrics[name]._isDeleted) {
if (_metrics[name] is! DoubleMetric) {
throw InspectStateError("Can't create DoubleMetric named $name;"
' a different type exists.');
}
return _metrics[name];
}
return _metrics[name] = DoubleMetric._(name, index, _writer);
}
}
/// RootNode wraps the root node of the VMO.
///
/// The root node has special behavior: Delete is a NOP.
///
/// This class should be hidden from the public API.
class RootNode extends Node {
/// Creates a Node wrapping the root of the Inspect hierarchy.
RootNode(VmoWriter writer) : super._root(writer);
/// Deletes of the root are NOPs.
@override
void delete() {}
}