private static FieldComparer getComparer(XmlNode node, Type[] genericArguments) { FieldComparer comparer = null; XmlAttribute attribute = node.Attributes [ATTRIBUTE_COMPARE]; if (attribute != null) { // explicit comparer, get from comparers or load from assembly comparers.TryGetValue(attribute.Value, out comparer); if (comparer == null) { Type typeInAnyAssembly = GenTypes.GetTypeInAnyAssembly(attribute.Value); if (typeInAnyAssembly != null && typeof(FieldComparer).IsAssignableFrom(typeInAnyAssembly)) { comparer = (FieldComparer)Activator.CreateInstance(typeInAnyAssembly); } } } if (comparer == null) { // try to infer comparer foreach (Type t in genericArguments) { if (comparers.ContainsKey(t.Name)) { comparer = comparers [t.Name]; break; } } } return(comparer); }
public static void RegisterComparer(Type type, FieldComparer comparer) { comparers.Add(type.Name, comparer); }
private void OverrideDataFromXml(XmlNode xmlRoot, Verse.Def destinationDef, bool debug) { Type destinationType = destinationDef.GetType(); string prefix = "Override :: " + destinationDef + " :: "; foreach (XmlNode node in xmlRoot.ChildNodes) { // field we are about to change string name = node.Name; if (name == null // may cause save issues if these change || name == "shortHash" || name == "index" || name == "debugRandomId") { continue; } string text = node.InnerText; Mode mode = getMode(node); if (string.IsNullOrEmpty(text) && mode != Mode.Clear) { // removal must be explicit replace or it's ignored continue; } if (name == "defName") { // not allowed to change target defName // we use it for tracking if (debug) { Log.Message(prefix + text); } prefix = "Override :: " + text + " :: "; continue; } FieldInfo destinationField = destinationType.GetField(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); if (destinationField == null) { Log.Warning(prefix + "\"" + name + "\" not found in target"); continue; } Type destinationFieldType = destinationField.FieldType; object destinationValue; if (mode == Mode.Clear && !destinationFieldType.IsPrimitive) { // we are clearing // primitives can't be null, set to 0 ? destinationValue = null; destinationField.SetValue(destinationDef, destinationValue); if (debug) { Log.Message(prefix + "\"" + name + "\" has been set to null"); } } else { destinationValue = destinationField.GetValue(destinationDef); } if (destinationFieldType.HasGenericDefinition(typeof(List <>))) { // its a list, search the source and queue or insert Type[] genericArguments = destinationFieldType.GetGenericArguments(); // Is it a def or derivate? Type targetDefType = null; foreach (Type t in genericArguments) { if (typeof(Verse.Def).IsAssignableFrom(t)) { targetDefType = t; break; } } // sometimes, they don't exist. Don't worry, no one will interfere. if (destinationValue == null) { destinationValue = Activator.CreateInstance(destinationFieldType); destinationField.SetValue(destinationDef, destinationValue); if (debug) { Log.Message(prefix + "\"" + name + "\" has been set to \"" + destinationValue + "\""); } } FieldParser parserFactory = getParserFactory(node); Func <XmlNode, object> parser = parserFactory.makeParser(genericArguments); // compares destination and result. Only available for Replace mode FieldComparer comparer = mode == Mode.Replace ? getComparer(node, genericArguments) : null; if (targetDefType != null) { // Crossreferencing a List needs the generic method MethodInfo crossRefLoader_RegisterListWantsCrossRef_generic = crossRefLoader_RegisterListWantsCrossRef.MakeGenericMethod(targetDefType); foreach (XmlNode child in node.ChildNodes) { object[] parameters = new object[] { destinationValue, child.InnerText }; crossRefLoader_RegisterListWantsCrossRef_generic.Invoke(null, parameters); if (debug) { Log.Message(prefix + "Registered into \"" + name + "\" the value \"" + child.InnerText + "\" of type \"" + targetDefType + "\""); } } } else { if (parser == null) { Log.Warning(prefix + "Parser is null"); continue; } IList destinationList = (IList)destinationValue; foreach (XmlNode child in node.ChildNodes) { object result = parser(child); if (result == null) { // no nulls allowed, they are troublemakers Log.Warning(prefix + "Can't Add null into \"" + name + "\""); continue; } if (mode == Mode.Replace) { if (comparer == null) { Log.Warning(prefix + "No known comparer for \"" + name + "\""); break; } Action findAndReplace = delegate { bool found = false; int index; for (index = 0; index < destinationList.Count; index++) { if (comparer.Compare(result, destinationList [index])) { destinationList [index] = result; found = true; break; } } if (found) { if (debug) { Log.Message(prefix + "Replaced into postion " + index + " at \"" + name + "\" the value \"" + result + "\""); } } else { destinationList.Add(result); if (debug) { Log.Message(prefix + "Added into \"" + name + "\" the value \"" + result + "\""); } } }; if (comparer.delay) { resolveReferencesActionQueue.Enqueue(findAndReplace); if (debug) { Log.Message(prefix + "Delaying Replace of element in \"" + name + "\" list"); } } else { findAndReplace(); } } else if (mode == Mode.Append) { destinationList.Add(result); if (debug) { Log.Message(prefix + "Added into \"" + name + "\" the value \"" + result + "\""); } } else { int index = mode == Mode.Insert ? getIndex(child, destinationList) : 0; destinationList.Insert(index, result); if (debug) { Log.Message(prefix + "Inserted into position " + index + " at \"" + destinationField.Name + "\" the value \"" + result + "\""); } } } } } else if (destinationFieldType.HasGenericDefinition(typeof(Dictionary <, >))) { // its a dict, what do we do? Log.Warning(prefix + "We don't know how to override Dictionary yet..."); } else if (typeof(Verse.Def).IsAssignableFrom(destinationFieldType)) { // its a Def, queue CrossRefLoader.RegisterObjectWantsCrossRef(destinationDef, destinationField, text); if (debug) { Log.Message(prefix + "Registered \"" + name + "\" with value \"" + destinationValue + "\" of type \"" + destinationFieldType.Name + "\" into \"" + text + "\""); } } else if (ParseHelper.HandlesType(destinationFieldType)) { // it can be handled by ParserHelper object result = ParseHelper.FromString(text, destinationFieldType); destinationField.SetValue(destinationDef, result); if (debug) { Log.Message(prefix + "Set \"" + name + "\" with value \"" + destinationValue + "\" of type \"" + destinationFieldType.Name + "\" into \"" + text + "\""); } } else { // it's most likely an object, try XmlToObject. FieldParser parserFactory = getParserFactory(node); Func <XmlNode, object> parser = parserFactory.makeParser(destinationFieldType); object result = null; if (parser != null) { result = parser(node); } if (result != null) { // this may fail, try catch? destinationField.SetValue(destinationDef, result); if (debug) { Log.Message(prefix + "Set \"" + name + "\" with value \"" + destinationValue + "\" of type \"" + destinationFieldType.Name + "\" into \"" + result + "\""); } } else { // user entered null Log.Warning(prefix + "Can't Set \"" + name + "\""); } } } }