/// <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); } } } } }
/// <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>Returns type of an item in the colelction.</summary> /// <returns>Item's type.</returns> internal Type GetItemType() { return(collectionProto.GetItemType()); }
/// <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); } } } } }