예제 #1
0
파일: Parser.cs 프로젝트: themotte/utils
        /// <summary>
        /// Pass an XML document string in for processing.
        /// </summary>
        /// <param name="stringName">A human-readable identifier useful for debugging. Generally, the name of the file that the string was read from. Not required (but very useful.)</param>
        public void AddString(string input, string stringName = "(unnamed)")
        {
            // This is a really easy error to make; we might as well handle it.
            if (input.EndsWith(".xml"))
            {
                Dbg.Err($"It looks like you've passed the filename {input} to AddString instead of the actual XML file. Either use AddFile() or pass the file contents in.");
            }

            if (s_Status != Status.Accumulating)
            {
                Dbg.Err($"Adding data while while the world is in {s_Status} state; should be {Status.Accumulating} state");
            }

            XDocument doc;

            try
            {
                doc = XDocument.Parse(input, LoadOptions.SetLineInfo);
            }
            catch (System.Xml.XmlException e)
            {
                Dbg.Ex(e);
                return;
            }

            if (doc.Elements().Count() > 1)
            {
                // This isn't testable, unfortunately; XDocument doesn't even support multiple root elements.
                Dbg.Err($"{stringName}: Found {doc.Elements().Count()} root elements instead of the expected 1");
            }

            var readerContext = new ReaderContext(stringName, false);

            foreach (var rootElement in doc.Elements())
            {
                if (rootElement.Name.LocalName != "Defs")
                {
                    Dbg.Wrn($"{stringName}:{rootElement.LineNumber()}: Found root element with name \"{rootElement.Name.LocalName}\" when it should be \"Defs\"");
                }

                foreach (var defElement in rootElement.Elements())
                {
                    string typeName = defElement.Name.LocalName;

                    Type typeHandle = UtilType.ParseDefFormatted(typeName, stringName, defElement.LineNumber());
                    if (typeHandle == null || !typeof(Def).IsAssignableFrom(typeHandle))
                    {
                        Dbg.Err($"{stringName}:{defElement.LineNumber()}: {typeName} is not a valid root Def type");
                        continue;
                    }

                    if (defElement.Attribute("defName") == null)
                    {
                        Dbg.Err($"{stringName}:{defElement.LineNumber()}: No def name provided");
                        continue;
                    }

                    string defName = defElement.Attribute("defName").Value;
                    if (!DefNameValidator.IsMatch(defName))
                    {
                        Dbg.Err($"{stringName}:{defElement.LineNumber()}: Def name \"{defName}\" doesn't match regex pattern \"{DefNameValidator}\"");
                        continue;
                    }

                    // Consume defName so we know it's not hanging around
                    defElement.Attribute("defName").Remove();

                    // Check to see if we're abstract
                    bool abstrct = false;
                    {
                        var abstractAttribute = defElement.Attribute("abstract");
                        if (abstractAttribute != null)
                        {
                            if (!bool.TryParse(abstractAttribute.Value, out abstrct))
                            {
                                Dbg.Err($"{stringName}:{defElement.LineNumber()}: Error encountered when parsing abstract attribute");
                            }

                            abstractAttribute.Remove();
                        }
                    }

                    // Get our parent info
                    string parent = null;
                    {
                        var parentAttribute = defElement.Attribute("parent");
                        if (parentAttribute != null)
                        {
                            parent = parentAttribute.Value;

                            parentAttribute.Remove();
                        }
                    }

                    // Register ourselves as an available parenting object
                    {
                        var identifier = Tuple.Create(typeHandle.GetDefRootType(), defName);
                        if (potentialParents.ContainsKey(identifier))
                        {
                            Dbg.Err($"{stringName}:{defElement.LineNumber()}: Def {identifier.Item1}:{identifier:Item2} redefined");
                        }
                        else
                        {
                            potentialParents[identifier] = new Parent {
                                xml = defElement, context = readerContext, parent = parent
                            };
                        }
                    }

                    if (!abstrct)
                    {
                        // Not abstract, so create our instance
                        var defInstance = (Def)Activator.CreateInstance(typeHandle);
                        defInstance.DefName = defName;

                        Database.Register(defInstance);

                        if (parent == null)
                        {
                            // Non-parent objects are simple; we just handle them here in order to avoid unnecessary GC churn
                            finishWork.Add(() => Serialization.ParseElement(defElement, typeHandle, defInstance, readerContext, isRootDef: true));
                        }
                        else
                        {
                            // Add an inheritance resolution job; we'll take care of this soon
                            inheritanceJobs.Add(new InheritanceJob {
                                target = defInstance, xml = defElement, context = readerContext, parent = parent
                            });
                        }
                    }
                }
            }
        }
예제 #2
0
        internal static object ParseString(string text, Type type, object model, string inputName, int lineNumber)
        {
            // Special case: Converter override
            // This is redundant if we're being called from ParseElement, but we aren't always.
            if (Converters.ContainsKey(type))
            {
                object result;

                try
                {
                    result = Converters[type].FromString(text, type, inputName, lineNumber);
                }
                catch (Exception e)
                {
                    Dbg.Ex(e);

                    if (type.IsValueType)
                    {
                        result = Activator.CreateInstance(type);
                    }
                    else
                    {
                        result = null;
                    }
                }

                if (result != null && !type.IsAssignableFrom(result.GetType()))
                {
                    Dbg.Err($"{inputName}:{lineNumber}: Converter {Converters[type].GetType()} for {type} returned unexpected type {result.GetType()}");
                    return(null);
                }

                return(result);
            }

            // Special case: defs
            if (typeof(Def).IsAssignableFrom(type))
            {
                if (type.GetDefRootType() == null)
                {
                    Dbg.Err($"{inputName}:{lineNumber}: Non-hierarchy defs cannot be used as references");
                    return(null);
                }

                if (text == "" || text == null)
                {
                    // you reference nothing, you get the null
                    return(null);
                }
                else
                {
                    Def result = Database.Get(type, text);
                    if (result == null)
                    {
                        Dbg.Err($"{inputName}:{lineNumber}: Couldn't find {type} named {text}");
                    }
                    return(result);
                }
            }

            // Special case: types
            if (type == typeof(Type))
            {
                if (text == "")
                {
                    return(null);
                }

                return(UtilType.ParseDefFormatted(text, inputName, lineNumber));
            }

            // Various non-composite-type special-cases
            if (text != "")
            {
                // If we've got text, treat us as an object of appropriate type
                try
                {
                    return(TypeDescriptor.GetConverter(type).ConvertFromString(text));
                }
                catch (System.Exception e)  // I would normally not catch System.Exception, but TypeConverter is wrapping FormatException in an Exception for some reason
                {
                    Dbg.Err($"{inputName}:{lineNumber}: {e.ToString()}");
                    return(model);
                }
            }
            else if (type == typeof(string))
            {
                // If we don't have text, and we're a string, return ""
                return("");
            }
            else
            {
                // If we don't have text, and we've fallen down to this point, that's an error (and return original value I guess)
                Dbg.Err($"{inputName}:{lineNumber}: Empty field provided for type {type}");
                return(model);
            }
        }