public VDFNode(string name, VDFData parentVDFStructure, VDFNode parent = null) { Name = name; Parent = parent; ParentVDFStructure = parentVDFStructure; InitializeKeysAndNodesList(); }
/// <summary> /// Removes node from its parent and/or from the VDFDataStructure. /// </summary> /// <param name="node">The node to be removed.</param> /// <param name="throwErrorOnNoParent">Throw an error if the parent/parentVDFStructure property of the node is set to null.</param> /// <param name="FullRemovalFromTheVDFStruct">If true, the ParentVDFStructure property of the node will be set to null as well. Always set this to true if the node is a root node.</param> public static void RemoveNodeFromParent(this VDFNode node, bool throwErrorOnNoParent = false, bool FullRemovalFromTheVDFStruct = false) { if (node.Parent != null) { node.Parent.Nodes.Remove(node); node.Parent = null; if (FullRemovalFromTheVDFStruct) { node.ParentVDFStructure = null; } } else { if (node.ParentVDFStructure == null || !FullRemovalFromTheVDFStruct) { if (throwErrorOnNoParent) { throw new NullReferenceException("Node " + node.Name + " parent property is not set!"); } else { return; } } node.ParentVDFStructure.Nodes.Remove(node); node.ParentVDFStructure = null; } }
public VDFKey(string name, string value, VDFNode parent) { Name = name ?? throw new VDFStreamException("Name of the Key cannot be Null!"); Parent = parent ?? throw new ArgumentNullException("parent"); Value = value; }
/// <summary> /// Creates a copy of the key. /// </summary> /// <param name="key">The Key to be duplicated.</param> /// <param name="parent">The Node that will parent the key.</param> /// <returns></returns> public static VDFKey Duplicate(this VDFKey key, VDFNode parent) { if (parent == null) { throw new ArgumentNullException("parent"); } return(new VDFKey(key.Name, key.Value, parent)); //Return a new instance of a key class. Copy the name and value of the original key but set the parent of the clone to the parent argument passed to this method. }
/// <summary> /// Moves the selected node to a new parent node/to the root. /// </summary> /// <param name="node">The node to be moved.</param> /// <param name="newParent">The new parent of the node. NOTE: If you want the node to be a root node, set this to null.</param> public static void Migrate(this VDFNode node, VDFNode newParent) { if (node.Parent != null) { node.Parent.Nodes.Remove(node); node.Parent = null; } if (newParent != null) { node.Parent = newParent; newParent.Nodes.Add(node); } }
/// <summary> /// Cleanly removes a node from the list of children of the parent node. /// </summary> /// <param name="nodes">The collection of nodes to search through.</param> /// <param name="Name">The name of the node that the method needs to find.</param> /// <param name="CaseSensitive">Indicates if the name argument and the name of the node needs to be an exact match in terms of capitalization.</param> /// <param name="FullRemovalFromTheVDFStruct">If true, the ParentVDFStructure property of the node will be set to null as well. Always set this to true if the node is a root node.</param> /// <param name="throwErrorIfNotFound">Throw an exception if the node could not be found.</param> /// <returns></returns> public static void CleanRemoveNode(this List <VDFNode> nodes, string Name, bool CaseSensitive = false, bool FullRemovalFromTheVDFStruct = false, bool throwErrorIfNotFound = false) { int i = nodes.FindBaseTokenIndex(Name, CaseSensitive, throwErrorIfNotFound); //Find our node from the nodes list by calling the FindBaseTokenIndex method and pass on the arguments. if (i == -1) //The FindBaseTokenIndex method will do the error handling for us. However, if the argument throwErrorIfNotFound is set to false, it wouldn't do that so what'll do is exit from this method if the func returns -1. { return; } VDFNode tNode = nodes[i];//cache our node. tNode.Parent = null; nodes.RemoveAt(i); if (FullRemovalFromTheVDFStruct) { tNode.ParentVDFStructure = null; } }
/// <summary> /// Moves the key to another node while making sure that the Parent property is set correctly and that the key is removed from the previous parent's key list and added to the new parent's key list. /// </summary> /// <param name="key">The key to be moved.</param> /// <param name="newParent">The new parent of the key.</param> public static void Migrate(this VDFKey key, VDFNode newParent) { if (newParent == null) { throw new ArgumentNullException("parent"); } if (key.Parent == null) { throw new NullReferenceException("The Parent property of key " + key.Name + " is set to null!"); } key.Parent.Keys.Remove(key); key.Parent = newParent; if (newParent.Keys == null) //If the newParent node's keys list is not yet created, we will have to instantiate it ourselves. { newParent.Keys = new List <VDFKey>(); } newParent.Keys.Add(key); }
/* Helper Functions for the reader */ /// <summary> /// Resets all the variables needed by the reader to perform its task. /// </summary> void ResetReaderVariables() { if (Nodes == null) { Nodes = new List <VDFNode>(); } else { Nodes.Clear(); //Just clear the nodes list if it is set already. Should be more efficient. } //Reset all the variables needed by the reader. currentNode = null; sb = null; currentMode = Mode.none; previousString = null; lineCounter = 1; characterCount = 1; }
/// <summary> /// Creates a VDF string which contains this node and its children (Keys and Nodes). /// </summary> /// <param name="delimiter">Indicates the delimiter to be appended after the name of the node, the curly brackets and the key-value pair.</param> /// <param name="StartingTabLevel">Indicates how many tab characters should be appended at the beginning of the name of the node, the curly brackets and the key-value pair.</param> /// <returns></returns> public string ToString(Delimiters delimiter, int StartingTabLevel = 0) { string strDelimiter = Helper.DelimiterEnumToString(delimiter); //Convert the selected delimiter to its String Value StringBuilder sb = new StringBuilder(); if (Nodes != null) { for (int i = 0; i < Nodes.Count; ++i) { VDFNode node = Nodes[i]; //Cache the node we are currently working on. if (i == Nodes.Count - 1) //We are on the final node. Set the strDelimiter to "" or empty so that we don't have a useless character at the end of the string. { strDelimiter = ""; } sb.Append(node.ToString(delimiter, StartingTabLevel) + strDelimiter); //We must make sure that the child nodes have the same styling as their parent node so pass on the delimiter and the starting tab level. } } return(sb.ToString()); }
/// <summary> /// Creates a copy of the node and its elements. /// </summary> /// <param name="node">The Node to be duplicated.</param> /// <param name="parent">Indicates which node will parent the duplicate node. Set it to null if you want the duplicate node to be a root node.</param> /// <returns></returns> public static VDFNode Duplicate(this VDFNode node, VDFNode parent, VDFData parentVDFStructure) { VDFNode newNode = new VDFNode(node.Name, parentVDFStructure, parent); //Create our clone node and set it's name to the name of the original node but set the parent to the one specified by the calling method. //Do a null check for the Nodes and Keys List of the original node. If they aren't null, loop through the elements of the lists and call the duplicate method for each elements. Make sure to set their parent to the clone node. if (node.Nodes != null) { foreach (VDFNode curNode in node.Nodes) { newNode.Nodes.Add(curNode.Duplicate(newNode, parentVDFStructure)); } } if (node.Keys != null) { foreach (VDFKey curKey in node.Keys) { newNode.Keys.Add(curKey.Duplicate(newNode)); } } return(newNode); }
/// <summary> /// Creates a VDF Data Structure and adds a single node to it. NOTE: This clears the current list of root nodes. /// </summary> /// <param name="nodeToAdd">The node to be added to the Nodes List.</param> public void LoadData(VDFNode nodeToAdd) { Nodes = new List <VDFNode>(1); //We are adding nodeToAdd to the list so let's explicitly instantiate a List<Node> with the length of 1 element. nodeToAdd.ParentVDFStructure = this; Nodes.Add(nodeToAdd); }
/// <summary> /// Creates a VDF Data Structure and adds a single node to it. /// </summary> /// <param name="nodeToAdd">The node to be added to the Nodes List.</param> public VDFData(VDFNode nodeToAdd) { LoadData(nodeToAdd); }
/// <summary> /// Parses a character array into VDF Data. NOTE: This clears the current list of root nodes. /// </summary> /// <param name="stream">The character array to be parsed.</param> public void LoadData(char[] stream) { if (stream.Length < SMALLEST_VDFDATA.Length) //Check if the length of the char array is equal to or larger than the smallest VDF data structure possible. If it isn't, we can safely assume that it is not a VDF data thus we can exit from the constructor. { throw new VDFStreamException("Provided data is not a valid VDF Data structure!"); } ResetReaderVariables(); int i = 0; while (i < stream.Length) //Loop through the our characters array { char curChar = stream[i]; //To avoid confusion, let's create a variable that will hold the current character we are working with. if (curChar == '\n') //Check if the current character is a new line. If it is, we will try to create a new token or key from the current contents of our stringbuilder and previousString { ++lineCounter; //We should keep track of the line that we are currently in case we have some errors. characterCount = 0; //We should also keep track of the position of the character that we are working on in the line (Not in the stream array) currentMode = Mode.none; //We can consider the new line as a delimiter as it is a whitespace as well. We set the mode to none. TryNewTokenOrKey(false); //Everytime we hit a delimiter, we must always try to create a new token or key from our stringbuilder and previousString. goto IterateI; //Add 1 to both i and characterCount then proceed to the next iteration. } else if (curChar == '\\' && currentMode != Mode.comment && currentMode != Mode.squareBracketTokens) //We check if the current character is a '\' which is the beginning of an escape character. We must also make sure that we do not check this if we are in a comment block or inside square brackets. { char nextChar = Helper.Peek(i + 1, stream); //Check what the next character is by using our Peek Helper method. if (nextChar == '\0') //If next character is null. (Normally if we reached the end of the stream.) { throw new VDFStreamException("Incomplete escape character detected!", lineCounter, characterCount); } if (sb == null) { sb = new StringBuilder(); } sb.Append(Helper.ParseSecondPartOfEscapeChar(nextChar, lineCounter, characterCount + 1)); //Use our Helper method to convert the escape character to its Human readable character counterpart. It also checks if it is valid. If it isn't, it throws an error. i += 2; //Since we already looked at the next character. No point in looking at it again. We just proceed to the character after it. characterCount += 2; //Make sure that we add 2 not 1 to the character counter. continue; } if (currentMode == Mode.comment) //If we are inside a comment block, no need to do anything else until we hit a new line. { goto IterateI; } else if (currentMode == Mode.squareBracketTokens) //If we have detected a left square bracket beforehand and we haven't detected a right square bracket yet. { if (curChar == ']') //If our current character is the right square bracket. Reset the currentMode back to normal. { currentMode = Mode.none; } goto IterateI; } else if (currentMode == Mode.insideDoubleQuotes) //If we are inside a double quote. { if (curChar == '"') //If we are already found a '"' before and we found it again, it means that we are closing a token. { currentMode = Mode.none; //Set the mode back to none as we aren't inside a token anymore. TryNewTokenOrKey(true); //Anything inside the double quotes is a token so we set the MustHaveNewToken argument to true. goto IterateI; //We know what our character is and we handled it. No need to do anything else so we proceed to the next character. } } else //If we are neither inside a double quote nor a comment block. { if (curChar == '"') //If we aren't inside a double quote already and we found '"'. { currentMode = Mode.insideDoubleQuotes; TryNewTokenOrKey(false); //Lets try a new token or key as the contents of the Stringbuilder might not have been turn into a token yet. (e.g. It wasn't inside a double quote.) sb = new StringBuilder(); } else if (curChar == '[') //Sometimes VDF files have bracketed tokens after a token. There's no information about what these do in the steam documentation. Ignore it for now. { currentMode = Mode.squareBracketTokens; TryNewTokenOrKey(false); } else if (curChar == '/' && Helper.Peek(i + 1, stream) == '/') //If we our current character is '/' and the next character is '/' as well, then we are in a comment block. { currentMode = Mode.comment; //Set the mode to comment so any succeeding characters will be ignored until we are in a new line. TryNewTokenOrKey(false); //Try a new token or key. The contents of the Stringbuilder and/or previousString might not have been set yet. Normally, it's because the previous characters weren't inside a double quote. i += 2; //We already looked at the next character so we skip it and proceed to the character following it. characterCount += 2; //Add 2 to the character count as well. continue; } else if (curChar == '{') { if (previousString == null) //Let's first check if the StringBuilder hasn't been flushed yet. { if (sb == null) //This is needed in the case that there's no previous word/character before '{' that isn't a part of a key already. Basically, our node doesn't have a name which isn't good. { throw new VDFStreamException("Node name is set to null!", lineCounter, characterCount); } SetPreviousStringToStringBuilder(); } VDFNode newNode = new VDFNode(previousString, this); //If we detected a '{', it means we are creating a new node so let's do it. previousString = null; //We already processed the previousString and it's the name of our node. if (currentNode != null) //Check if we are already inside another node. { newNode.Parent = currentNode; //If we are, then the node we are creating is the children of our current node so let's set the parent reference of the new node to our current node. currentNode.Nodes.Add(newNode); //Add new node to the nodes of the parent node } else { Nodes.Add(newNode); //If we aren't, it means that the node we are creating is a root node so we have to add the new node to the nodes list of this class. } currentNode = newNode; //WWe are now inside a child node } else if (curChar == '}') { TryNewTokenOrKey(false); //First, we must make sure that the Stringbuilder and the previousString has been taken care of. if (currentNode.Parent != null) { currentNode = currentNode.Parent; //Now we check if our current node has a parent. If it does, we set the current node that we are working on to the parent node. } else { currentNode = null; //If it doesn't have any parent, it means that the node we are working on is a root node. We have to make sure that we set the currentNode to null as we have already reached the end of this current node. } } else if (char.IsWhiteSpace(curChar)) { TryNewTokenOrKey(false); //Since whitespaces are delimiters, we need to check if the Stringbuilder and the previousString has been taken care of. Sometimes, it isn't due to the previous word/characters before this whitespace isn't inside a double quotation. } else { goto AppendChar; //If the character isn't any of the above, then it's part of a token so we append it to our StringBuilder. } goto IterateI; } AppendChar: if (sb == null) { sb = new StringBuilder(); } sb.Append(curChar); IterateI: ++i; //Add 1 to i to go to the next character. ++characterCount; //Add 1 to the character counter as well in case we have any errors. } if (currentNode != null) { throw new VDFStreamException("\"}\" expected.", lineCounter, characterCount); } }