/// <summary> /// Parses the specified XML content and returns all prototypes which could be parsed. /// </summary> /// <param name="xmlContent">The xml content to use for parsing-</param> /// <param name="parameters">The parameters for the parser.</param> /// <param name="extend">The result to extend when parsing. Setting this will give the parser the ability to let data to be loaded now inherited from data loaded in extend.</param> /// <returns></returns> public void Parse(string xmlContent, string filename, PrototypeParseParameters parameters) { PrototypeCaches.LazyInit(); // Pre-parse data var data = new List <SerializedData>(); _PreParse(xmlContent, filename, ref parameters, data); _Parse(data, ref parameters); }
/// <summary> /// Same as <see cref="Parse(string, string)"/>, but can parse many xmls with relationships / dependencies in them together. /// This will make it possible to have a prototype in one file which is inherited from in another file. /// /// The prototypes will be loaded in order and able to resolve references across multiple files! /// </summary> public void Parse(string[] xmlContents, string[] filenames, PrototypeParseParameters parameters) { PrototypeCaches.LazyInit(); List <SerializedData> data = new List <SerializedData>(); if (xmlContents.Length != filenames.Length) { throw new ArgumentException("Xml content string count must match filename count in Prototypes.Parse()!"); } for (int i = 0; i < xmlContents.Length; i++) { _PreParse(xmlContents[i], filenames[i], ref parameters, data); } _Parse(data, ref parameters); }
/// <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> /// <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); } } }
private static SerializableTypeCache GetSerializableTypeCacheFor(string name, ref PrototypeParseParameters parameters) { return(PrototypeCaches.GetSerializableTypeCacheFor(name, parameters.standardNamespace)); }
private static SerializableTypeCache LookupSerializableTypeCache(string name, ref PrototypeParseParameters parameters) { return(PrototypeCaches.LookupSerializableTypeCache(name, parameters.standardNamespace)); }