/// <summary>Restores a collection from the config node.</summary> /// <remarks>This method never throws.</remarks> /// <param name="node">The node to read the state from.</param> /// <param name="instance"> /// The owner of the field. It can be <c>null</c> for the static fields. /// </param> void ReadCollectionFromConfig(ConfigNode node, object instance) { var value = fieldInfo.GetValue(instance); if (value == null) { // Collections are the complex objects, they must exist in order to be restored. DebugEx.Warning("Skip reading collection field {0}.{1} due to it's not initalized", fieldInfo.DeclaringType.FullName, fieldInfo.Name); return; } collectionProto.ClearItems(value); if (isCompound) { // For compound items read nodes and have them parsed. var itemCfgs = ConfigAccessor.GetNodesByPath(node, cfgPath); if (itemCfgs != null) { foreach (var itemCfg in itemCfgs) { var itemValue = Activator.CreateInstance(collectionProto.GetItemType()); DeserializeCompoundFieldsFromNode(itemCfg, itemValue); collectionProto.AddItem(value, itemValue); } } } else { // For ordinary items read strings and have them parsed. var itemCfgs = ConfigAccessor.GetValuesByPath(node, cfgPath); if (itemCfgs != null) { foreach (var itemCfg in itemCfgs) { try { object itemValue; if (isCustomSimpleType) { itemValue = Activator.CreateInstance(collectionProto.GetItemType()); ((IPersistentField)itemValue).ParseFromString(itemCfg); } else { itemValue = simpleTypeProto.ParseFromString(itemCfg, collectionProto.GetItemType()); } collectionProto.AddItem(value, itemValue); } catch (Exception ex) { DebugEx.Error("Cannot parse value \"{0}\" as {1}: {2}", itemCfgs, collectionProto.GetItemType().FullName, ex.Message); } } } } }
/// <summary> /// Reads a value of an arbitrary type <typeparamref name="T"/> from the config node. /// </summary> /// <param name="node">The node to read data from.</param> /// <param name="pathKeys"> /// The array of values that makes the full path. The first node in the array is the top most /// component of the path. /// </param> /// <param name="typeProto"> /// A proto that can parse values of type <typeparamref name="T"/>. If not set, then /// <see cref="StandardOrdinaryTypesProto"/> is used. /// </param> /// <returns>The parsed value or <c>null</c> if not found.</returns> /// <typeparam name="T"> /// The value type to write. The <paramref name="typeProto"/> instance must be able to handle it. /// </typeparam> /// <exception cref="ArgumentException">If type cannot be handled by the proto.</exception> /// <seealso cref="SetValueByPath<T>(ConfigNode, string[], T, KSPDev.ConfigUtils.AbstractOrdinaryValueTypeProto)"/> public static T?GetValueByPath <T>( ConfigNode node, string[] pathKeys, AbstractOrdinaryValueTypeProto typeProto = null) where T : struct { if (typeProto == null) { typeProto = standardTypesProto; } if (!typeProto.CanHandle(typeof(T))) { throw new ArgumentException(string.Format( "Proto {0} cannot handle type {1}", typeProto.GetType(), typeof(T))); } var strValue = ConfigAccessor.GetValueByPath(node, pathKeys); return(strValue == null ? null : (T?)typeProto.ParseFromString(strValue, typeof(T))); }
/// <summary> /// Reads a value of arbitrary type <typeparamref name="T"/> from a config node. /// </summary> /// <param name="node">A node to read data from.</param> /// <param name="pathKeys">An array of values that makes the full path. First node in the array is /// the top most component of the path.</param> /// <param name="value">A variable to read value into. The <paramref name="typeProto"/> handler /// must know how to convert value's type from string.</param> /// <param name="typeProto">A proto capable to handle the type of <paramref name="value"/>. If not /// set then <see cref="StandardOrdinaryTypesProto"/> is used.</param> /// <returns><c>true</c> if value was successfully read and stored.</returns> /// <typeparam name="T">The value type to read. Type proto must be able to handle it. /// </typeparam> /// <exception cref="ArgumentException">If type cannot be handled by the proto.</exception> public static bool GetValueByPath <T>(ConfigNode node, string[] pathKeys, ref T value, AbstractOrdinaryValueTypeProto typeProto = null) { if (typeProto == null) { typeProto = standardTypesProto; } if (!typeProto.CanHandle(typeof(T))) { throw new ArgumentException(string.Format( "Proto {0} cannot handle type {1}", typeProto.GetType(), typeof(T))); } var strValue = GetValueByPath(node, pathKeys); if (strValue == null) { return(false); } value = (T)typeProto.ParseFromString(strValue, typeof(T)); return(true); }
/// <summary>Converts a value from config into an actual fiel's value.</summary> /// <param name="cfgValue">A string if value can be handled by the proto or /// <see cref="ConfigNode"/> if type is compound.</param> /// <returns>Field's value.</returns> internal object DeserializeValue(object cfgValue) { if (simpleTypeProto.CanHandle(valueType)) { try { return(simpleTypeProto.ParseFromString((string)cfgValue, valueType)); } catch (ArgumentException ex) { Logger.logError(string.Format( "Cannot parse value \"{0}\" as {1}: {2}", cfgValue, valueType, ex.Message)); return(null); } } if (IsCompound()) { var instance = Activator.CreateInstance(valueType); persistentField.DeserializeCompoundFieldsFromNode((ConfigNode)cfgValue, instance); return(instance); } Logger.logError("{0} doesn't know how to parse value type: {1}", GetType(), valueType); return(null); }
/// <summary>Reads field from a config node.</summary> /// <param name="node">A node to read state from.</param> /// <param name="instance">An owner of the field. Can be <c>null</c> for static fields.</param> public void ReadFromConfig(ConfigNode node, object instance) { var value = fieldInfo.GetValue(instance); if (collectionProto != null) { // For collection field use existing object and restore its items. if (value == null) { Debug.LogWarningFormat("Skip reading collection field {0}.{1} due to it's not initalized", fieldInfo.DeclaringType.FullName, fieldInfo.Name); return; } collectionProto.ClearItems(value); if (isCompound) { // For compound items read nodes and have them parsed. var itemCfgs = ConfigAccessor.GetNodesByPath(node, cfgPath); if (itemCfgs != null) { foreach (var itemCfg in itemCfgs) { var itemValue = Activator.CreateInstance(collectionProto.GetItemType()); DeserializeCompoundFieldsFromNode(itemCfg, itemValue); collectionProto.AddItem(value, itemValue); } } } else { // For ordinary items read strings and have them parsed. var itemCfgs = ConfigAccessor.GetValuesByPath(node, cfgPath); if (itemCfgs != null) { foreach (var itemCfg in itemCfgs) { try { object itemValue; if (isCustomSimpleType) { itemValue = Activator.CreateInstance(collectionProto.GetItemType()); ((IPersistentField)itemValue).ParseFromString(itemCfg); } else { itemValue = simpleTypeProto.ParseFromString(itemCfg, collectionProto.GetItemType()); } collectionProto.AddItem(value, itemValue); } catch (Exception ex) { Debug.LogErrorFormat("Cannot parse value \"{0}\" as {1}: {2}", itemCfgs, collectionProto.GetItemType().FullName, ex.Message); } } } } } else { // For ordinary field just restore value and assign it to the field. if (isCompound) { if (value != null) { DeserializeCompoundFieldsFromNode(ConfigAccessor.GetNodeByPath(node, cfgPath), value); } else { Debug.LogWarningFormat("Skip reading compound field {0}.{1} due to it's not initalized", fieldInfo.DeclaringType.FullName, fieldInfo.Name); } } else { var cfgValue = ConfigAccessor.GetValueByPath(node, cfgPath); if (cfgValue != null) { try { object fieldValue; if (isCustomSimpleType) { // Prefer the existing instance of the field value when available. fieldValue = value ?? Activator.CreateInstance(fieldInfo.FieldType); ((IPersistentField)fieldValue).ParseFromString(cfgValue); } else { fieldValue = simpleTypeProto.ParseFromString(cfgValue, fieldInfo.FieldType); } fieldInfo.SetValue(instance, fieldValue); } catch (Exception ex) { Debug.LogErrorFormat("Cannot parse value \"{0}\" as {1}: {2}", cfgValue, fieldInfo.FieldType.FullName, ex.Message); } } } } }