您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 
 

1585 行
52 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,
}
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;
this.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;
}
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 this._hasMultipleLines; }
}
public void write(string s) {
if (s.isEmpty()) {
return;
}
if (s == "\n") {
if (this._buffer.Length == 0) {
this._buffer.Append(this.prefixLineOne.TrimEnd());
}
else if (this._atLineStart) {
this._buffer.Append(this.prefixOtherLines.TrimEnd());
this._hasMultipleLines = true;
}
this._buffer.Append("\n");
this._atLineStart = true;
return;
}
if (this._buffer.Length == 0) {
this._buffer.Append(this.prefixLineOne);
}
else if (this._atLineStart) {
this._buffer.Append(this.prefixOtherLines);
this._hasMultipleLines = true;
}
bool lineTerminated = false;
if (s.EndsWith("\n")) {
s = s.Substring(0, s.Length - 1);
lineTerminated = true;
}
var parts = s.Split('\n');
this._buffer.Append(parts[0]);
for (int i = 1; i < parts.Length; ++i) {
this._buffer.Append("\n")
.Append(this.prefixOtherLines)
.Append(parts[i]);
}
if (lineTerminated) {
this._buffer.Append("\n");
}
this._atLineStart = lineTerminated;
}
public void writeRaw(string text) {
if (text.isEmpty()) {
return;
}
this._buffer.Append(text);
this._atLineStart = text.EndsWith("\n");
}
public void writeRawLine(string line) {
if (line.isEmpty()) {
return;
}
this._buffer.Append(line);
if (!line.EndsWith("\n")) {
this._buffer.Append('\n');
}
this._atLineStart = true;
}
public override string ToString() {
return this._buffer.ToString();
}
}
class _NoDefaultValue {
internal _NoDefaultValue() {
}
}
class _NullDefaultValue {
internal _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;
this._style = style;
this._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 this.level < minLevel;
}
public virtual DiagnosticLevel level {
get { return DiagnosticLevel.info; }
}
public virtual bool showName {
get { return this._showName; }
}
readonly bool _showName;
public virtual string emptyBodyDescription {
get { return null; }
}
public abstract object valueObject { get; }
public virtual DiagnosticsTreeStyle? style {
get { return this._style; }
}
readonly DiagnosticsTreeStyle? _style;
public abstract List<DiagnosticsNode> getProperties();
public abstract List<DiagnosticsNode> getChildren();
string _separator {
get { return this.showSeparator ? ":" : ""; }
}
public virtual Dictionary<string, object> toJsonMap() {
var data = new Dictionary<string, object> {
{"name", this.name},
{"showSeparator", this.showSeparator},
{"description", this.toDescription()},
{"level", Convert.ToString(this.level)},
{"showName", this.showName},
{"emptyBodyDescription", this.emptyBodyDescription},
{"style", Convert.ToString(this.style)},
{"valueToString", Convert.ToString(this.valueObject)},
{"type", this.GetType().ToString()},
{"hasChildren", this.getChildren().isNotEmpty()}
};
return data;
}
public override string ToString() {
return this.toString();
}
public virtual string toString(
TextTreeConfiguration parentConfiguration = null,
DiagnosticLevel minLevel = DiagnosticLevel.info
) {
D.assert(this.style != null);
if (this.style == DiagnosticsTreeStyle.singleLine) {
return this.toStringDeep(parentConfiguration: parentConfiguration, minLevel: minLevel);
}
var description = this.toDescription(parentConfiguration: parentConfiguration);
if (this.name.isEmpty() || !this.showName) {
return description;
}
return description.Contains("\n")
? this.name + this._separator + "\n" + description
: this.name + this._separator + description;
}
protected TextTreeConfiguration textTreeConfiguration {
get {
D.assert(this.style != null);
switch (this.style) {
case DiagnosticsTreeStyle.dense:
return Diagnostics.denseTextConfiguration;
case DiagnosticsTreeStyle.sparse:
return Diagnostics.sparseTextConfiguration;
case DiagnosticsTreeStyle.offstage:
return Diagnostics.dashedTextConfiguration;
case DiagnosticsTreeStyle.whitespace:
return Diagnostics.whitespaceTextConfiguration;
case DiagnosticsTreeStyle.transition:
return Diagnostics.transitionTextConfiguration;
case DiagnosticsTreeStyle.singleLine:
return Diagnostics.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 = this.getChildren();
var config = this.textTreeConfiguration;
if (prefixOtherLines.isEmpty()) {
prefixOtherLines += config.prefixOtherLinesRootNode;
}
var builder = new _PrefixedstringBuilder(
prefixLineOne,
prefixOtherLines
);
var description = this.toDescription(parentConfiguration: parentConfiguration);
if (description.isEmpty()) {
if (this.name.isNotEmpty() && this.showName) {
builder.write(this.name);
}
}
else {
if (this.name.isNotEmpty() && this.showName) {
builder.write(this.name);
if (this.showSeparator) {
builder.write(config.afterName);
}
builder.write(
config.isNameOnOwnLine || description.Contains("\n") ? "\n" : "");
if (description.Contains("\n") && this.style == DiagnosticsTreeStyle.singleLine) {
builder.prefixOtherLines += " ";
}
}
builder.prefixOtherLines +=
children.isEmpty() ? config.propertyPrefixNoChildren : config.propertyPrefixIfChildren;
builder.write(description);
}
var properties = this.getProperties().Where(n => !n.isFiltered(minLevel)).ToList();
if (properties.isNotEmpty() || children.isNotEmpty() || this.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 (this.emptyBodyDescription != null &&
properties.isEmpty() &&
children.isEmpty() &&
prefixLineOne.isNotEmpty()) {
builder.write(this.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 = this._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 = this._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"] = this.quoted;
return json;
}
protected override string valueToString(TextTreeConfiguration parentConfiguration = null) {
string text = this._description ?? this.value;
if (parentConfiguration != null &&
!parentConfiguration.lineBreakProperties &&
text != null) {
text = text.Replace("\n", "\\n");
}
if (this.quoted && text != null) {
if (this.ifEmpty != null && text.isEmpty()) {
return this.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 (this.unit != null) {
json["unit"] = this.unit;
}
json["numberToString"] = this.numberToString();
return json;
}
public readonly string unit;
protected abstract string numberToString();
protected override string valueToString(TextTreeConfiguration parentConfiguration = null) {
if (this.value == null) {
return "null";
}
return this.unit != null ? this.numberToString() + this.unit : this.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 (this.value == null) {
return "null";
}
return this.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 (this.value != null) {
return this.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 (this.value == null) {
return "null";
}
return this.unit != null ? this.numberToString() + " " + this.unit : this.numberToString();
}
protected override string numberToString() {
if (this.value == null) {
return "null";
}
return (this.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 (this.ifTrue != null) {
json["ifTrue"] = this.ifTrue;
}
if (this.ifFalse != null) {
json["ifFalse"] = this.ifFalse;
}
return json;
}
public readonly string ifTrue;
public readonly string ifFalse;
protected override string valueToString(TextTreeConfiguration parentConfiguration = null) {
if (this.value == true) {
if (this.ifTrue != null) {
return this.ifTrue;
}
}
else if (this.value == false) {
if (this.ifFalse != null) {
return this.ifFalse;
}
}
return base.valueToString(parentConfiguration: parentConfiguration);
}
public override bool showName {
get {
if (this.value == null || this.value == true && this.ifTrue == null ||
this.value == false && this.ifFalse == null) {
return true;
}
return base.showName;
}
}
public override DiagnosticLevel level {
get {
if (this.value == true) {
if (this.ifTrue == null) {
return DiagnosticLevel.hidden;
}
}
if (this.value == false) {
if (this.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 (this.value == null) {
return "null";
}
if (!this.value.Any()) {
return this.ifEmpty ?? "[]";
}
if (parentConfiguration != null && !parentConfiguration.lineBreakProperties) {
return string.Join(", ", this.value.Select(v => v.ToString()).ToArray());
}
return string.Join(this.style == DiagnosticsTreeStyle.singleLine ? ", " : "\n",
this.value.Select(v => v.ToString()).ToArray());
}
public override DiagnosticLevel level {
get {
if (this.ifEmpty == null &&
this.value != null && !this.value.Any()
&& base.level != DiagnosticLevel.hidden) {
return DiagnosticLevel.fine;
}
return base.level;
}
}
public override Dictionary<string, object> toJsonMap() {
var json = base.toJsonMap();
if (this.value != null) {
json["values"] = this.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 (this.value == null) {
return "null";
}
return this.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);
this.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 (this.value != null) {
if (this.ifPresent != null) {
return this.ifPresent;
}
}
else {
if (this.ifNull != null) {
return this.ifNull;
}
}
return base.valueToString(parentConfiguration: parentConfiguration);
}
public override bool showName {
get {
if ((this.value != null && this.ifPresent == null) || (this.value == null && this.ifNull == null)) {
return true;
}
return base.showName;
}
}
public override DiagnosticLevel level {
get {
if (this.value != null) {
if (this.ifPresent == null) {
return DiagnosticLevel.hidden;
}
}
else {
if (this.ifNull == null) {
return DiagnosticLevel.hidden;
}
}
return base.level;
}
}
public override Dictionary<string, object> toJsonMap() {
var json = base.toJsonMap();
if (this.ifPresent != null) {
json["ifPresent"] = this.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 ?? Diagnostics.kNoDefaultValue;
if (defaultValue == Diagnostics.kNullDefaultValue) {
defaultValue = null;
}
D.assert(defaultValue == null || defaultValue == Diagnostics.kNoDefaultValue || defaultValue is T);
this._description = description;
this._valueComputed = true;
this._value = value;
this._computeValue = null;
this.ifNull = ifNull ?? (missingIfNull ? "MISSING" : null);
this._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 ?? Diagnostics.kNoDefaultValue;
if (defaultValue == Diagnostics.kNullDefaultValue) {
defaultValue = null;
}
D.assert(defaultValue == null || defaultValue == Diagnostics.kNoDefaultValue || defaultValue is T);
this._description = description;
this._valueComputed = false;
this._value = default(T);
this._computeValue = computeValue;
this._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 (this.defaultValue != Diagnostics.kNoDefaultValue) {
json["defaultValue"] = Convert.ToString(this.defaultValue);
}
if (this.ifEmpty != null) {
json["ifEmpty"] = this.ifEmpty;
}
if (this.ifNull != null) {
json["ifNull"] = this.ifNull;
}
if (this.tooltip != null) {
json["tooltip"] = this.tooltip;
}
json["missingIfNull"] = this.missingIfNull;
if (this.exception != null) {
json["exception"] = this.exception.ToString();
}
json["propertyType"] = this.propertyType.ToString();
json["valueToString"] = this.valueToString();
json["defaultLevel"] = Convert.ToString(this._defaultLevel);
if (typeof(Diagnosticable).IsAssignableFrom(typeof(T))) {
json["isDiagnosticableValue"] = true;
}
return json;
}
protected virtual string valueToString(
TextTreeConfiguration parentConfiguration = null
) {
var v = this.value;
var tree = v as DiagnosticableTree;
return tree != null ? tree.toStringShort() : v != null ? v.ToString() : "null";
}
public override string toDescription(
TextTreeConfiguration parentConfiguration = null
) {
if (this._description != null) {
return this._addTooltip(this._description);
}
if (this.exception != null) {
return "EXCEPTION (" + this.exception.GetType() + ")";
}
if (this.ifNull != null && this.value == null) {
return this._addTooltip(this.ifNull);
}
string result = this.valueToString(parentConfiguration: parentConfiguration);
if (result.isEmpty() && this.ifEmpty != null) {
result = this.ifEmpty;
}
return this._addTooltip(result);
}
string _addTooltip(string text) {
D.assert(text != null);
return this.tooltip == null ? text : text + "(" + this.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 this.value; }
}
public T value {
get {
this._maybeCacheValue();
return this._value;
}
}
T _value;
bool _valueComputed;
Exception _exception;
public Exception exception {
get {
this._maybeCacheValue();
return this._exception;
}
}
void _maybeCacheValue() {
if (this._valueComputed) {
return;
}
this._valueComputed = true;
try {
this._value = this._computeValue();
}
catch (Exception ex) {
this._exception = ex;
this._value = default(T);
}
}
public readonly object defaultValue;
DiagnosticLevel _defaultLevel;
public override DiagnosticLevel level {
get {
if (this._defaultLevel == DiagnosticLevel.hidden) {
return this._defaultLevel;
}
if (this.exception != null) {
return DiagnosticLevel.error;
}
if (this.value == null && this.missingIfNull) {
return DiagnosticLevel.warning;
}
if (this.defaultValue != Diagnostics.kNoDefaultValue && Equals(this.value, this.defaultValue)) {
return DiagnosticLevel.fine;
}
return this._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);
this._value = value;
}
public override object valueObject {
get { return this.value; }
}
public T value {
get { return this._value; }
}
readonly T _value;
DiagnosticPropertiesBuilder _cachedBuilder;
DiagnosticPropertiesBuilder _builder {
get {
if (this._cachedBuilder == null) {
this._cachedBuilder = new DiagnosticPropertiesBuilder();
if (this._value != null) {
this._value.debugFillProperties(this._cachedBuilder);
}
}
return this._cachedBuilder;
}
}
public override DiagnosticsTreeStyle? style {
get { return base.style ?? this._builder.defaultDiagnosticsTreeStyle; }
}
public override string emptyBodyDescription {
get { return this._builder.emptyBodyDescription; }
}
public override List<DiagnosticsNode> getProperties() {
return this._builder.properties;
}
public override List<DiagnosticsNode> getChildren() {
return new List<DiagnosticsNode>();
}
public override string toDescription(
TextTreeConfiguration parentConfiguration = null
) {
return this._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 (this.value != null) {
return this.value.debugDescribeChildren();
}
return new List<DiagnosticsNode>();
}
}
public class DiagnosticPropertiesBuilder {
public void add(DiagnosticsNode property) {
this.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 Diagnostics.describeIdentity(this);
}
public override string ToString() {
return this.toString();
}
public virtual string toString(DiagnosticLevel minLevel = DiagnosticLevel.debug) {
return this.toDiagnosticsNode(style: DiagnosticsTreeStyle.singleLine).toString(minLevel: minLevel);
}
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(this.ToString());
result.Append(joiner);
DiagnosticPropertiesBuilder builder = new DiagnosticPropertiesBuilder();
this.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 this.toDiagnosticsNode().toStringDeep(
prefixLineOne: prefixLineOne,
prefixOtherLines: prefixOtherLines,
minLevel: minLevel);
}
public override string toStringShort() {
return Diagnostics.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>();
}
}
public static class Diagnostics {
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: ""
);
internal static readonly _NoDefaultValue kNoDefaultValue = new _NoDefaultValue();
public static readonly object kNullDefaultValue = new _NullDefaultValue();
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)}";
}
}
}