/// <summary> /// Builds the string for an array object value /// </summary> /// <param name="arrayObjectValue">EntityPropertyObjectValue object of the value of the array</param> /// <param name="arrayItemIndex">index of this item in the array</param> /// <param name="indentationLevel">current indentation level</param> /// <returns>the string for the array object value></returns> internal static string GetArrayObjectValueString(EntityPropertyObjectValue arrayObjectValue, int arrayItemIndex, uint indentationLevel) { StringBuilder objectString = new StringBuilder(); objectString.Append(IndentText("item[", indentationLevel)).Append(arrayItemIndex).Append("] = {").Append("\n"); indentationLevel++; foreach (var value in arrayObjectValue.Value) { if (value == null) { continue; } if (value.Value.GetType() == typeof(EntityPropertyArrayValue)) { objectString.Append(GetArrayString(value, ref indentationLevel)).Append("\n"); } else if (value.Value.GetType() == typeof(EntityPropertyObjectValue)) { objectString.Append(GetObjectString(value, ref indentationLevel)).Append("\n"); } else { objectString.Append(GetPropertyString(value, indentationLevel)).Append("\n"); } } indentationLevel--; objectString.Append(IndentText("}", indentationLevel)); return(objectString.ToString()); }
/// <summary> /// Parses any kind of property from an entityDef into an EntityProperty object /// </summary> /// <param name="propertyText">text containing the property</param> /// <param name="currentLineNumber">current line number reference in the file</param> /// <param name="isFromPropertiesSection">indicates wether or not this property comes from the properties section</param> /// <returns>an EntityProperty object containing the parsed data from the property</returns> internal EntityProperty ParseEntityProperty(string propertyText, ref int currentLineNumber, bool isFromPropertiesSection = false) { // Split the property definition into lines (if it has any) EntityProperty entityProperty = new EntityProperty(); string[] propertyLines = propertyText.Split('\n'); currentLineNumber -= propertyLines.Length - 1; for (int i = 0; i < propertyLines.Length; i++) { propertyLines[i] = propertyLines[i].Trim(); if (i > 0) { currentLineNumber++; } // Skip closing brackets if (propertyLines[i].Equals("}")) { continue; } // Again, first we need to determine which kind of property this is to properly parse it string[] propertyParts = propertyLines[i].Split('='); if (propertyParts.Length != 2 || string.IsNullOrEmpty(propertyParts[1].Trim())) { Errors.Add(string.Format("Line {0}: bad property declaration '{1}'", currentLineNumber, propertyLines[i].Trim())); continue; } propertyParts[0] = propertyParts[0].TrimEnd(); propertyParts[1] = propertyParts[1].Trim(); // This is a "single-line" property if (propertyParts[1][propertyParts[1].Length - 1] != '{') { propertyParts[0] = propertyParts[0].Trim(); propertyParts[1] = propertyParts[1].Trim(); // Check if the property name is quoted if (propertyParts[0].StartsWith("\"") && propertyParts[0].EndsWith("\"")) { entityProperty.IsQuoted = true; propertyParts[0] = propertyParts[0].Remove(0, 1); propertyParts[0] = propertyParts[0].Remove(propertyParts[0].Length - 1, 1); } if (!IsValidPropertyName(propertyParts[0])) { Errors.Add(string.Format("Line {0}: invalid property name '{1}'", currentLineNumber, propertyParts[0])); continue; } entityProperty.Name = propertyParts[0]; try { // Check for and remove semicolon at the end for the value parser if (propertyParts[1][propertyParts[1].Length - 1] != ';' && !isFromPropertiesSection) { Errors.Add(string.Format("Line {0}: missing ';'", currentLineNumber)); } else if (!isFromPropertiesSection) { propertyParts[1] = propertyParts[1].Remove(propertyParts[1].Length - 1, 1); } entityProperty.Value = ParseEntityPropertyValue(propertyParts[1].Trim(), ref currentLineNumber); if (entityProperty.Value == null) { continue; } } catch (Exception) { throw; } } else { // Check if the "Important" flag is present if (propertyParts[1].Length > 1 && propertyParts[1][0] == '!') { entityProperty.Important = true; } // We will loop through all the properties in this object/array // and do recursion to get the values List <EntityProperty> properties = new List <EntityProperty>(); EntityProperty arrayNumProperty = null; bool isArray = false; int arrayNumPropertyLineNumber = 0; int openingBracketCount = 1; int closingBracketCount = 0; currentLineNumber++; // Get everything until the block is closed for (int j = i + 1; j < propertyLines.Length - 1; j++, currentLineNumber++, i = j) { if (string.IsNullOrEmpty(propertyLines[j])) { continue; } if (propertyLines[j][propertyLines[j].Length - 1] == '{') { openingBracketCount++; } if (propertyLines[j][propertyLines[j].Length - 1] == '}') { closingBracketCount++; } if (openingBracketCount == closingBracketCount) { currentLineNumber++; break; } // Check if this is a single property or an object // Everything inside the entityDef must be a property of some kind // We will get all the text of the property and pass it to the property parser EntityProperty nestedEntityProperty = new EntityProperty(); string[] currentPropertyText = propertyLines[j].Split('='); if (currentPropertyText.Length != 2) { if (propertyLines[j].Equals("{") || propertyLines[j].Equals("}")) { continue; } else { Errors.Add(string.Format("Line {0}: unexpected '{1}'", currentLineNumber, propertyLines[j])); continue; } } currentPropertyText[0] = currentPropertyText[0].TrimEnd(); currentPropertyText[1] = currentPropertyText[1].Trim(); if (string.IsNullOrEmpty(currentPropertyText[1])) { Errors.Add(string.Format("Line {0}: missing property value", currentLineNumber)); continue; } // Determine what kind of property this is to get all it's text // This is probably a "single-line" property, so we can just pass the whole line to the property parser if (currentPropertyText[1][currentPropertyText[1].Length - 1] != '{') { // Check if this is an array or an object string trimmedPropertyName = currentPropertyText[0].TrimStart(); // Get the number of items in the array // We will use it to check that the number matches with the actual // amount of items if (!isArray & trimmedPropertyName.Equals("num")) { arrayNumPropertyLineNumber = currentLineNumber; arrayNumProperty = ParseEntityProperty(propertyLines[j], ref currentLineNumber); if (arrayNumProperty != null && arrayNumProperty.Value != null && arrayNumProperty.Value.GetType() != typeof(EntityPropertyLongValue)) { Errors.Add(string.Format("Line {0}: 'num' property value must be a number", currentLineNumber)); } isArray = true; } try { nestedEntityProperty = ParseEntityProperty(propertyLines[j], ref currentLineNumber); } catch (FormatException) { throw; } } else { // Check if the "Important" flag is present if (currentPropertyText[1].Length > 1 && currentPropertyText[1][0] == '!') { nestedEntityProperty.Important = true; } // This is either an object or an array, so we'll just get the whole block // to pass it to the property parser StringBuilder currentNestedPropertyText = new StringBuilder(); int nestedOpeningBracketCount = 1; int nestedClosingBracketCount = 0; currentNestedPropertyText.Append(propertyLines[j]).Append("\n"); // Get everything until the block is closed for (int k = j + 1; j < propertyLines.Length; k++, currentLineNumber++, j = k) { if (string.IsNullOrEmpty(propertyLines[k])) { continue; } if (propertyLines[k][propertyLines[k].Length - 1] == '{') { nestedOpeningBracketCount++; currentNestedPropertyText.Append(propertyLines[k]).Append("\n"); continue; } if (propertyLines[k][propertyLines[k].Length - 1] == '}') { nestedClosingBracketCount++; } if (nestedOpeningBracketCount == nestedClosingBracketCount) { currentNestedPropertyText.Append(propertyLines[k]); currentLineNumber++; break; } currentNestedPropertyText.Append(propertyLines[k]).Append("\n"); } nestedEntityProperty = ParseEntityProperty(currentNestedPropertyText.ToString(), ref currentLineNumber); currentNestedPropertyText.Clear(); } if (nestedEntityProperty != null) { properties.Add(nestedEntityProperty); } } if (!isArray) { EntityPropertyObjectValue objectValue = new EntityPropertyObjectValue(); objectValue.Value = properties; entityProperty.Value = objectValue; } else { // If this is an array we only need the values uint elementCount = 0; EntityPropertyArrayValue arrayValue = new EntityPropertyArrayValue(); arrayValue.Values = new List <EntityPropertyValue>(); foreach (var property in properties) { if (property.Value == null || property.Name == null || property.Name.Equals("num")) { continue; } arrayValue.Values.Add(property.Value); elementCount++; } // Print a warning if the 'num' value doesn't match the array's actual element count if (arrayNumProperty != null && arrayNumProperty.Value != null && arrayNumProperty.Value.GetType() == typeof(EntityPropertyLongValue) && ((EntityPropertyLongValue)arrayNumProperty.Value).Value != elementCount) { Warnings.Add(string.Format("Line {0}: 'num' property value '{1}' doesn't match the array's actual element count '{2}'", arrayNumPropertyLineNumber, ((EntityPropertyLongValue)arrayNumProperty.Value).Value, elementCount)); } entityProperty.Value = arrayValue; } // Check if the property name is quoted if (propertyParts[0].StartsWith("\"") && propertyParts[0].EndsWith("\"")) { entityProperty.IsQuoted = true; propertyParts[0] = propertyParts[0].Remove(0, 1); propertyParts[0] = propertyParts[0].Remove(propertyParts[0].Length - 1, 1); } if (!IsValidPropertyName(propertyParts[0].Trim())) { Errors.Add(string.Format("Line {0}: invalid property name '{1}'", currentLineNumber, propertyParts[0].Trim())); return(null); } entityProperty.Name = propertyParts[0].Trim(); } } return(entityProperty); }