您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
1598 行
51 KiB
1598 行
51 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using Unity.UIWidgets.ui;
|
|
|
|
namespace Unity.UIWidgets.foundation {
|
|
public enum DiagnosticLevel {
|
|
hidden,
|
|
fine,
|
|
debug,
|
|
info,
|
|
warning,
|
|
error,
|
|
off,
|
|
}
|
|
|
|
public enum DiagnosticsTreeStyle {
|
|
sparse,
|
|
offstage,
|
|
dense,
|
|
transition,
|
|
whitespace,
|
|
singleLine,
|
|
errorProperty,
|
|
}
|
|
|
|
public class TextTreeConfiguration {
|
|
public TextTreeConfiguration(
|
|
string prefixLineOne = null,
|
|
string prefixOtherLines = null,
|
|
string prefixLastChildLineOne = null,
|
|
string prefixOtherLinesRootNode = null,
|
|
string linkCharacter = null,
|
|
string propertyPrefixIfChildren = null,
|
|
string propertyPrefixNoChildren = null,
|
|
string lineBreak = "\n",
|
|
bool lineBreakProperties = true,
|
|
string afterName = ":",
|
|
string afterDescriptionIfBody = "",
|
|
string beforeProperties = "",
|
|
string afterProperties = "",
|
|
string propertySeparator = "",
|
|
string bodyIndent = "",
|
|
string footer = "",
|
|
bool showChildren = true,
|
|
bool addBlankLineIfNoChildren = true,
|
|
bool isNameOnOwnLine = false,
|
|
bool isBlankLineBetweenPropertiesAndChildren = true
|
|
) {
|
|
D.assert(prefixLineOne != null);
|
|
D.assert(prefixOtherLines != null);
|
|
D.assert(prefixLastChildLineOne != null);
|
|
D.assert(prefixOtherLinesRootNode != null);
|
|
D.assert(linkCharacter != null);
|
|
D.assert(propertyPrefixIfChildren != null);
|
|
D.assert(propertyPrefixNoChildren != null);
|
|
D.assert(lineBreak != null);
|
|
D.assert(afterName != null);
|
|
D.assert(afterDescriptionIfBody != null);
|
|
D.assert(beforeProperties != null);
|
|
D.assert(afterProperties != null);
|
|
D.assert(propertySeparator != null);
|
|
D.assert(bodyIndent != null);
|
|
D.assert(footer != null);
|
|
|
|
this.prefixLineOne = prefixLineOne;
|
|
this.prefixOtherLines = prefixOtherLines;
|
|
this.prefixLastChildLineOne = prefixLastChildLineOne;
|
|
this.prefixOtherLinesRootNode = prefixOtherLinesRootNode;
|
|
this.propertyPrefixIfChildren = propertyPrefixIfChildren;
|
|
this.propertyPrefixNoChildren = propertyPrefixNoChildren;
|
|
this.linkCharacter = linkCharacter;
|
|
childLinkSpace = new string(' ', linkCharacter.Length);
|
|
this.lineBreak = lineBreak;
|
|
this.lineBreakProperties = lineBreakProperties;
|
|
this.afterName = afterName;
|
|
this.afterDescriptionIfBody = afterDescriptionIfBody;
|
|
this.beforeProperties = beforeProperties;
|
|
this.afterProperties = afterProperties;
|
|
this.propertySeparator = propertySeparator;
|
|
this.bodyIndent = bodyIndent;
|
|
this.showChildren = showChildren;
|
|
this.addBlankLineIfNoChildren = addBlankLineIfNoChildren;
|
|
this.isNameOnOwnLine = isNameOnOwnLine;
|
|
this.footer = footer;
|
|
this.isBlankLineBetweenPropertiesAndChildren = isBlankLineBetweenPropertiesAndChildren;
|
|
}
|
|
|
|
public readonly string prefixLineOne;
|
|
|
|
public readonly string prefixOtherLines;
|
|
|
|
public readonly string prefixLastChildLineOne;
|
|
|
|
public readonly string prefixOtherLinesRootNode;
|
|
|
|
public readonly string propertyPrefixIfChildren;
|
|
|
|
public readonly string propertyPrefixNoChildren;
|
|
|
|
public readonly string linkCharacter;
|
|
|
|
public readonly string childLinkSpace;
|
|
|
|
public readonly string lineBreak;
|
|
|
|
public readonly bool lineBreakProperties;
|
|
|
|
public readonly string afterName;
|
|
|
|
public readonly string afterDescriptionIfBody;
|
|
|
|
public readonly string beforeProperties;
|
|
|
|
public readonly string afterProperties;
|
|
|
|
public readonly string propertySeparator;
|
|
|
|
public readonly string bodyIndent;
|
|
|
|
public readonly bool showChildren;
|
|
|
|
public readonly bool addBlankLineIfNoChildren;
|
|
|
|
public readonly bool isNameOnOwnLine;
|
|
|
|
public readonly string footer;
|
|
|
|
public readonly bool isBlankLineBetweenPropertiesAndChildren;
|
|
}
|
|
|
|
|
|
public static partial class foundation_ {
|
|
public static readonly TextTreeConfiguration sparseTextConfiguration = new TextTreeConfiguration(
|
|
prefixLineOne: "├─",
|
|
prefixOtherLines: " ",
|
|
prefixLastChildLineOne: "└─",
|
|
linkCharacter: "│",
|
|
propertyPrefixIfChildren: "│ ",
|
|
propertyPrefixNoChildren: " ",
|
|
prefixOtherLinesRootNode: " "
|
|
);
|
|
|
|
public static readonly TextTreeConfiguration dashedTextConfiguration = new TextTreeConfiguration(
|
|
prefixLineOne: "╎╌",
|
|
prefixLastChildLineOne: "└╌",
|
|
prefixOtherLines: " ",
|
|
linkCharacter: "╎",
|
|
propertyPrefixIfChildren: "│ ",
|
|
propertyPrefixNoChildren: " ",
|
|
prefixOtherLinesRootNode: " "
|
|
);
|
|
|
|
public static readonly TextTreeConfiguration denseTextConfiguration = new TextTreeConfiguration(
|
|
propertySeparator: ", ",
|
|
beforeProperties: "(",
|
|
afterProperties: ")",
|
|
lineBreakProperties: false,
|
|
prefixLineOne: "├",
|
|
prefixOtherLines: "",
|
|
prefixLastChildLineOne: "└",
|
|
linkCharacter: "│",
|
|
propertyPrefixIfChildren: "│",
|
|
propertyPrefixNoChildren: " ",
|
|
prefixOtherLinesRootNode: "",
|
|
addBlankLineIfNoChildren: false,
|
|
isBlankLineBetweenPropertiesAndChildren: false
|
|
);
|
|
|
|
public static readonly TextTreeConfiguration transitionTextConfiguration = new TextTreeConfiguration(
|
|
prefixLineOne: "╞═╦══ ",
|
|
prefixLastChildLineOne: "╘═╦══ ",
|
|
prefixOtherLines: " ║ ",
|
|
footer: " ╚═══════════\n",
|
|
linkCharacter: "│",
|
|
propertyPrefixIfChildren: "",
|
|
propertyPrefixNoChildren: "",
|
|
prefixOtherLinesRootNode: "",
|
|
afterName: " ═══",
|
|
afterDescriptionIfBody: ":",
|
|
bodyIndent: " ",
|
|
isNameOnOwnLine: true,
|
|
addBlankLineIfNoChildren: false,
|
|
isBlankLineBetweenPropertiesAndChildren: false
|
|
);
|
|
|
|
public static readonly TextTreeConfiguration whitespaceTextConfiguration = new TextTreeConfiguration(
|
|
prefixLineOne: "",
|
|
prefixLastChildLineOne: "",
|
|
prefixOtherLines: " ",
|
|
prefixOtherLinesRootNode: " ",
|
|
bodyIndent: "",
|
|
propertyPrefixIfChildren: "",
|
|
propertyPrefixNoChildren: "",
|
|
linkCharacter: " ",
|
|
addBlankLineIfNoChildren: false,
|
|
afterDescriptionIfBody: ":",
|
|
isBlankLineBetweenPropertiesAndChildren: false
|
|
);
|
|
|
|
public static readonly TextTreeConfiguration singleLineTextConfiguration = new TextTreeConfiguration(
|
|
propertySeparator: ", ",
|
|
beforeProperties: "(",
|
|
afterProperties: ")",
|
|
prefixLineOne: "",
|
|
prefixOtherLines: "",
|
|
prefixLastChildLineOne: "",
|
|
lineBreak: "",
|
|
lineBreakProperties: false,
|
|
addBlankLineIfNoChildren: false,
|
|
showChildren: false,
|
|
propertyPrefixIfChildren: "",
|
|
propertyPrefixNoChildren: "",
|
|
linkCharacter: "",
|
|
prefixOtherLinesRootNode: ""
|
|
);
|
|
}
|
|
class _PrefixedStringBuilder {
|
|
internal _PrefixedStringBuilder(string prefixLineOne, string prefixOtherLines) {
|
|
this.prefixLineOne = prefixLineOne;
|
|
this.prefixOtherLines = prefixOtherLines;
|
|
}
|
|
|
|
public readonly string prefixLineOne;
|
|
|
|
public string prefixOtherLines;
|
|
|
|
readonly StringBuilder _buffer = new StringBuilder();
|
|
bool _atLineStart = true;
|
|
bool _hasMultipleLines = false;
|
|
|
|
public bool hasMultipleLines {
|
|
get { return _hasMultipleLines; }
|
|
}
|
|
|
|
public void write(string s) {
|
|
if (s.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
if (s == "\n") {
|
|
if (_buffer.Length == 0) {
|
|
_buffer.Append(prefixLineOne.TrimEnd());
|
|
}
|
|
else if (_atLineStart) {
|
|
_buffer.Append(prefixOtherLines.TrimEnd());
|
|
_hasMultipleLines = true;
|
|
}
|
|
|
|
_buffer.Append("\n");
|
|
_atLineStart = true;
|
|
return;
|
|
}
|
|
|
|
if (_buffer.Length == 0) {
|
|
_buffer.Append(prefixLineOne);
|
|
}
|
|
else if (_atLineStart) {
|
|
_buffer.Append(prefixOtherLines);
|
|
_hasMultipleLines = true;
|
|
}
|
|
|
|
bool lineTerminated = false;
|
|
|
|
if (s.EndsWith("\n")) {
|
|
s = s.Substring(0, s.Length - 1);
|
|
lineTerminated = true;
|
|
}
|
|
|
|
var parts = s.Split('\n');
|
|
_buffer.Append(parts[0]);
|
|
for (int i = 1; i < parts.Length; ++i) {
|
|
_buffer.Append("\n")
|
|
.Append(prefixOtherLines)
|
|
.Append(parts[i]);
|
|
}
|
|
|
|
if (lineTerminated) {
|
|
_buffer.Append("\n");
|
|
}
|
|
|
|
_atLineStart = lineTerminated;
|
|
}
|
|
|
|
public void writeRaw(string text) {
|
|
if (text.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
_buffer.Append(text);
|
|
_atLineStart = text.EndsWith("\n");
|
|
}
|
|
|
|
|
|
public void writeRawLine(string line) {
|
|
if (line.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
_buffer.Append(line);
|
|
if (!line.EndsWith("\n")) {
|
|
_buffer.Append('\n');
|
|
}
|
|
|
|
_atLineStart = true;
|
|
}
|
|
|
|
public override string ToString() {
|
|
return _buffer.ToString();
|
|
}
|
|
}
|
|
|
|
class _NoDefaultValue {
|
|
internal _NoDefaultValue() {
|
|
}
|
|
}
|
|
|
|
class _NullDefaultValue {
|
|
internal _NullDefaultValue() {
|
|
}
|
|
}
|
|
|
|
public static partial class foundation_ {
|
|
|
|
public static readonly object kNoDefaultValue = new _NoDefaultValue();
|
|
|
|
public static readonly object kNullDefaultValue = new _NullDefaultValue();
|
|
}
|
|
|
|
|
|
public abstract class DiagnosticsNode {
|
|
protected DiagnosticsNode(
|
|
string name = null,
|
|
DiagnosticsTreeStyle? style = null,
|
|
bool showName = true,
|
|
bool showSeparator = true
|
|
) {
|
|
D.assert(name == null || !name.EndsWith(":"),
|
|
() => "Names of diagnostic nodes must not end with colons.");
|
|
this.name = name;
|
|
_style = style;
|
|
_showName = showName;
|
|
this.showSeparator = showSeparator;
|
|
}
|
|
|
|
public static DiagnosticsNode message(
|
|
string message,
|
|
DiagnosticsTreeStyle style = DiagnosticsTreeStyle.singleLine,
|
|
DiagnosticLevel level = DiagnosticLevel.info
|
|
) {
|
|
return new DiagnosticsProperty<object>(
|
|
"",
|
|
null,
|
|
description: message,
|
|
style: style,
|
|
showName: false,
|
|
level: level
|
|
);
|
|
}
|
|
|
|
public readonly string name;
|
|
|
|
public abstract string toDescription(
|
|
TextTreeConfiguration parentConfiguration = null
|
|
);
|
|
|
|
public readonly bool showSeparator;
|
|
|
|
public bool isFiltered(DiagnosticLevel minLevel) {
|
|
return level < minLevel;
|
|
}
|
|
|
|
public virtual DiagnosticLevel level {
|
|
get { return DiagnosticLevel.info; }
|
|
}
|
|
|
|
public virtual bool showName {
|
|
get { return _showName; }
|
|
}
|
|
|
|
readonly bool _showName;
|
|
|
|
public virtual string emptyBodyDescription {
|
|
get { return null; }
|
|
}
|
|
|
|
public abstract object valueObject { get; }
|
|
|
|
public virtual DiagnosticsTreeStyle? style {
|
|
get { return _style; }
|
|
}
|
|
|
|
readonly DiagnosticsTreeStyle? _style;
|
|
|
|
public abstract List<DiagnosticsNode> getProperties();
|
|
|
|
public abstract List<DiagnosticsNode> getChildren();
|
|
|
|
string _separator {
|
|
get { return showSeparator ? ":" : ""; }
|
|
}
|
|
|
|
public virtual Dictionary<string, object> toJsonMap() {
|
|
var data = new Dictionary<string, object> {
|
|
{"name", name},
|
|
{"showSeparator", showSeparator},
|
|
{"description", toDescription()},
|
|
{"level", Convert.ToString(level)},
|
|
{"showName", showName},
|
|
{"emptyBodyDescription", emptyBodyDescription},
|
|
{"style", Convert.ToString(style)},
|
|
{"valueToString", Convert.ToString(valueObject)},
|
|
{"type", GetType().ToString()},
|
|
{"hasChildren", getChildren().isNotEmpty()}
|
|
};
|
|
return data;
|
|
}
|
|
|
|
public override string ToString() {
|
|
return toString();
|
|
}
|
|
|
|
public virtual string toString(
|
|
TextTreeConfiguration parentConfiguration = null,
|
|
DiagnosticLevel minLevel = DiagnosticLevel.info
|
|
) {
|
|
D.assert(style != null);
|
|
if (style == DiagnosticsTreeStyle.singleLine) {
|
|
return toStringDeep(parentConfiguration: parentConfiguration, minLevel: minLevel);
|
|
}
|
|
|
|
var description = toDescription(parentConfiguration: parentConfiguration);
|
|
if (name.isEmpty() || !showName) {
|
|
return description;
|
|
}
|
|
|
|
return description.Contains("\n")
|
|
? name + _separator + "\n" + description
|
|
: name + _separator + description;
|
|
}
|
|
|
|
protected TextTreeConfiguration textTreeConfiguration {
|
|
get {
|
|
D.assert(style != null);
|
|
switch (style) {
|
|
case DiagnosticsTreeStyle.dense:
|
|
return foundation_.denseTextConfiguration;
|
|
case DiagnosticsTreeStyle.sparse:
|
|
return foundation_.sparseTextConfiguration;
|
|
case DiagnosticsTreeStyle.offstage:
|
|
return foundation_.dashedTextConfiguration;
|
|
case DiagnosticsTreeStyle.whitespace:
|
|
return foundation_.whitespaceTextConfiguration;
|
|
case DiagnosticsTreeStyle.transition:
|
|
return foundation_.transitionTextConfiguration;
|
|
case DiagnosticsTreeStyle.singleLine:
|
|
return foundation_.singleLineTextConfiguration;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
TextTreeConfiguration _childTextConfiguration(
|
|
DiagnosticsNode child,
|
|
TextTreeConfiguration textStyle
|
|
) {
|
|
return child != null && child.style != DiagnosticsTreeStyle.singleLine
|
|
? child.textTreeConfiguration
|
|
: textStyle;
|
|
}
|
|
|
|
public string toStringDeep(
|
|
string prefixLineOne = "",
|
|
string prefixOtherLines = null,
|
|
TextTreeConfiguration parentConfiguration = null,
|
|
DiagnosticLevel minLevel = DiagnosticLevel.debug
|
|
) {
|
|
prefixOtherLines = prefixOtherLines ?? prefixLineOne;
|
|
|
|
var children = getChildren();
|
|
var config = textTreeConfiguration;
|
|
if (prefixOtherLines.isEmpty()) {
|
|
prefixOtherLines += config.prefixOtherLinesRootNode;
|
|
}
|
|
|
|
var builder = new _PrefixedStringBuilder(
|
|
prefixLineOne,
|
|
prefixOtherLines
|
|
);
|
|
|
|
var description = toDescription(parentConfiguration: parentConfiguration);
|
|
if (description.isEmpty()) {
|
|
if (name.isNotEmpty() && showName) {
|
|
builder.write(name);
|
|
}
|
|
}
|
|
else {
|
|
if (name.isNotEmpty() && showName) {
|
|
builder.write(name);
|
|
if (showSeparator) {
|
|
builder.write(config.afterName);
|
|
}
|
|
|
|
builder.write(
|
|
config.isNameOnOwnLine || description.Contains("\n") ? "\n" : "");
|
|
if (description.Contains("\n") && style == DiagnosticsTreeStyle.singleLine) {
|
|
builder.prefixOtherLines += " ";
|
|
}
|
|
}
|
|
|
|
builder.prefixOtherLines +=
|
|
children.isEmpty() ? config.propertyPrefixNoChildren : config.propertyPrefixIfChildren;
|
|
builder.write(description);
|
|
}
|
|
|
|
var properties = getProperties().Where(n => !n.isFiltered(minLevel)).ToList();
|
|
|
|
if (properties.isNotEmpty() || children.isNotEmpty() || emptyBodyDescription != null) {
|
|
builder.write(config.afterDescriptionIfBody);
|
|
}
|
|
|
|
if (config.lineBreakProperties) {
|
|
builder.write(config.lineBreak);
|
|
}
|
|
|
|
if (properties.isNotEmpty()) {
|
|
builder.write(config.beforeProperties);
|
|
}
|
|
|
|
builder.prefixOtherLines += config.bodyIndent;
|
|
if (emptyBodyDescription != null &&
|
|
properties.isEmpty() &&
|
|
children.isEmpty() &&
|
|
prefixLineOne.isNotEmpty()) {
|
|
builder.write(emptyBodyDescription);
|
|
if (config.lineBreakProperties) {
|
|
builder.write(config.lineBreak);
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < properties.Count; ++i) {
|
|
DiagnosticsNode property = properties[i];
|
|
if (i > 0) {
|
|
builder.write(config.propertySeparator);
|
|
}
|
|
|
|
const int kWrapWidth = 65;
|
|
if (property.style != DiagnosticsTreeStyle.singleLine) {
|
|
TextTreeConfiguration propertyStyle = property.textTreeConfiguration;
|
|
builder.writeRaw(property.toStringDeep(
|
|
prefixLineOne: builder.prefixOtherLines + propertyStyle.prefixLineOne,
|
|
prefixOtherLines: builder.prefixOtherLines + propertyStyle.linkCharacter +
|
|
propertyStyle.prefixOtherLines,
|
|
parentConfiguration: config,
|
|
minLevel: minLevel
|
|
));
|
|
continue;
|
|
}
|
|
|
|
D.assert(property.style == DiagnosticsTreeStyle.singleLine);
|
|
string message = property.toString(parentConfiguration: config, minLevel: minLevel);
|
|
if (!config.lineBreakProperties || message.Length < kWrapWidth) {
|
|
builder.write(message);
|
|
}
|
|
else {
|
|
var lines = message.Split('\n');
|
|
for (int j = 0; j < lines.Length; ++j) {
|
|
string line = lines[j];
|
|
if (j > 0) {
|
|
builder.write(config.lineBreak);
|
|
}
|
|
|
|
builder.write(string.Join("\n",
|
|
DebugPrint.debugWordWrap(line, kWrapWidth, wrapIndent: " ").ToArray()));
|
|
}
|
|
}
|
|
|
|
if (config.lineBreakProperties) {
|
|
builder.write(config.lineBreak);
|
|
}
|
|
}
|
|
|
|
if (properties.isNotEmpty()) {
|
|
builder.write(config.afterProperties);
|
|
}
|
|
|
|
if (!config.lineBreakProperties) {
|
|
builder.write(config.lineBreak);
|
|
}
|
|
|
|
var prefixChildren = prefixOtherLines + config.bodyIndent;
|
|
if (children.isEmpty() &&
|
|
config.addBlankLineIfNoChildren &&
|
|
builder.hasMultipleLines) {
|
|
string prefix = prefixChildren.TrimEnd();
|
|
if (prefix.isNotEmpty()) {
|
|
builder.writeRaw(prefix + config.lineBreak);
|
|
}
|
|
}
|
|
|
|
if (children.isNotEmpty() && config.showChildren) {
|
|
if (config.isBlankLineBetweenPropertiesAndChildren &&
|
|
properties.isNotEmpty() &&
|
|
children.First().textTreeConfiguration.isBlankLineBetweenPropertiesAndChildren) {
|
|
builder.write(config.lineBreak);
|
|
}
|
|
|
|
for (int i = 0; i < children.Count; i++) {
|
|
DiagnosticsNode child = children[i];
|
|
D.assert(child != null);
|
|
TextTreeConfiguration childConfig = _childTextConfiguration(child, config);
|
|
if (i == children.Count - 1) {
|
|
string lastChildPrefixLineOne = prefixChildren + childConfig.prefixLastChildLineOne;
|
|
builder.writeRawLine(child.toStringDeep(
|
|
prefixLineOne: lastChildPrefixLineOne,
|
|
prefixOtherLines: prefixChildren + childConfig.childLinkSpace +
|
|
childConfig.prefixOtherLines,
|
|
parentConfiguration: config,
|
|
minLevel: minLevel
|
|
));
|
|
if (childConfig.footer.isNotEmpty()) {
|
|
builder.writeRaw(prefixChildren + childConfig.childLinkSpace + childConfig.footer);
|
|
}
|
|
}
|
|
else {
|
|
TextTreeConfiguration nextChildStyle = _childTextConfiguration(children[i + 1], config);
|
|
string childPrefixLineOne = prefixChildren + childConfig.prefixLineOne;
|
|
string childPrefixOtherLines =
|
|
prefixChildren + nextChildStyle.linkCharacter + childConfig.prefixOtherLines;
|
|
builder.writeRawLine(child.toStringDeep(
|
|
prefixLineOne: childPrefixLineOne,
|
|
prefixOtherLines: childPrefixOtherLines,
|
|
parentConfiguration: config,
|
|
minLevel: minLevel
|
|
));
|
|
if (childConfig.footer.isNotEmpty()) {
|
|
builder.writeRaw(prefixChildren + nextChildStyle.linkCharacter + childConfig.footer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return builder.ToString();
|
|
}
|
|
}
|
|
|
|
public class MessageProperty : DiagnosticsProperty<object> {
|
|
public MessageProperty(string name, string message,
|
|
DiagnosticLevel level = DiagnosticLevel.info
|
|
) : base(name, null, description: message, level: level) {
|
|
D.assert(name != null);
|
|
D.assert(message != null);
|
|
}
|
|
}
|
|
|
|
public class StringProperty : DiagnosticsProperty<string> {
|
|
public StringProperty(string name, string value,
|
|
string description = null,
|
|
string tooltip = null,
|
|
bool showName = true,
|
|
object defaultValue = null,
|
|
bool quoted = true,
|
|
string ifEmpty = null,
|
|
DiagnosticLevel level = DiagnosticLevel.info
|
|
) : base(name,
|
|
value,
|
|
description: description,
|
|
defaultValue: defaultValue,
|
|
tooltip: tooltip,
|
|
showName: showName,
|
|
ifEmpty: ifEmpty,
|
|
level: level) {
|
|
this.quoted = quoted;
|
|
}
|
|
|
|
public readonly bool quoted;
|
|
|
|
public override Dictionary<string, object> toJsonMap() {
|
|
var json = base.toJsonMap();
|
|
json["quoted"] = quoted;
|
|
return json;
|
|
}
|
|
|
|
protected override string valueToString(TextTreeConfiguration parentConfiguration = null) {
|
|
string text = _description ?? value;
|
|
if (parentConfiguration != null &&
|
|
!parentConfiguration.lineBreakProperties &&
|
|
text != null) {
|
|
text = text.Replace("\n", "\\n");
|
|
}
|
|
|
|
if (quoted && text != null) {
|
|
if (ifEmpty != null && text.isEmpty()) {
|
|
return ifEmpty;
|
|
}
|
|
|
|
return "\"" + text + "\"";
|
|
}
|
|
|
|
return text ?? "null";
|
|
}
|
|
}
|
|
|
|
public abstract class _NumProperty<T> : DiagnosticsProperty<T> {
|
|
internal _NumProperty(string name,
|
|
T value,
|
|
string ifNull = null,
|
|
string unit = null,
|
|
bool showName = true,
|
|
object defaultValue = null,
|
|
string tooltip = null,
|
|
DiagnosticLevel level = DiagnosticLevel.info
|
|
) : base(
|
|
name,
|
|
value,
|
|
ifNull: ifNull,
|
|
showName: showName,
|
|
defaultValue: defaultValue,
|
|
tooltip: tooltip,
|
|
level: level
|
|
) {
|
|
this.unit = unit;
|
|
}
|
|
|
|
internal _NumProperty(string name,
|
|
ComputePropertyValueCallback<T> computeValue,
|
|
string ifNull = null,
|
|
string unit = null,
|
|
bool showName = true,
|
|
object defaultValue = null,
|
|
string tooltip = null,
|
|
DiagnosticLevel level = DiagnosticLevel.info
|
|
) : base(
|
|
name,
|
|
computeValue,
|
|
ifNull: ifNull,
|
|
showName: showName,
|
|
defaultValue: defaultValue,
|
|
tooltip: tooltip,
|
|
level: level
|
|
) {
|
|
this.unit = unit;
|
|
}
|
|
|
|
public override Dictionary<string, object> toJsonMap() {
|
|
var json = base.toJsonMap();
|
|
if (unit != null) {
|
|
json["unit"] = unit;
|
|
}
|
|
|
|
json["numberToString"] = numberToString();
|
|
return json;
|
|
}
|
|
|
|
public readonly string unit;
|
|
|
|
protected abstract string numberToString();
|
|
|
|
protected override string valueToString(TextTreeConfiguration parentConfiguration = null) {
|
|
if (value == null) {
|
|
return "null";
|
|
}
|
|
|
|
return unit != null ? numberToString() + unit : numberToString();
|
|
}
|
|
}
|
|
|
|
public class IntProperty : _NumProperty<int?> {
|
|
public IntProperty(string name, int? value,
|
|
string ifNull = null,
|
|
bool showName = true,
|
|
string unit = null,
|
|
object defaultValue = null,
|
|
DiagnosticLevel level = DiagnosticLevel.info
|
|
) : base(
|
|
name,
|
|
value,
|
|
ifNull: ifNull,
|
|
showName: showName,
|
|
unit: unit,
|
|
defaultValue: defaultValue,
|
|
level: level
|
|
) {
|
|
}
|
|
|
|
protected override string numberToString() {
|
|
if (value == null) {
|
|
return "null";
|
|
}
|
|
|
|
return value.Value.ToString();
|
|
}
|
|
}
|
|
|
|
public class FloatProperty : _NumProperty<float?> {
|
|
public FloatProperty(string name, float? value,
|
|
string ifNull = null,
|
|
string unit = null,
|
|
string tooltip = null,
|
|
object defaultValue = null,
|
|
bool showName = true,
|
|
DiagnosticLevel level = DiagnosticLevel.info
|
|
) : base(
|
|
name,
|
|
value,
|
|
ifNull: ifNull,
|
|
unit: unit,
|
|
tooltip: tooltip,
|
|
defaultValue: defaultValue,
|
|
showName: showName,
|
|
level: level
|
|
) {
|
|
}
|
|
|
|
FloatProperty(
|
|
string name,
|
|
ComputePropertyValueCallback<float?> computeValue,
|
|
string ifNull = null,
|
|
bool showName = true,
|
|
string unit = null,
|
|
string tooltip = null,
|
|
object defaultValue = null,
|
|
DiagnosticLevel level = DiagnosticLevel.info
|
|
) : base(
|
|
name,
|
|
computeValue,
|
|
showName: showName,
|
|
ifNull: ifNull,
|
|
unit: unit,
|
|
tooltip: tooltip,
|
|
defaultValue: defaultValue,
|
|
level: level
|
|
) {
|
|
}
|
|
|
|
public static FloatProperty lazy(
|
|
string name,
|
|
ComputePropertyValueCallback<float?> computeValue,
|
|
string ifNull = null,
|
|
bool showName = true,
|
|
string unit = null,
|
|
string tooltip = null,
|
|
object defaultValue = null,
|
|
DiagnosticLevel level = DiagnosticLevel.info
|
|
) {
|
|
return new FloatProperty(
|
|
name,
|
|
computeValue,
|
|
showName: showName,
|
|
ifNull: ifNull,
|
|
unit: unit,
|
|
tooltip: tooltip,
|
|
defaultValue: defaultValue,
|
|
level: level
|
|
);
|
|
}
|
|
|
|
protected override string numberToString() {
|
|
if (value != null) {
|
|
return value.Value.ToString("F1");
|
|
}
|
|
|
|
return "null";
|
|
}
|
|
}
|
|
|
|
public class PercentProperty : FloatProperty {
|
|
public PercentProperty(string name, float fraction,
|
|
string ifNull = null,
|
|
bool showName = true,
|
|
string tooltip = null,
|
|
string unit = null,
|
|
DiagnosticLevel level = DiagnosticLevel.info
|
|
) : base(
|
|
name,
|
|
fraction,
|
|
ifNull: ifNull,
|
|
showName: showName,
|
|
tooltip: tooltip,
|
|
unit: unit,
|
|
level: level
|
|
) {
|
|
}
|
|
|
|
protected override string valueToString(TextTreeConfiguration parentConfiguration = null) {
|
|
if (value == null) {
|
|
return "null";
|
|
}
|
|
|
|
return unit != null ? numberToString() + " " + unit : numberToString();
|
|
}
|
|
|
|
protected override string numberToString() {
|
|
if (value == null) {
|
|
return "null";
|
|
}
|
|
|
|
return (value.Value.clamp(0.0f, 1.0f) * 100).ToString("F1") + "%";
|
|
}
|
|
}
|
|
|
|
public class FlagProperty : DiagnosticsProperty<bool?> {
|
|
public FlagProperty(string name,
|
|
bool? value,
|
|
string ifTrue = null,
|
|
string ifFalse = null,
|
|
bool showName = false,
|
|
object defaultValue = null,
|
|
DiagnosticLevel level = DiagnosticLevel.info
|
|
) : base(name,
|
|
value,
|
|
showName: showName,
|
|
defaultValue: defaultValue,
|
|
level: level
|
|
) {
|
|
D.assert(ifTrue != null || ifFalse != null);
|
|
}
|
|
|
|
public override Dictionary<string, object> toJsonMap() {
|
|
var json = base.toJsonMap();
|
|
if (ifTrue != null) {
|
|
json["ifTrue"] = ifTrue;
|
|
}
|
|
|
|
if (ifFalse != null) {
|
|
json["ifFalse"] = ifFalse;
|
|
}
|
|
|
|
return json;
|
|
}
|
|
|
|
public readonly string ifTrue;
|
|
|
|
public readonly string ifFalse;
|
|
|
|
protected override string valueToString(TextTreeConfiguration parentConfiguration = null) {
|
|
if (value == true) {
|
|
if (ifTrue != null) {
|
|
return ifTrue;
|
|
}
|
|
}
|
|
else if (value == false) {
|
|
if (ifFalse != null) {
|
|
return ifFalse;
|
|
}
|
|
}
|
|
|
|
return base.valueToString(parentConfiguration: parentConfiguration);
|
|
}
|
|
|
|
public override bool showName {
|
|
get {
|
|
if (value == null || value == true && ifTrue == null ||
|
|
value == false && ifFalse == null) {
|
|
return true;
|
|
}
|
|
|
|
return base.showName;
|
|
}
|
|
}
|
|
|
|
public override DiagnosticLevel level {
|
|
get {
|
|
if (value == true) {
|
|
if (ifTrue == null) {
|
|
return DiagnosticLevel.hidden;
|
|
}
|
|
}
|
|
|
|
if (value == false) {
|
|
if (ifFalse == null) {
|
|
return DiagnosticLevel.hidden;
|
|
}
|
|
}
|
|
|
|
return base.level;
|
|
}
|
|
}
|
|
}
|
|
|
|
public class EnumerableProperty<T> : DiagnosticsProperty<IEnumerable<T>> {
|
|
public EnumerableProperty(
|
|
string name,
|
|
IEnumerable<T> value,
|
|
object defaultValue = null,
|
|
string ifNull = null,
|
|
string ifEmpty = "[]",
|
|
DiagnosticsTreeStyle style = DiagnosticsTreeStyle.singleLine,
|
|
bool showName = true,
|
|
DiagnosticLevel level = DiagnosticLevel.info
|
|
) : base(
|
|
name,
|
|
value,
|
|
defaultValue: defaultValue,
|
|
ifNull: ifNull,
|
|
ifEmpty: ifEmpty,
|
|
style: style,
|
|
showName: showName,
|
|
level: level
|
|
) {
|
|
}
|
|
|
|
protected override string valueToString(TextTreeConfiguration parentConfiguration = null) {
|
|
if (value == null) {
|
|
return "null";
|
|
}
|
|
|
|
if (!value.Any()) {
|
|
return ifEmpty ?? "[]";
|
|
}
|
|
|
|
if (parentConfiguration != null && !parentConfiguration.lineBreakProperties) {
|
|
return string.Join(", ", value.Select(v => v.ToString()).ToArray());
|
|
}
|
|
|
|
return string.Join(style == DiagnosticsTreeStyle.singleLine ? ", " : "\n",
|
|
value.Select(v => v.ToString()).ToArray());
|
|
}
|
|
|
|
public override DiagnosticLevel level {
|
|
get {
|
|
if (ifEmpty == null &&
|
|
value != null && !value.Any()
|
|
&& base.level != DiagnosticLevel.hidden) {
|
|
return DiagnosticLevel.fine;
|
|
}
|
|
|
|
return base.level;
|
|
}
|
|
}
|
|
|
|
public override Dictionary<string, object> toJsonMap() {
|
|
var json = base.toJsonMap();
|
|
if (value != null) {
|
|
json["values"] = value.Select(v => v.ToString()).ToList();
|
|
}
|
|
|
|
return json;
|
|
}
|
|
}
|
|
|
|
|
|
public class EnumProperty<T> : DiagnosticsProperty<T> {
|
|
public EnumProperty(string name, T value,
|
|
object defaultValue = null,
|
|
DiagnosticLevel level = DiagnosticLevel.info
|
|
) : base(
|
|
name,
|
|
value,
|
|
defaultValue: defaultValue,
|
|
level: level
|
|
) {
|
|
}
|
|
|
|
protected override string valueToString(TextTreeConfiguration parentConfiguration = null) {
|
|
if (value == null) {
|
|
return "null";
|
|
}
|
|
|
|
return value.ToString();
|
|
}
|
|
}
|
|
|
|
public class ObjectFlagProperty<T> : DiagnosticsProperty<T> {
|
|
public ObjectFlagProperty(string name, T value,
|
|
string ifPresent = null,
|
|
string ifNull = null,
|
|
bool showName = false,
|
|
DiagnosticLevel level = DiagnosticLevel.info
|
|
) : base(
|
|
name,
|
|
value,
|
|
showName: showName,
|
|
ifNull: ifNull,
|
|
level: level
|
|
) {
|
|
D.assert(ifPresent != null || ifNull != null);
|
|
}
|
|
|
|
ObjectFlagProperty(
|
|
string name,
|
|
T value,
|
|
DiagnosticLevel level = DiagnosticLevel.info
|
|
) : base(
|
|
name,
|
|
value,
|
|
showName: false,
|
|
level: level
|
|
) {
|
|
D.assert(name != null);
|
|
ifPresent = "has " + name;
|
|
}
|
|
|
|
public static ObjectFlagProperty<T> has(
|
|
string name,
|
|
T value,
|
|
DiagnosticLevel level = DiagnosticLevel.info
|
|
) {
|
|
return new ObjectFlagProperty<T>(name, value, level);
|
|
}
|
|
|
|
public readonly string ifPresent;
|
|
|
|
protected override string valueToString(
|
|
TextTreeConfiguration parentConfiguration = null) {
|
|
if (value != null) {
|
|
if (ifPresent != null) {
|
|
return ifPresent;
|
|
}
|
|
}
|
|
else {
|
|
if (ifNull != null) {
|
|
return ifNull;
|
|
}
|
|
}
|
|
|
|
return base.valueToString(parentConfiguration: parentConfiguration);
|
|
}
|
|
|
|
public override bool showName {
|
|
get {
|
|
if ((value != null && ifPresent == null) || (value == null && ifNull == null)) {
|
|
return true;
|
|
}
|
|
|
|
return base.showName;
|
|
}
|
|
}
|
|
|
|
public override DiagnosticLevel level {
|
|
get {
|
|
if (value != null) {
|
|
if (ifPresent == null) {
|
|
return DiagnosticLevel.hidden;
|
|
}
|
|
}
|
|
else {
|
|
if (ifNull == null) {
|
|
return DiagnosticLevel.hidden;
|
|
}
|
|
}
|
|
|
|
return base.level;
|
|
}
|
|
}
|
|
|
|
public override Dictionary<string, object> toJsonMap() {
|
|
var json = base.toJsonMap();
|
|
if (ifPresent != null) {
|
|
json["ifPresent"] = ifPresent;
|
|
}
|
|
|
|
return json;
|
|
}
|
|
}
|
|
|
|
public delegate T ComputePropertyValueCallback<T>();
|
|
|
|
public class DiagnosticsProperty<T> : DiagnosticsNode {
|
|
public DiagnosticsProperty(
|
|
string name,
|
|
T value,
|
|
string description = null,
|
|
string ifNull = null,
|
|
string ifEmpty = null,
|
|
bool showName = true,
|
|
bool showSeparator = true,
|
|
object defaultValue = null,
|
|
string tooltip = null,
|
|
bool missingIfNull = false,
|
|
DiagnosticsTreeStyle style = DiagnosticsTreeStyle.singleLine,
|
|
DiagnosticLevel level = DiagnosticLevel.info
|
|
) : base(
|
|
name: name,
|
|
showName: showName,
|
|
showSeparator: showSeparator,
|
|
style: style
|
|
) {
|
|
defaultValue = defaultValue ?? foundation_.kNoDefaultValue;
|
|
if (defaultValue == foundation_.kNullDefaultValue) {
|
|
defaultValue = null;
|
|
}
|
|
|
|
D.assert(defaultValue == null || defaultValue == foundation_.kNoDefaultValue || defaultValue is T);
|
|
_description = description;
|
|
_valueComputed = true;
|
|
_value = value;
|
|
_computeValue = null;
|
|
this.ifNull = ifNull ?? (missingIfNull ? "MISSING" : null);
|
|
_defaultLevel = level;
|
|
this.ifEmpty = ifEmpty;
|
|
this.defaultValue = defaultValue;
|
|
this.tooltip = tooltip;
|
|
this.missingIfNull = missingIfNull;
|
|
}
|
|
|
|
internal DiagnosticsProperty(
|
|
string name,
|
|
ComputePropertyValueCallback<T> computeValue,
|
|
string description = null,
|
|
string ifNull = null,
|
|
string ifEmpty = null,
|
|
bool showName = true,
|
|
bool showSeparator = true,
|
|
object defaultValue = null,
|
|
string tooltip = null,
|
|
bool missingIfNull = false,
|
|
DiagnosticsTreeStyle style = DiagnosticsTreeStyle.singleLine,
|
|
DiagnosticLevel level = DiagnosticLevel.info
|
|
) : base(
|
|
name: name,
|
|
showName: showName,
|
|
showSeparator: showSeparator,
|
|
style: style
|
|
) {
|
|
defaultValue = defaultValue ?? foundation_.kNoDefaultValue;
|
|
if (defaultValue == foundation_.kNullDefaultValue) {
|
|
defaultValue = null;
|
|
}
|
|
|
|
D.assert(defaultValue == null || defaultValue == foundation_.kNoDefaultValue || defaultValue is T);
|
|
_description = description;
|
|
_valueComputed = false;
|
|
_value = default(T);
|
|
_computeValue = computeValue;
|
|
_defaultLevel = level;
|
|
this.ifNull = ifNull ?? (missingIfNull ? "MISSING" : null);
|
|
this.ifEmpty = ifEmpty;
|
|
this.defaultValue = defaultValue;
|
|
this.tooltip = tooltip;
|
|
this.missingIfNull = missingIfNull;
|
|
}
|
|
|
|
public static DiagnosticsProperty<T> lazy(
|
|
string name,
|
|
ComputePropertyValueCallback<T> computeValue,
|
|
string description = null,
|
|
string ifNull = null,
|
|
string ifEmpty = null,
|
|
bool showName = true,
|
|
bool showSeparator = true,
|
|
object defaultValue = null,
|
|
string tooltip = null,
|
|
bool missingIfNull = false,
|
|
DiagnosticsTreeStyle style = DiagnosticsTreeStyle.singleLine,
|
|
DiagnosticLevel level = DiagnosticLevel.info
|
|
) {
|
|
return new DiagnosticsProperty<T>(
|
|
name,
|
|
computeValue,
|
|
description,
|
|
ifNull,
|
|
ifEmpty,
|
|
showName,
|
|
showSeparator,
|
|
defaultValue,
|
|
tooltip,
|
|
missingIfNull,
|
|
style,
|
|
level);
|
|
}
|
|
|
|
internal readonly string _description;
|
|
|
|
public override Dictionary<string, object> toJsonMap() {
|
|
var json = base.toJsonMap();
|
|
if (defaultValue != foundation_.kNoDefaultValue) {
|
|
json["defaultValue"] = Convert.ToString(defaultValue);
|
|
}
|
|
|
|
if (ifEmpty != null) {
|
|
json["ifEmpty"] = ifEmpty;
|
|
}
|
|
|
|
if (ifNull != null) {
|
|
json["ifNull"] = ifNull;
|
|
}
|
|
|
|
if (tooltip != null) {
|
|
json["tooltip"] = tooltip;
|
|
}
|
|
|
|
json["missingIfNull"] = missingIfNull;
|
|
if (exception != null) {
|
|
json["exception"] = exception.ToString();
|
|
}
|
|
|
|
json["propertyType"] = propertyType.ToString();
|
|
json["valueToString"] = valueToString();
|
|
json["defaultLevel"] = Convert.ToString(_defaultLevel);
|
|
if (typeof(Diagnosticable).IsAssignableFrom(typeof(T))) {
|
|
json["isDiagnosticableValue"] = true;
|
|
}
|
|
|
|
return json;
|
|
}
|
|
|
|
protected virtual string valueToString(
|
|
TextTreeConfiguration parentConfiguration = null
|
|
) {
|
|
var v = value;
|
|
var tree = v as DiagnosticableTree;
|
|
return tree != null ? tree.toStringShort() : v != null ? v.ToString() : "null";
|
|
}
|
|
|
|
public override string toDescription(
|
|
TextTreeConfiguration parentConfiguration = null
|
|
) {
|
|
if (_description != null) {
|
|
return _addTooltip(_description);
|
|
}
|
|
|
|
if (exception != null) {
|
|
return "EXCEPTION (" + exception.GetType() + ")";
|
|
}
|
|
|
|
if (ifNull != null && value == null) {
|
|
return _addTooltip(ifNull);
|
|
}
|
|
|
|
string result = valueToString(parentConfiguration: parentConfiguration);
|
|
if (result.isEmpty() && ifEmpty != null) {
|
|
result = ifEmpty;
|
|
}
|
|
|
|
return _addTooltip(result);
|
|
}
|
|
|
|
string _addTooltip(string text) {
|
|
D.assert(text != null);
|
|
return tooltip == null ? text : text + "(" + tooltip + ")";
|
|
}
|
|
|
|
public readonly string ifNull;
|
|
public readonly string ifEmpty;
|
|
public readonly string tooltip;
|
|
public readonly bool missingIfNull;
|
|
|
|
public Type propertyType {
|
|
get { return typeof(T); }
|
|
}
|
|
|
|
public override object valueObject {
|
|
get { return value; }
|
|
}
|
|
|
|
public T value {
|
|
get {
|
|
_maybeCacheValue();
|
|
return _value;
|
|
}
|
|
}
|
|
|
|
T _value;
|
|
bool _valueComputed;
|
|
Exception _exception;
|
|
|
|
public Exception exception {
|
|
get {
|
|
_maybeCacheValue();
|
|
return _exception;
|
|
}
|
|
}
|
|
|
|
void _maybeCacheValue() {
|
|
if (_valueComputed) {
|
|
return;
|
|
}
|
|
|
|
_valueComputed = true;
|
|
try {
|
|
_value = _computeValue();
|
|
}
|
|
catch (Exception ex) {
|
|
_exception = ex;
|
|
_value = default(T);
|
|
}
|
|
}
|
|
|
|
public readonly object defaultValue;
|
|
DiagnosticLevel _defaultLevel;
|
|
|
|
public override DiagnosticLevel level {
|
|
get {
|
|
if (_defaultLevel == DiagnosticLevel.hidden) {
|
|
return _defaultLevel;
|
|
}
|
|
|
|
if (exception != null) {
|
|
return DiagnosticLevel.error;
|
|
}
|
|
|
|
if (value == null && missingIfNull) {
|
|
return DiagnosticLevel.warning;
|
|
}
|
|
|
|
if (defaultValue != foundation_.kNoDefaultValue && Equals(value, defaultValue)) {
|
|
return DiagnosticLevel.fine;
|
|
}
|
|
|
|
return _defaultLevel;
|
|
}
|
|
}
|
|
|
|
readonly ComputePropertyValueCallback<T> _computeValue;
|
|
|
|
public override List<DiagnosticsNode> getProperties() {
|
|
return new List<DiagnosticsNode>();
|
|
}
|
|
|
|
public override List<DiagnosticsNode> getChildren() {
|
|
return new List<DiagnosticsNode>();
|
|
}
|
|
}
|
|
|
|
public class DiagnosticableNode<T> : DiagnosticsNode where T : Diagnosticable {
|
|
public DiagnosticableNode(
|
|
string name = null,
|
|
T value = null,
|
|
DiagnosticsTreeStyle? style = null
|
|
) : base(name: name, style: style) {
|
|
D.assert(value != null);
|
|
_value = value;
|
|
}
|
|
|
|
public override object valueObject {
|
|
get { return value; }
|
|
}
|
|
|
|
public T value {
|
|
get { return _value; }
|
|
}
|
|
|
|
readonly T _value;
|
|
DiagnosticPropertiesBuilder _cachedBuilder;
|
|
|
|
DiagnosticPropertiesBuilder _builder {
|
|
get {
|
|
if (_cachedBuilder == null) {
|
|
_cachedBuilder = new DiagnosticPropertiesBuilder();
|
|
if (_value != null) {
|
|
_value.debugFillProperties(_cachedBuilder);
|
|
}
|
|
}
|
|
|
|
return _cachedBuilder;
|
|
}
|
|
}
|
|
|
|
public override DiagnosticsTreeStyle? style {
|
|
get { return base.style ?? _builder.defaultDiagnosticsTreeStyle; }
|
|
}
|
|
|
|
public override string emptyBodyDescription {
|
|
get { return _builder.emptyBodyDescription; }
|
|
}
|
|
|
|
public override List<DiagnosticsNode> getProperties() {
|
|
return _builder.properties;
|
|
}
|
|
|
|
public override List<DiagnosticsNode> getChildren() {
|
|
return new List<DiagnosticsNode>();
|
|
}
|
|
|
|
public override string toDescription(
|
|
TextTreeConfiguration parentConfiguration = null
|
|
) {
|
|
return _value.toStringShort();
|
|
}
|
|
}
|
|
|
|
class _DiagnosticableTreeNode : DiagnosticableNode<DiagnosticableTree> {
|
|
internal _DiagnosticableTreeNode(
|
|
string name,
|
|
DiagnosticableTree value,
|
|
DiagnosticsTreeStyle style
|
|
) : base(
|
|
name: name,
|
|
value: value,
|
|
style: style
|
|
) {
|
|
}
|
|
|
|
public override List<DiagnosticsNode> getChildren() {
|
|
if (value != null) {
|
|
return value.debugDescribeChildren();
|
|
}
|
|
|
|
return new List<DiagnosticsNode>();
|
|
}
|
|
}
|
|
|
|
public static partial class foundation_ {
|
|
public static string shortHash(object o) {
|
|
return (o.GetHashCode() & 0xFFFFF).ToString("X").PadLeft(5, '0');
|
|
}
|
|
|
|
public static string describeIdentity(object o) {
|
|
return $"{o.GetType()}#{shortHash(o)}";
|
|
}
|
|
}
|
|
|
|
public class DiagnosticPropertiesBuilder {
|
|
public void add(DiagnosticsNode property) {
|
|
properties.Add(property);
|
|
}
|
|
|
|
public readonly List<DiagnosticsNode> properties = new List<DiagnosticsNode>();
|
|
public DiagnosticsTreeStyle defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.sparse;
|
|
public string emptyBodyDescription;
|
|
}
|
|
|
|
public abstract class Diagnosticable {
|
|
protected Diagnosticable() {
|
|
}
|
|
|
|
public virtual string toStringShort() {
|
|
return foundation_.describeIdentity(this);
|
|
}
|
|
|
|
public override string ToString() {
|
|
return toString();
|
|
}
|
|
|
|
public virtual string toString(DiagnosticLevel minLevel = DiagnosticLevel.debug) {
|
|
string fullString = null;
|
|
D.assert(() => {
|
|
fullString = toDiagnosticsNode(style: DiagnosticsTreeStyle.singleLine)
|
|
.toString(minLevel: minLevel);
|
|
return true;
|
|
});
|
|
return fullString ?? toStringShort();
|
|
}
|
|
|
|
public virtual DiagnosticsNode toDiagnosticsNode(
|
|
string name = null,
|
|
DiagnosticsTreeStyle style = DiagnosticsTreeStyle.sparse) {
|
|
return new DiagnosticableNode<Diagnosticable>(
|
|
name: name, value: this, style: style
|
|
);
|
|
}
|
|
|
|
public virtual void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
|
}
|
|
}
|
|
|
|
public abstract class DiagnosticableTree : Diagnosticable {
|
|
protected DiagnosticableTree() {
|
|
}
|
|
|
|
public virtual string toStringShallow(
|
|
string joiner = ", ",
|
|
DiagnosticLevel minLevel = DiagnosticLevel.debug
|
|
) {
|
|
var result = new StringBuilder();
|
|
result.Append(ToString());
|
|
result.Append(joiner);
|
|
DiagnosticPropertiesBuilder builder = new DiagnosticPropertiesBuilder();
|
|
debugFillProperties(builder);
|
|
result.Append(string.Join(joiner,
|
|
builder.properties.Where(n => !n.isFiltered(minLevel)).Select(n => n.ToString()).ToArray())
|
|
);
|
|
return result.ToString();
|
|
}
|
|
|
|
public virtual string toStringDeep(
|
|
string prefixLineOne = "",
|
|
string prefixOtherLines = null,
|
|
DiagnosticLevel minLevel = DiagnosticLevel.debug
|
|
) {
|
|
return toDiagnosticsNode().toStringDeep(
|
|
prefixLineOne: prefixLineOne,
|
|
prefixOtherLines: prefixOtherLines,
|
|
minLevel: minLevel);
|
|
}
|
|
|
|
public override string toStringShort() {
|
|
return foundation_.describeIdentity(this);
|
|
}
|
|
|
|
public override DiagnosticsNode toDiagnosticsNode(
|
|
string name = null,
|
|
DiagnosticsTreeStyle style = DiagnosticsTreeStyle.sparse) {
|
|
return new _DiagnosticableTreeNode(
|
|
name: name,
|
|
value: this,
|
|
style: style
|
|
);
|
|
}
|
|
|
|
public virtual List<DiagnosticsNode> debugDescribeChildren() {
|
|
return new List<DiagnosticsNode>();
|
|
}
|
|
}
|
|
}
|