private static void SetDictionaryNodeGeneric <TKey, TValue>(Node node, Dictionary <TKey, TValue> dictionary, FileStyle style, bool forceArrayMode = false) { bool keyIsBase = BaseTypes.IsBaseType(typeof(TKey)); if (keyIsBase && !forceArrayMode && !style.AlwaysArrayDictionaries) { // we might have switched between standard and array dictionary storage, and if so, children need to be reset if (node.ChildNodeType != NodeChildrenType.key) { node.ClearChildren(newChildrenType: NodeChildrenType.key); } var CurrentKeys = new List <string>(capacity: dictionary.Count); foreach (var key in dictionary.Keys) { var value = dictionary[key]; string keyAsText = BaseTypes.SerializeBaseType <TKey>(key, style); if (!Utilities.IsValidKey(keyAsText)) { SetDictionaryNodeGeneric(node, dictionary, style, forceArrayMode: true); return; } CurrentKeys.Add(keyAsText); KeyNode child = node.GetChildAddressedByName(keyAsText); NodeManager.SetNodeData <TValue>(child, value, style); } // make sure that old data in the file is deleted when a new dictionary is saved. // node.ClearChildren() is not used because we want to keep comments and whitespace intact as much as possible. foreach (var key in node.GetChildKeys()) { if (!CurrentKeys.Contains(key)) { node.RemoveChild(key); } } } else // save dictionary as KeyValuePair<TKey, TValue>[] { // we might have switched between standard and array dictionary storage, and if so, children need to be reset if (node.ChildNodeType != NodeChildrenType.list) { node.ClearChildren(newChildrenType: NodeChildrenType.list); } var array = GetWritableKeyValuePairArray(dictionary); NodeManager.SetNodeData(node, array, array.GetType(), style); } }
private static Node GetNodeFromLine(string line, DataFile file) { var DataType = GetDataLineType(line); Node node = null; switch (DataType) { case DataLineType.key: node = new KeyNode(rawText: line, file); break; case DataLineType.list: node = new ListNode(rawText: line, file); break; default: throw new FormatException($"format error on line: {line}"); } return(node); }
public KeyNode GetChildAddressedByName(string name) { EnsureProperChildType(NodeChildrenType.key); foreach (var node in ChildNodes) { var keynode = node as KeyNode; if (keynode.Key == name) { return(keynode); } } return(CreateKeyNode(name)); KeyNode CreateKeyNode(string key) { var newnode = new KeyNode(GetProperChildIndentation(), key, File); AddChild(newnode); return(newnode); } }
/// <summary> /// Parses lines of SUCC into a data structure /// </summary> internal static (List <Line>, Dictionary <string, KeyNode>) DataStructureFromSUCC(string[] lines, ReadableDataFile fileRef) // I am so, so sorry. If you need to understand this function for whatever reason... may god give you guidance. { // if the file is empty // do this because otherwise new files are created with a newline at the top if (lines.Length == 1 && lines[0] == "") { return(new List <Line>(), new Dictionary <string, KeyNode>()); } var TopLevelLines = new List <Line>(); var TopLevelNodes = new Dictionary <string, KeyNode>(); var NestingNodeStack = new Stack <Node>(); // the top of the stack is the node that new nodes should be children of bool DoingMultiLineString = false; var file = fileRef as DataFile; // this will be null if fileRef is a ReadOnlyDataFile // parse the input line by line for (int i = 0; i < lines.Length; i++) { var line = lines[i]; if (line.Contains('\t')) { throw new FormatException("a SUCC file cannot contain tabs. Please use spaces instead."); } if (DoingMultiLineString) { if (NestingNodeStack.Peek().ChildNodeType != NodeChildrenType.multiLineString) { throw new Exception("oh f**k, we were supposed to be doing a multi-line string but the top of the node stack isn't a multi-line string node!"); } var newboi = new MultiLineStringNode(rawText: line, file); NestingNodeStack.Peek().AddChild(newboi); if (newboi.IsTerminator) { DoingMultiLineString = false; NestingNodeStack.Pop(); } continue; } if (LineHasData(line)) { Node node = GetNodeFromLine(line, file); boobies: if (NestingNodeStack.Count == 0) // if this is a top-level node { if (!(node is KeyNode)) { throw new FormatException($"top level lines must be key nodes. Line {i} does not conform to this: '{line}'"); } TopLevelLines.Add(node); KeyNode heck = node as KeyNode; TopLevelNodes.Add(heck.Key, heck); } else // if this is NOT a top-level node { int StackTopIndentation = NestingNodeStack.Peek().IndentationLevel; int LineIndentation = line.GetIndentationLevel(); if (LineIndentation > StackTopIndentation) // if this should be a child of the stack top { Node newParent = NestingNodeStack.Peek(); if (newParent.ChildNodes.Count == 0) // if this is the first child of the parent, assign the parent's child type { if (node is KeyNode) { newParent.ChildNodeType = NodeChildrenType.key; } else if (node is ListNode) { newParent.ChildNodeType = NodeChildrenType.list; } else { throw new Exception("what the f**k?"); } } else // if the parent already has children, check for errors with this line { CheckNewSiblingForErrors(child: node, newParent: newParent); } newParent.AddChild(node); } else // if this should NOT be a child of the stack top { NestingNodeStack.Pop(); goto boobies; } } if (node.Value == "") // if this is a node with children { NestingNodeStack.Push(node); } if (node.Value == MultiLineStringNode.Terminator) // if this is the start of a multi line string { NestingNodeStack.Push(node); node.ChildNodeType = NodeChildrenType.multiLineString; DoingMultiLineString = true; } } else // line has no data { Line NoDataLine = new Line(rawText: line); if (NestingNodeStack.Count == 0) { TopLevelLines.Add(NoDataLine); } else { NestingNodeStack.Peek().AddChild(NoDataLine); } } } return(TopLevelLines, TopLevelNodes); }