Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #2894 via ToStringIndent() #2909

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 44 additions & 1 deletion src/kOS.Safe/Encapsulation/EnumerableValue.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using kOS.Safe.Encapsulation.Suffixes;
using kOS.Safe.Serialization;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
Expand Down Expand Up @@ -37,11 +38,53 @@ public bool Contains(T item)
return InnerEnumerable.Contains(item);
}

public override string ToStringIndented(int level)
{
if (level >= TerminalFormatter.MAX_INDENT_LEVEL)
return "<<TOSTRING REFUSES TO RECURSE DEEPER THAN NESTING LEVEL " + level + ">>";

StringBuilder sb = new StringBuilder();
string pad = string.Empty.PadRight(level * TerminalFormatter.INDENT_SPACES, ' ');

int cnt = this.Count();
if (cnt == 0)
sb.Append(string.Format("{0} (empty)", KOSName));
else if (cnt == 1)
sb.Append(string.Format("{0} of 1 item:", KOSName));
else
sb.Append(string.Format("{0} of {1} items:", KOSName, cnt));

sb.Append(string.Format("\n{0}",ToStringItems(level + 1)));
return sb.ToString();
}

public override string ToString()
{
return new SafeSerializationMgr(null).ToString(this);
StringBuilder sb = new StringBuilder();

int cnt = this.Count();
if (cnt == 0)
sb.Append(string.Format("{0} (empty)", KOSName));
else if (cnt == 1)
sb.Append(string.Format("{0} of 1 item:", KOSName));
else
sb.Append(string.Format("{0} of {1} items:", KOSName, cnt));

sb.Append(string.Format("\n{0}",ToStringItems(1)));
return sb.ToString();
}

/// <summary>
/// Print the inner items (not the header) of a container. Override this and this
/// enumerable structure will use it in its ToString() and its ToStringIndented().
/// IMPORTANT: If your enumerable contains zero things, then return empty string, not even
/// a newline. If your enumerable contains at least one thing, then print
/// a line break at the end of each thing.
/// </summary>
/// <param name="level">you must pad all lines with this level of indent*TerminalFormatter.INDENT_SPACES</param>
/// <returns></returns>
public abstract string ToStringItems(int level);

public override Dump Dump()
{
var result = new DumpWithHeader
Expand Down
43 changes: 42 additions & 1 deletion src/kOS.Safe/Encapsulation/Lexicon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using kOS.Safe.Utilities;
using kOS.Safe.Function;

Expand Down Expand Up @@ -297,9 +298,49 @@ public void SetIndex(int index, Structure value)
internalDictionary[FromPrimitiveWithAssert(index)] = value;
}

public override string ToStringIndented(int level)
{
// eraseme - I NOTICED I REPEATED THIS EXACT SNIP OF CODE CUT-N-PASTED A FEW TIMES
// eraseme - IN DIFFERENT PLACES. THIS IS PROBABLY A CANDIDATE FOR MAKING INTO ONE
// eraseme - COMMON UTILITY METHOD (THE PART THAT PRINTS THIS HEADER WHEN A ToStringIndented()
// eraseme - WANTS TO).
if (level >= TerminalFormatter.MAX_INDENT_LEVEL)
return "<<TOSTRING REFUSES TO RECURSE DEEPER THAN NESTING LEVEL " + level + ">>";

StringBuilder sb = new StringBuilder();
string pad = string.Empty.PadRight(level * TerminalFormatter.INDENT_SPACES, ' ');

int cnt = this.Count();
if (cnt == 0)
sb.Append(string.Format("{0} (empty)", KOSName));
else if (cnt == 1)
sb.Append(string.Format("{0} of 1 item:", KOSName));
else
sb.Append(string.Format("{0} of {1} items:", KOSName, cnt));

sb.Append(string.Format("\n{0}", ToStringItems(level + 1)));
return sb.ToString();
}

public string ToStringItems(int level)
{
StringBuilder sb = new StringBuilder();
string pad = string.Empty.PadRight(level * TerminalFormatter.INDENT_SPACES, ' ');
foreach (Structure key in internalDictionary.Keys)
{
Structure val = internalDictionary[key];
sb.Append(string.Format("{0}[{1}] = {2}\n",
pad,
key.ToString(),
val.ToStringIndented(level)
));
}
return sb.ToString();
}

public override string ToString()
{
return new SafeSerializationMgr(null).ToString(this);
return ToStringIndented(0);
}

// Try to call the normal SetSuffix that all structures do, but if that fails,
Expand Down
25 changes: 25 additions & 0 deletions src/kOS.Safe/Encapsulation/ListValue.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using kOS.Safe.Encapsulation.Suffixes;
using kOS.Safe.Exceptions;
using kOS.Safe.Properties;
Expand Down Expand Up @@ -73,6 +74,30 @@ public override void LoadDump(Dump dump)
}
}

public override string ToStringItems(int level)
{
StringBuilder sb = new StringBuilder();
string pad = string.Empty.PadRight(level * TerminalFormatter.INDENT_SPACES, ' ');
int cnt = this.Count();
int digitWidth = Utilities.KOSMath.DecimalDigitsIn(cnt);
for (int i = 0; i < cnt; ++i)
{
Structure asStructure = this[i] as Structure;
if (asStructure != null)
{
sb.Append(string.Format("{0}[{1}] = {2}\n",
pad,
i.ToString().PadLeft(digitWidth),
asStructure.ToStringIndented(level)
));
}
else // Hypothetically this case should not happen, but if we screwed up somewhere so it does, at least you can see something.
{
sb.Append(this[i].ToString());
}
}
return sb.ToString();
}
private void ListInitializeSuffixes()
{
AddSuffix("COPY", new NoArgsSuffix<ListValue<T>> (() => new ListValue<T>(this)));
Expand Down
29 changes: 29 additions & 0 deletions src/kOS.Safe/Encapsulation/QueueValue.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using kOS.Safe.Encapsulation.Suffixes;
using kOS.Safe.Serialization;
using kOS.Safe.Function;
Expand Down Expand Up @@ -63,6 +64,33 @@ public static QueueValue<T> CreateQueue<TU>(IEnumerable<TU> list)
{
return new QueueValue<T>(list.Cast<T>());
}

public override string ToStringItems(int level)
{
StringBuilder sb = new StringBuilder();
string pad = string.Empty.PadRight(level * TerminalFormatter.INDENT_SPACES, ' ');
var asArray = InnerEnumerable.ToArray();
int i = 0;
foreach (object item in asArray)
{
Structure asStructure = item as Structure;
if (asStructure != null)
{
sb.Append(string.Format("{0}[{1}] = {2}\n",
pad,
(i == 0 ? "top ->" : (i == asArray.Count() ? "bottom" : " ")),
asStructure.ToStringIndented(level)
));
}
else // Hypothetically this case should not happen, but if we screwed up somewhere so it does, at least you can see something.
{
sb.Append(item.ToString());
}
++i;
}
return sb.ToString();
}

}

[kOS.Safe.Utilities.KOSNomenclature("Queue", KOSToCSharp = false)] // one-way because the generic templated QueueValue<T> is the canonical one.
Expand Down Expand Up @@ -110,5 +138,6 @@ private void InitializeSuffixes()
{
return new QueueValue(toCopy.Select(x => FromPrimitiveWithAssert(x)));
}

}
}
14 changes: 14 additions & 0 deletions src/kOS.Safe/Encapsulation/RangeValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,20 @@ public override string ToString()
{
return "RANGE(" + InnerEnumerable.Start + ", " + InnerEnumerable.Stop + ", " + InnerEnumerable.Step + ")";
}

public override string ToStringIndented(int level)
{
// By default, an Enumerable's ToStringIndented() would print out a header line, but here it's
// not needed, so override it to just print the content line only:
return ToStringItems(level);
}
public override string ToStringItems(int level)
{
// Indent level is being ignored because this is single-line and
// never contains other things, despite being implemented as
// a EnumerableValue which needs a ToStringItems().
return ToString();
}
}

public class Range : IEnumerable<ScalarValue>
Expand Down
26 changes: 26 additions & 0 deletions src/kOS.Safe/Encapsulation/StackValue.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using kOS.Safe.Encapsulation.Suffixes;
using kOS.Safe.Serialization;
using kOS.Safe.Function;
Expand Down Expand Up @@ -69,6 +70,31 @@ public static StackValue<T> CreateStack<TU>(IEnumerable<TU> list)
{
return new StackValue<T>(list.Cast<T>());
}
public override string ToStringItems(int level)
{
StringBuilder sb = new StringBuilder();
string pad = string.Empty.PadRight(level * TerminalFormatter.INDENT_SPACES, ' ');
var asArray = InnerEnumerable.ToArray();
int i = 0;
foreach (object item in asArray)
{
Structure asStructure = item as Structure;
if (asStructure != null)
{
sb.Append(string.Format("{0}[{1}] = {2}\n",
pad,
(i == 0 ? "front->" : (i == asArray.Count() ? "back ->" : " ")),
asStructure.ToStringIndented(level)
));
}
else // Hypothetically this case should not happen, but if we screwed up somewhere so it does, at least you can see something.
{
sb.Append(item.ToString());
}
++i;
}
return sb.ToString();
}
}

[kOS.Safe.Utilities.KOSNomenclature("Stack", KOSToCSharp = false)] // one-way because the generic templated StackValue<T> is the canonical one.
Expand Down
24 changes: 24 additions & 0 deletions src/kOS.Safe/Encapsulation/Structure.cs
Original file line number Diff line number Diff line change
Expand Up @@ -358,5 +358,29 @@ public static object ToPrimitive(object value)

return value;
}

/// <summary>
/// A wrapper around Structure.ToString() that will indent the ToString() output
/// to the desired indent level.
/// </summary>
public virtual string ToStringIndented(int level)
{
if (level >= TerminalFormatter.MAX_INDENT_LEVEL)
return "<<TOSTRING REFUSES TO RECURSE DEEPER THAN NESTING LEVEL " + level + ">>";

StringBuilder returnVal = new StringBuilder();
string[] lines = ToString().Split('\n');
string pad = "";
if (lines.Count() > 1)
{
pad = String.Empty.PadRight(level * TerminalFormatter.INDENT_SPACES, ' ');
returnVal.Append("\n");
}
foreach (string line in lines)
{
returnVal.AppendFormat("{0}{1}", pad, line);
}
return returnVal.ToString();
}
}
}
26 changes: 25 additions & 1 deletion src/kOS.Safe/Encapsulation/UniqueSetValue.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using kOS.Safe.Encapsulation.Suffixes;
using kOS.Safe.Exceptions;
using kOS.Safe.Serialization;
Expand Down Expand Up @@ -67,7 +68,30 @@ private void SetInitializeSuffixes()
AddSuffix("COPY", new NoArgsSuffix<UniqueSetValue<T>> (() => new UniqueSetValue<T>(this)));
AddSuffix("ADD", new OneArgsSuffix<T> (toAdd => Collection.Add(toAdd)));
AddSuffix("REMOVE", new OneArgsSuffix<BooleanValue, T> (toRemove => Collection.Remove(toRemove)));
}
}

public override string ToStringItems(int level)
{
StringBuilder sb = new StringBuilder();
string pad = string.Empty.PadRight(level * TerminalFormatter.INDENT_SPACES, ' ');
var asArray = InnerEnumerable.ToArray();
foreach (object item in asArray)
{
Structure asStructure = item as Structure;
if (asStructure != null)
{
sb.Append(string.Format("{0}{1}\n",
pad,
asStructure.ToStringIndented(level)
));
}
else // Hypothetically this case should not happen, but if we screwed up somewhere so it does, at least you can see something.
{
sb.Append(item.ToString());
}
}
return sb.ToString();
}
}

[kOS.Safe.Utilities.KOSNomenclature("UniqueSet", KOSToCSharp = false)] // one-way because the generic templated UniqueSetValue<T> is the canonical one.
Expand Down
14 changes: 12 additions & 2 deletions src/kOS.Safe/Serialization/TerminalFormatter.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using kOS.Safe.Encapsulation;
using System.Linq;
Expand All @@ -7,7 +7,17 @@ namespace kOS.Safe.Serialization
{
public class TerminalFormatter : IFormatWriter
{
private static int INDENT_SPACES = 2;
public static int INDENT_SPACES = 2;

// eraseme - MUST INCREASE THIS VALUE AFTER TESTING IS OVER!!!
public static int MAX_INDENT_LEVEL = 5; // SET LOW DURING TESTING SO IT'S EASY TO TRIGGER IT

// eraseme - THIS ENTIRE CLASS BELOW THIS POINT IS PROBABLY NOT NEEDED ANYMORE IF THIS PR IS USED.
// eraseme - IT ONLY USES THE ABOVE TWO SETTINGS (TEST THIS BY REMOVING THE REST OF THIS AND
// eraseme - SEEING IF IT COMPILES.)
// eraseme - THE ENTIRE CLASS COULD GO AWAY AND THESE SETTINGS COULD BE MOVED ELSEWHERE,
// eraseme - WHERE A USER SCRIPT COULD ALTER THEM.

private static readonly TerminalFormatter instance;

public static TerminalFormatter Instance
Expand Down
22 changes: 22 additions & 0 deletions src/kOS.Safe/Utilities/kosMath.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,5 +147,27 @@ public static double GetRandom(string key)
randomizers.Add(key, new Random());
return randomizers[key].NextDouble();
}

/// <summary>
/// Get the number of decimal digits in a number. i.e. if input = 33333, return a 5.
/// </summary>
/// <param name="val"></param>
/// <returns></returns>
public static int DecimalDigitsIn(int val)
{
int absVal = val < 0 ? -val : val;
// Believe it or not, a basic hardcoded if-else chain is actually the fastest performance
// when you know the numbers aren't allowed to be large (have to fit in int32):
if (absVal < 10) return 1;
if (absVal < 100) return 2;
if (absVal < 1000) return 3;
if (absVal < 10000) return 4;
if (absVal < 100000) return 5;
if (absVal < 1000000) return 6;
if (absVal < 10000000) return 7;
if (absVal < 100000000) return 8;
if (absVal < 1000000000) return 9;
return 10;
}
}
}