/// <summary> /// Applies the data stored in this serialized data to the specified object using reflection. /// </summary> public void ApplyTo(object obj, List <ParsingError> errors, PrototypeParserState state) { foreach (var field in fields) { var fieldInfo = this.targetType.GetFieldData(field.Key); var value = field.Value; var sub = value as SerializedData; if (!ReferenceEquals(sub, null)) { // Field already set? value = fieldInfo.fieldInfo.GetValue(obj); // If not, create new obj if (ReferenceEquals(value, null)) { value = sub.targetType.Create(); } sub.ApplyTo(value, errors, state); } var col = value as SerializedCollectionData; if (!ReferenceEquals(col, null)) { // Field already set? value = fieldInfo.fieldInfo.GetValue(obj); CollectionOverrideAction action; if (!ReferenceEquals(value, null)) { if (!collectionOverrideActions.TryGetValue(field.Key, out action)) { action = CollectionOverrideAction.Combine; // Always default to comibining } switch (action) { case CollectionOverrideAction.Combine: value = col.CombineWithInNew(value); break; case CollectionOverrideAction.Replace: value = col.CreateCollection(); break; } } else // Write new collection { value = col.CreateCollection(); } } if (!ParsingValidation.TypeCheck(debug.TryGet(field.Key), field.Key, value, fieldInfo.fieldInfo.FieldType, filename, errors)) { continue; } fieldInfo.fieldInfo.SetValue(obj, value); } }
protected override Vector2 _Deserialize(string value, PrototypeParserState state) { string[] parts = value.Split(','); if (parts.Length < 2) { throw new FormatException("Malformed vector2 data " + value + " - Format: (x,y - . as decimal delimiter)"); } return(new Vector2(float.Parse(parts[0], CultureInfo.InvariantCulture), float.Parse(parts[1], CultureInfo.InvariantCulture))); }
protected override Type _Deserialize(string value, PrototypeParserState state) { // Create std namespace name string by prepending std namespace to value bool doStdNamespaceCheck = !value.Contains('.'); string stdNamespacePrepended = doStdNamespaceCheck ? state.parameters.standardNamespace + "." + value : null; // Look for type foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) { var t = asm.GetType(value, false, false); if (doStdNamespaceCheck && ReferenceEquals(t, null)) { t = asm.GetType(stdNamespacePrepended, false, false); } if (!ReferenceEquals(t, null)) { return(t); } } return(null); }
/// <summary> /// Actually loads the data from the previous parse <see cref="ParseFields(List{ParsingError})"/>. /// It will for every field with string data deserialize this data using <see cref="IPrototypeDataSerializer"/>. /// /// For every sub-data field (<seealso cref="ParseFields(List{ParsingError})"/>) a <see cref="SerializedData"/> object is being written to <see cref="fields"/>. /// The sub-data object will have <see cref="PrepareParse(SerializableTypeCache, XElement, string)"/>, <see cref="ParseFields(List{ParsingError})"/> and <see cref="LoadFields(List{ParsingError}, PrototypeParserState)"/> called. /// </summary> public void LoadFields(List <ParsingError> errors, PrototypeParserState state) { foreach (var xNode in xElement.Nodes()) { if (!ParsingValidation.NodeIsElement(xNode, this.filename, errors)) // Malformed XML { continue; } var xElement = xNode as XElement; var elementName = xElement.Name.LocalName; // Field unknown? if (!ParsingValidation.FieldKnown(xElement, targetType, elementName, filename, errors)) { continue; } debug.Add(elementName, xElement); var fieldData = targetType.GetFieldData(elementName); var fieldType = fieldData.serializableTypeCache; bool isCollection = SerializedCollectionData.IsCollection(fieldData.fieldInfo.FieldType); IPrototypeDataSerializer serializer = PrototypeCaches.GetBestSerializerFor(fieldData.fieldInfo.FieldType); if (!ReferenceEquals(serializer, null)) { try { if (!ParsingValidation.SerializerWasFound(xElement, serializer, elementName, targetType == null ? null : targetType.type, fieldType == null ? null : fieldType.type, filename, errors)) { continue; } fields.Add(elementName, serializer.Deserialize(fieldData.fieldInfo.FieldType, xElement, state)); } catch (Exception ex) { errors.Add(new ParsingError(ParsingErrorSeverity.ERROR, filename, -1, "Serializer threw exception on field " + elementName + " on type " + targetType.type + ":\n\n" + ex.ToString() + "\n\nSkipping field!")); } } else if (isCollection) { var col = new SerializedCollectionData(fieldData.fieldInfo.FieldType, xElement, this.filename); col.ParseAndLoadData(errors, state); fields.Add(elementName, col); // Collection override action? var collectionOverrideAttrib = xElement.Attribute(PrototypeParser.PrototypeAttributeCollectionOverrideAction); if (!ReferenceEquals(collectionOverrideAttrib, null)) { collectionOverrideActions.Set(elementName, (CollectionOverrideAction)Enum.Parse(typeof(CollectionOverrideAction), collectionOverrideAttrib.Value)); } } else { // A known serializable // Prototype reference? if (fieldData.isPrototype) { fields.Add(elementName, new SerializedPrototypeReference() { identifier = xElement.Value as string }); } else { // Determine which type to serialize var targetType = fieldData.serializableTypeCache; string typeName = fieldData.fieldInfo.Name; // Check if element explicitly overwrites the type to support polymorphism // The field type might be some base class type and the xml overwrites this type with a class extending from the base var classAttrib = xElement.Attribute(PrototypeParser.PrototypeAttributeType); if (!ReferenceEquals(classAttrib, null)) { targetType = PrototypeCaches.GetSerializableTypeCacheFor(classAttrib.Value, state.parameters.standardNamespace); typeName = classAttrib.Value; } // Field not serializable? if (!ParsingValidation.DataFieldSerializerValid(xElement, targetType, typeName, elementName, filename, errors)) { continue; } // Resolve field name type var d = new SerializedData(targetType, xElement as XElement, this.filename); d.LoadFields(errors, state); fields.Add(elementName, d); } } } }
/// <summary> /// The last step for loading data. /// In this step, prototype references are being resolved for this serialized data and their sub data objects. /// </summary> /// <param name="prototypes">Prototypes to use for remapping</param> /// <param name="errors"></param> public void ResolveReferenceFields(List <IPrototype> prototypes, List <ParsingError> errors, PrototypeParserState state) { Dictionary <string, object> updates = DictionaryPool <string, object> .Get(); try { foreach (var field in fields) { var @ref = field.Value as SerializedPrototypeReference; if (!ReferenceEquals(@ref, null)) { updates.Add(field.Key, @ref.Resolve(prototypes)); } var sub = field.Value as SerializedData; if (!ReferenceEquals(sub, null)) { sub.ResolveReferenceFields(prototypes, errors, state); } var col = field.Value as SerializedCollectionData; if (!ReferenceEquals(col, null)) { col.ResolveReferenceFieldsAndSubData(prototypes, errors, state); } } // Write updates foreach (var update in updates) { this.fields[update.Key] = update.Value; } } finally { DictionaryPool <string, object> .Return(updates); } }
public void ResolveReferenceFieldsAndSubData(List <IPrototype> prototypes, List <ParsingError> errors, PrototypeParserState state) { for (int i = 0; i < this.elements.Count; i++) { // Finalize, create and apply var element = this.elements[i]; var sElement = element as SerializedData; var protoRef = element as SerializedPrototypeReference; if (!ReferenceEquals(sElement, null)) { sElement.ResolveReferenceFields(prototypes, errors, state); var value = sElement.targetType.Create(); sElement.ApplyTo(value, errors, state); this.elements[i] = value; } else if (!ReferenceEquals(protoRef, null)) { this.elements[i] = protoRef.Resolve(prototypes); } } }
/// <summary> /// <see cref="SerializedData.LoadFields(List{ParsingError}, PrototypeParserState)"/> /// </summary> /// <param name="errors"></param> /// <param name="state"></param> public void ParseAndLoadData(List <ParsingError> errors, PrototypeParserState state) { var elementNodes = xElement.Nodes().ToList(); var collection = GetCollectionInstance(this.collectionType, elementNodes.Count); Type elementType = GetElementType(this.collectionType); var elementTypeCache = PrototypeCaches.GetSerializableTypeCacheFor(elementType); string elementTypeName = elementType.Name; foreach (var node in elementNodes) { var xElementNode = node as XElement; if (ReferenceEquals(xElementNode, null)) // Malformed XML { errors.Add(new ParsingError(ParsingErrorSeverity.ERROR, filename, (node as IXmlLineInfo).LineNumber, "Unable to cast node to element for " + node + "! Skipping element!")); continue; } if (typeof(IPrototype).IsAssignableFrom(elementType)) { // Prototype ref this.elements.Add(new SerializedPrototypeReference() { identifier = xElementNode.Value }); } else { // Determine type var serializableTypeCache = elementTypeCache; string typeName = elementTypeName; // Try to read class attrib var classAttrib = xElementNode.Attribute(PrototypeParser.PrototypeAttributeType); if (!ReferenceEquals(classAttrib, null)) { serializableTypeCache = PrototypeCaches.GetSerializableTypeCacheFor(classAttrib.Value, state.parameters.standardNamespace); typeName = classAttrib.Value; } // Validity checks // Field not serializable? if (ReferenceEquals(serializableTypeCache, null)) { // TODO: Line number, better reporting as to why this type is unserializable! errors.Add(new ParsingError(ParsingErrorSeverity.ERROR, filename, -1, "Collection element with unknown type " + typeName + " unserializable! Skipping field!")); continue; } // Add element this.elements.Add(new SerializedData(serializableTypeCache, xElementNode, this.filename)); } } foreach (var element in this.elements) { var sElement = element as SerializedData; if (!ReferenceEquals(sElement, null)) { sElement.LoadFields(errors, state); } } }
protected override bool _Deserialize(string value, PrototypeParserState state) { return(bool.Parse(value)); }
protected override short _Deserialize(string value, PrototypeParserState state) { return(short.Parse(value)); }
protected override double _Deserialize(string value, PrototypeParserState state) { return(double.Parse(value, CultureInfo.InvariantCulture)); }
protected override string _Deserialize(string value, PrototypeParserState state) { return(value); }
protected abstract T _Deserialize(string value, PrototypeParserState state);
public object Deserialize(Type type, XElement value, PrototypeParserState state) { return(_Deserialize(value.Value, state)); }
public object Deserialize(Type type, XElement value, PrototypeParserState state) { return(Enum.Parse(type, value.Value as string)); }
private void _Parse(List <SerializedData> data, ref PrototypeParseParameters parameters) { ListPool <ParsingError> .GetIfNull(ref errors); // Get prototypes with others inheriting from first // Key = type which is inheriting from something, Value = the type its inheriting from Dictionary <SerializedData, List <SerializedData> > inheritingFrom = new Dictionary <SerializedData, List <SerializedData> >(); // This is only used for topo sort! Dictionary <SerializedData, object> instances = new Dictionary <SerializedData, object>(); Dictionary <string, SerializedData> idMapping = new Dictionary <string, SerializedData>(); List <SerializedData> invalid = new List <SerializedData>(); // Pre-parse names, create instances and apply name foreach (var d in data) { if (!ParsingValidation.ElementHasId(d.xElement, d.filename, errors)) { invalid.Add(d); continue; } // Read name var attribName = d.xElement.Attribute(PrototypeAttributeIdentifier); idMapping.Add(attribName.Value, d); // Check if abstract prototype data var attribAbstract = d.xElement.Attribute(PrototypeAttributeAbstract); bool isAbstract = !ReferenceEquals(attribAbstract, null) && string.Equals("True", attribAbstract.Value); if (!isAbstract) { var obj = d.targetType.Create(); instances.Add(d, obj); (obj as IPrototype).identifier = attribName.Value; this.prototypes.Add(obj as IPrototype); } } // Remove invalidated entries foreach (var d in invalid) { data.Remove(d); } invalid.Clear(); foreach (var d in data) { SerializedData inheritedData; if (!string.IsNullOrEmpty(d.inherits) && idMapping.TryGetValue(d.inherits, out inheritedData)) { inheritingFrom.GetOrCreate(d).Add(inheritedData); } } PrototypeParserState state = new PrototypeParserState() { parameters = parameters }; // Remove invalidated entries foreach (var d in invalid) { data.Remove(d); } // Step 1 - sort by inheritance List <SerializedData> empty = new List <SerializedData>(); var sorted = data.TSort((sd) => inheritingFrom.ContainsKey(sd) ? inheritingFrom[sd] : empty, true).ToList(); // Step 2 - Preloads the fields and creates sub-data objects foreach (var d in sorted) { d.LoadFields(errors, state); } // Step 3 - run sorting algorithm for reference resolve foreach (var d in data) { d.ResolveReferenceFields(this.prototypes, errors, state); } // Step 4 - Final data apply List <SerializedData> inheritingFromTmp = new List <SerializedData>(); foreach (var d in sorted) { if (!instances.ContainsKey(d)) { continue; } // Apply inherited data first if (!string.IsNullOrEmpty(d.inherits)) { // Look up all inherited data in bottom to top order inheritingFromTmp.Clear(); var inheritedData = d.inherits; while (!string.IsNullOrEmpty(inheritedData)) { SerializedData serializedData = null; if (!this.serializedData.TryGetValue(inheritedData, out serializedData) && !idMapping.TryGetValue(inheritedData, out serializedData)) { this.errors.Add(new ParsingError(ParsingErrorSeverity.ERROR, d.filename, (d.xElement as IXmlLineInfo).LinePosition, "Could not find the prototype '" + inheritedData + "' for prototype '" + (instances[d] as IPrototype).identifier + "'! Ignoring inheritance!")); } else { inheritingFromTmp.Add(serializedData); } // Recursion inheritedData = serializedData.inherits; } // Reverse so we apply in top to bottom order inheritingFromTmp.Reverse(); // Apply foreach (var _d in inheritingFromTmp) { _d.ApplyTo(instances[d], errors, state); } } // Apply data over inherited d.ApplyTo(instances[d], errors, state); } // Step 5 - record serialized data in result foreach (var kvp in idMapping) { this.serializedData.Add(kvp.Key, kvp.Value); } }