/// <param name="persistentField">A persitent field descriptor.</param> /// <param name="valueType">A type to handle. If field is a collection then this type is a type of /// the collection's item.</param> /// <param name="simpleTypeProtoType">A proto that handles (de)serializing (in)to a simple string. /// if this proto cannot handle <paramref name="valueType"/> then the type will be attempted to be /// handled as a complex type.</param> internal OrdinaryFieldHandler( PersistentField persistentField, Type valueType, Type simpleTypeProtoType) { this.persistentField = persistentField; this.valueType = valueType; this.simpleTypeProto = Activator.CreateInstance(simpleTypeProtoType) as AbstractOrdinaryValueTypeProto; }
/// <param name="fieldInfo">An annotated field metadata.</param> /// <param name="fieldAttr">An annotation of the field.</param> public PersistentField(FieldInfo fieldInfo, PersistentFieldAttribute fieldAttr) { this.fieldInfo = fieldInfo; cfgPath = fieldAttr.path; var ordinaryType = fieldInfo.FieldType; if (fieldAttr.collectionTypeProto != null) { collectionProto = Activator.CreateInstance(fieldAttr.collectionTypeProto, new[] { fieldInfo.FieldType }) as AbstractCollectionTypeProto; ordinaryType = collectionProto.GetItemType(); } simpleTypeProto = Activator.CreateInstance(fieldAttr.ordinaryTypeProto) as AbstractOrdinaryValueTypeProto; isCustomSimpleType = typeof(IPersistentField).IsAssignableFrom(ordinaryType); // Determine if field's or collection item's type is a class (compound type). isCompound = !simpleTypeProto.CanHandle(ordinaryType) && !isCustomSimpleType && (ordinaryType.IsValueType && !ordinaryType.IsEnum || // IsStruct ordinaryType.IsClass && ordinaryType != typeof(string) && !ordinaryType.IsEnum); // IsClass if (!isCompound && !simpleTypeProto.CanHandle(ordinaryType) && !isCustomSimpleType) { Debug.LogErrorFormat( "Proto {0} cannot handle value in field {1}.{2}", ordinaryType.FullName, fieldInfo.DeclaringType.FullName, fieldInfo.Name); isDisabled = true; } // For a compound type retrieve all its persisted fields. if (isCompound && !isDisabled) { // Ignore static fields of the compound type since it can be used by multiple persistent // fields or as an item in a collection field. // Also, ignore groups in the compound types to reduce configuration complexity. compoundTypeFields = PersistentFieldsFactory.GetPersistentFields( ordinaryType, false /* needStatic */, true /* needInstance */, null /* group */) // Parent nodes have to be handled before children! .OrderBy(x => string.Join("/", x.cfgPath)) .ToArray(); // Disable if cannot deal with the field anyways. if (compoundTypeFields.Length == 0 && !typeof(IConfigNode).IsAssignableFrom(ordinaryType)) { Debug.LogErrorFormat( "Compound type in field {0}.{1} has no persistent fields and doesn't implement" + " IConfigNode", fieldInfo.DeclaringType.FullName, fieldInfo.Name); isDisabled = true; } } }
/// <summary> /// Stores a value of arbitrary type <typeparamref name="T"/> into a config node. /// </summary> /// <param name="node">A node to set data in.</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 value to store. The <paramref name="typeProto"/> handler must know how /// to convert value's type into 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> /// <typeparam name="T">The value type to store. Type proto must be able to handle it. /// </typeparam> /// <exception cref="ArgumentException">If type cannot be handled by the proto.</exception> public static void SetValueByPath <T>(ConfigNode node, string[] pathKeys, 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 = typeProto.SerializeToString(value); SetValueByPath(node, pathKeys, strValue); }
/// <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> /// 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="path">A string path to the node. Path components should be separated by '/' /// symbol.</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 path, ref T value, AbstractOrdinaryValueTypeProto typeProto = null) { return(GetValueByPath(node, path.Split('/'), ref value, typeProto)); }
/// <summary> /// Stores a value of arbitrary type <typeparamref name="T"/> into a config node. /// </summary> /// <param name="node">A node to set data in.</param> /// <param name="path">A string path to the node. Path components should be separated by '/' /// symbol.</param> /// <param name="value">A value to store. The <paramref name="typeProto"/> handler must know how /// to convert the value into 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> /// <typeparam name="T">The value type to store. Type proto must be able to handle it. /// </typeparam> /// <exception cref="ArgumentException">If type cannot be handled by the proto.</exception> public static void SetValueByPath <T>(ConfigNode node, string path, T value, AbstractOrdinaryValueTypeProto typeProto = null) { SetValueByPath(node, path.Split('/'), value, typeProto); }
/// <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="path"> /// The path to the node. The path components should be separated by '/' symbol. /// </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 path, AbstractOrdinaryValueTypeProto typeProto = null) where T : struct { return(GetValueByPath <T>(node, ConfigAccessor.StrToPath(path), typeProto)); }