コード例 #1
0
        /// <summary>
        /// Primary internal method to build the KSP data string. Contains version specific rules and formatting
        /// </summary>
        /// <param name="jobj">JSon Object data to use.</param>
        /// <param name="tabCount">starting point for indented tabs (method will further indent children through recursive calls</param>
        /// <param name="objectName">name of data object for header</param>
        /// <returns>KSP data string</returns>
        private static void BuildKspData(KspDataObject dataObj, StreamWriter writer, int tabCount = 0)
        {
            var tabIndent = GetTabs(tabCount);

            // Write out any property values
            foreach (var prop in dataObj.Values)
            {
                var key = prop.Name;

                if (key.Contains("KCOM-"))
                {
                    writer.WriteLine("// " + prop.Value.ToString().Trim());
                }
                else
                {
                    writer.WriteLine(tabIndent + key + " = " + prop.Value.ToString().Trim());
                }
            }

            // Write out any object children using recursive calls.
            foreach (var obj in dataObj.Children)
            {
                writer.WriteLine(tabIndent + obj.Name);
                writer.WriteLine(tabIndent + '{' + Environment.NewLine);

                BuildKspData(obj, writer, tabCount + 1);

                writer.WriteLine(Environment.NewLine + tabIndent + '}');
            }
        }
コード例 #2
0
        /// <summary>
        /// Internal class that handles converting KSP data to JSON. Recursively calls itself to handle nested elements. Contains version specific rules and code.
        /// </summary>
        /// <param name="root">root data object (data context is stripped)</param>
        /// <param name="tabCount">starting tab index for proper formatting</param>
        /// <returns>JSON string</returns>
        private JObject BuildJson(KspDataObject root)
        {
            var jobj = new JObject();

            BuildProerties(root, jobj);

            var childNames = root.Children.Select(o => o.Name).Distinct();

            // Loop through child names rather than the full collection to allow for array building logic
            for (var n = 0; n < childNames.Count(); n++)
            {
                var childName    = childNames.ElementAt(n);
                var childObjects = root.Children.Where(o => o.Name == childName);

                if (childObjects.Count() > 1) // Handle collection of children
                {
                    var collectionName = childName;
                    var array          = new object[childObjects.Count()];

                    for (var c = 0; c < childObjects.Count(); c++)
                    {
                        array[c] = BuildJson(childObjects.ElementAt(c));
                    }

                    jobj[collectionName] = JArray.FromObject(array);
                }
                else if (childObjects.Count() == 1) // Handle uniquely named children
                {
                    var childObject = childObjects.ElementAt(0);

                    // Recursive call to build child
                    jobj[childName] = BuildJson(childObject);
                }
            }

            return(jobj);
        }
コード例 #3
0
 /// <summary>
 /// Initializes a new instance of the <see cref="KspDataContext" /> class.
 /// </summary>
 public KspDataContext()
 {
     Data = new KspDataObject();
 }
コード例 #4
0
        /// <summary>
        /// Internal parsing method, recursively calls itself as needed
        /// </summary>
        /// <param name="data">data to parse</param>
        /// <param name="dataObj">data object to populate</param>
        private static void ParseData(IList <string> data, KspDataObject dataObj)
        {
            for (var i = 0; i < data.Count; i++)
            {
                // I came across a type of reader that does this for me, need to test it out. For now this works well.
                var lastLine    = i > 0 ? data[i - 1] : string.Empty;
                var currentLine = data[i];
                var nextLine    = i + 1 < data.Count ? data[i + 1] : string.Empty;

                // Check if the line is a header line. All lines in a KSP data file contain one of the characters below
                // with the exception of the header name for a new object.
                if (currentLine.Trim().StartsWith("//"))
                {
                    dataObj.Values.Add(new KspDataField {
                        Name = "KCOM-" + Guid.NewGuid().ToString(), Value = currentLine.Replace("// ", "")
                    });
                }
                else if (!currentLine.Contains('=') && !currentLine.Contains('{') && !currentLine.Contains('}') && !String.IsNullOrEmpty(currentLine.Trim()))
                {
                    var objectName = currentLine.Trim();
                    var objectData = new List <string>();

                    // Tracking value to find the end of an object.
                    var depth = 0;

                    // Loop through the data looking for the end of the object
                    for (var x = i + 2; x < data.Count - 1; x++)
                    {
                        if (data[x].Trim().Equals("}"))
                        {
                            if (depth == 0)
                            {
                                break;
                            }

                            depth--;
                        }
                        else if (data[x].Trim().Equals("{"))
                        {
                            depth++;
                        }

                        objectData.Add(data[x]);
                    }

                    // Push the primary loop counter up to account for the object data we just pulled out
                    i += objectData.Count + 2;

                    if (objectData.Count > 0)
                    {
                        var child = new KspDataObject {
                            Name = objectName
                        };

                        // Recursive call to de-serialize child
                        ParseData(objectData, child);
                        dataObj.Children.Add(child);
                    }
                    else
                    {
                        // In this case we have an object with no children and no properties.
                        // This is another thing we do to try and remain compatible. There are a number of empty objects that are stored in the
                        // KSP data file. To remain as close to 100% we even track these empty objects to make sure they are shown to the consumer and
                        // written back on save.
                        dataObj.Children.Add(new KspDataObject {
                            Name = objectName
                        });
                    }
                }
                else if (currentLine.Contains('=')) // If the line is a value assignment (in ksp files "=" is only value assignment for fields. Child object names act as the field name for complex objects.
                {
                    var comma = string.Empty;
                    if ((!(i + 2 > data.Count) && nextLine.Contains('=')) || (!nextLine.Contains('=') && !string.IsNullOrEmpty(nextLine)))
                    {
                        comma = ",";
                    }

                    var lineParts = currentLine.Split('=');
                    var leftSide  = lineParts[0].Trim();
                    var rightSide = lineParts.Count() > 1 ? lineParts[1].Trim() : string.Empty;

                    dataObj.Values.Add(new KspDataField {
                        Name = leftSide, Value = rightSide
                    });
                }
            }
        }
コード例 #5
0
        /// <summary>
        /// Primary internal method to build the KSP data string. Contains version specific rules and formatting
        /// </summary>
        /// <param name="jobj">JSon Object data to use.</param>
        /// <param name="root">data object to populate</param>
        private static void BuildKspData(JObject jobj, KspDataObject root)
        {
            foreach (var obj in jobj)
            {
                // THe "-KSPD-MULTI-" substring handles for the odd collection type where multiple fields of the same name exist in the same object in the KSP data.
                // This is most often seen with the "sym" property but can be found elsewhere.
                // Ideas for a better way to handle this are welcome.
                var key = obj.Key.Contains("-KMULTI-")
                    ? obj.Key.Substring(0, obj.Key.IndexOf("-KMULTI-"))
                    : obj.Key;

                switch (obj.Value.Type)
                {
                case JTokenType.String:
                {
                    root.Values.Add(new KspDataField()
                        {
                            Name = key, Value = obj.Value.ToString()
                        });
                    break;
                }

                case JTokenType.Object:
                {
                    var childObj = new KspDataObject()
                    {
                        Name = key
                    };

                    BuildKspData(obj.Value.ToObject <JObject>(), childObj);
                    root.Children.Add(childObj);
                    break;
                }

                case JTokenType.Array:
                {
                    var itemName = key;

                    if (obj.Value.Count() > 0)
                    {
                        switch (obj.Value[0].Type)
                        {
                        case JTokenType.Object:
                        {
                            foreach (var item in obj.Value)
                            {
                                var childObj = new KspDataObject()
                                {
                                    Name = itemName
                                };

                                BuildKspData(item.ToObject <JObject>(), childObj);
                                root.Children.Add(childObj);
                            }
                            break;
                        }

                        case JTokenType.String:
                        {
                            root.Values.Add(
                                new KspDataField()
                                    {
                                        Name  = itemName,
                                        Value = BuildArrayField(obj.Value)
                                    });
                            break;
                        }

                        default:
                        {
                            root.Values.Add(
                                new KspDataField()
                                    {
                                        Name  = itemName,
                                        Value = BuildArrayField(obj.Value)
                                    });
                            break;
                        }
                        }
                    }
                    break;
                }
                }
            }
        }
コード例 #6
0
        /// <summary>
        /// Builds the child properties for the JObject
        /// </summary>
        /// <param name="root">data object to use</param>
        /// <param name="jobj">JObject instance to populate</param>
        private static void BuildProerties(KspDataObject root, JObject jobj)
        {
            var fieldNames = root.Values.Select(f => f.Name).Distinct();

            foreach (var fieldName in fieldNames)
            {
                var fields = root.Values.Where(f => f.Name == fieldName);

                if (fields.Count() == 1)
                {
                    var field = fields.ElementAt(0);
                    if (!field.Value.Contains(',') ||
                        fieldName.ToUpper().Equals("CDATA") ||
                        fieldName.ToUpper().Equals("DESCRIPTION") ||
                        fieldName.ToUpper().Equals("TITLE") ||
                        fieldName.ToUpper().Contains("KCOM-"))
                    {
                        jobj[field.Name] = field.Value;
                    }
                    else
                    {
                        try
                        {
                            jobj[field.Name] = JArray.Parse(BuildArrayField(field.Value));
                        }
                        catch (Exception ex)
                        {
                            throw new KerbalDataException("An error has occured while converting de-serialized data to a JSON object", ex);
                        }
                    }
                }
                else if (fields.Count() > 1)
                {
                    // Special handling for the strange case of multiple properties of the same name as children of the same object. JSON does not support this so we tag the name with the
                    // substring "-KSPD-MULTI-".
                    // TODO: Wrapper classes need to somehow handle for this and display data in a more reasonable manner rather than relying on consumers to use the "special" substring.
                    for (var f = 0; f < fields.Count(); f++)
                    {
                        var field = fields.ElementAt(f);
                        if (!field.Value.Contains(',') ||
                            fieldName.ToUpper().Equals("CDATA") ||
                            fieldName.ToUpper().Equals("DESCRIPTION") ||
                            fieldName.ToUpper().Equals("TITLE") ||
                            fieldName.ToUpper().Contains("KCOM-"))
                        {
                            jobj[field.Name + "-KMULTI-" + Guid.NewGuid().ToString()] = field.Value;
                        }
                        else
                        {
                            try
                            {
                                jobj[field.Name + "-KMULTI-" + Guid.NewGuid().ToString()] =
                                    JArray.Parse(BuildArrayField(field.Value));
                            }
                            catch (Exception ex)
                            {
                                throw new KerbalDataException("An error has occured while converting de-serialized data to a JSON object", ex);
                            }
                        }
                    }
                }
            }
        }