/// <summary> /// Lazy init method to ensure the cache is built. /// </summary> public static void LazyInit() { if (wasInitialized) { return; } foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) { foreach (var type in asm.GetTypes()) { if (typeof(IPrototype).IsAssignableFrom(type) || type.GetCustomAttributes(true).Any((a) => a.GetType() == typeof(PrototypeDataSerializableAttribute))) { SerializableTypeCache cache = new SerializableTypeCache(); cache.Build(type); typeCache.Add(type, cache); } else if (!type.IsAbstract && !type.IsInterface && typeof(IPrototypeDataSerializer).IsAssignableFrom(type)) { serializers.Add(Activator.CreateInstance(type) as IPrototypeDataSerializer); } } } wasInitialized = true; }
public static bool TypeFound(XElement xElement, XAttribute typeAttribute, SerializableTypeCache type, string filename, List <ParsingError> errors) { if (ReferenceEquals(type, null)) { errors.Add(new ParsingError(ParsingErrorSeverity.ERROR, filename, (xElement as IXmlLineInfo).LineNumber, "Element type " + typeAttribute.Value + " unknown!")); return(false); } return(true); }
// TODO: string.Format() public static bool DataFieldSerializerFound(XElement xElement, SerializableTypeCache typeCache, string typeName, string fieldName, string filename, List <ParsingError> errors) { if (ReferenceEquals(typeCache, null)) { string msg = string.Format("Field '{0}' with unknown type {1} - unknown by the serializer cache! Are you missing {2} attribute?", fieldName, typeName, nameof(PrototypeDataSerializableAttribute)); errors.Add(new ParsingError(ParsingErrorSeverity.ERROR, filename, (xElement as IXmlLineInfo).LineNumber, msg)); return(false); } return(true); }
// TODO: string.Format() public static bool DataFieldSerializerValid(XElement xElement, SerializableTypeCache typeCache, string typeName, string fieldName, string filename, List <ParsingError> errors) { if (ReferenceEquals(typeCache, null)) { // TODO: Explanation - why unserializable!? string msg = string.Format("Field '{0}' with unserializeable type {1}!", fieldName, typeName); errors.Add(new ParsingError(ParsingErrorSeverity.ERROR, filename, (xElement as IXmlLineInfo).LineNumber, msg)); return(false); } return(true); }
public static bool FieldKnown(IXmlLineInfo debug, SerializableTypeCache type, string field, string filename, List <ParsingError> errors) { if (!type.HasField(field)) { // TODO: Line number string msg = string.Format("Unknown field {0}!", field); errors.Add(new ParsingError(ParsingErrorSeverity.ERROR, filename, ReferenceEquals(debug, null) ? -1 : debug.LineNumber, msg)); return(false); } return(true); }
/// <summary> /// Returns the serializable type cache if known for the specified type. /// Will be cached just in time. /// </summary> public static SerializableTypeCache GetSerializableTypeCacheFor(Type type) { SerializableTypeCache cache; if (!typeCache.TryGetValue(type, out cache)) { cache = SerializableTypeCache.TryBuild(type); typeCache.Add(type, cache); } return(cache); }
/// <summary> /// </summary> /// <param name="targetType">The target type to be parsed.</param> /// <param name="element">The xml element to parse the data from.</param> /// <param name="filename">The filename to be reported in case of errors.</param> public SerializedData(SerializableTypeCache targetType, XElement element, string filename) { this.targetType = targetType; this.xElement = element; this.filename = filename; var inheritsAttrib = element.Attribute(PrototypeParser.PrototypeAttributeInherits); if (!ReferenceEquals(inheritsAttrib, null)) { this.inherits = inheritsAttrib.Value; } }
/// <summary> /// Tries buildding the cache for the specified type. /// In case of an issue while building, null will be returned. /// /// If you want to know more as to why building failed, <see cref="GetBuildError(Type)"/> /// </summary> public static SerializableTypeCache TryBuild(Type type) { SerializableTypeCache stc = new SerializableTypeCache(); try { stc.Build(type); } catch (Exception ex) { buildErrors.Set(type, ex.ToString()); return(null); } return(stc); }
/// <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); } } } }