Ejemplo n.º 1
0
        static object CallPostDoc(Type serializedType, object obj)
        {
            var postDoc = ReflectionCache.GetPostDoc(serializedType);

            if (postDoc != null)
            {
                try {
                    obj = postDoc.Invoke(null, new[] { obj });
                } catch (System.Reflection.TargetInvocationException e) {
                    throw e.InnerException;
                }
            }

            return(obj);
        }
Ejemplo n.º 2
0
        static object ValueOfType(Type fieldType, object existing, DocNode value, ConfigOptions?options)
        {
            try {
                if (fieldType == typeof(System.Boolean))
                {
                    return(Convert.ToBoolean(value.StringValue, System.Globalization.CultureInfo.InvariantCulture));
                }
                // floating-point value types
                if (fieldType == typeof(System.Single))
                {
                    return(Convert.ToSingle(value.StringValue, System.Globalization.CultureInfo.InvariantCulture));
                }
                if (fieldType == typeof(System.Double))
                {
                    return(Convert.ToDouble(value.StringValue, System.Globalization.CultureInfo.InvariantCulture));
                }
                if (fieldType == typeof(System.Decimal))
                {
                    return(Convert.ToDecimal(value.StringValue, System.Globalization.CultureInfo.InvariantCulture));
                }
                // integral value types
                if (fieldType == typeof(System.SByte))
                {
                    return(Convert.ToSByte(value.StringValue, System.Globalization.CultureInfo.InvariantCulture));
                }
                if (fieldType == typeof(System.Byte))
                {
                    return(Convert.ToByte(value.StringValue, System.Globalization.CultureInfo.InvariantCulture));
                }
                if (fieldType == typeof(System.Char))
                {
                    return(Convert.ToChar(value.StringValue, System.Globalization.CultureInfo.InvariantCulture));
                }
                if (fieldType == typeof(System.Int16))
                {
                    return(Convert.ToInt16(value.StringValue, System.Globalization.CultureInfo.InvariantCulture));
                }
                if (fieldType == typeof(System.UInt16))
                {
                    return(Convert.ToUInt16(value.StringValue, System.Globalization.CultureInfo.InvariantCulture));
                }
                if (fieldType == typeof(System.Int32))
                {
                    return(Convert.ToInt32(value.StringValue, System.Globalization.CultureInfo.InvariantCulture));
                }
                if (fieldType == typeof(System.UInt32))
                {
                    return(Convert.ToUInt32(value.StringValue, System.Globalization.CultureInfo.InvariantCulture));
                }
                if (fieldType == typeof(System.Int64))
                {
                    return(Convert.ToInt64(value.StringValue, System.Globalization.CultureInfo.InvariantCulture));
                }
                if (fieldType == typeof(System.UInt64))
                {
                    return(Convert.ToUInt64(value.StringValue, System.Globalization.CultureInfo.InvariantCulture));
                }
                if (fieldType == typeof(string))
                {
                    return(value.StringValue);
                }
                if (fieldType.IsEnum)          // AudioRolloffMode, "Custom" => AudioRolloffMode.Custom
                {
                    return(Enum.Parse(fieldType, value.StringValue, true));
                }
                if (fieldType == typeof(DocNode))
                {
                    return(value);
                }
                if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(Nullable <>))
                {
                    if (value.Type == DocNodeType.Scalar && value.StringValue == "null")
                    {
                        return(null);
                    }
                    var innerType = Nullable.GetUnderlyingType(fieldType);
                    return(ValueOfType(innerType, existing, value, options));
                }
                if (s_fromDocs.ContainsKey(fieldType))
                {
                    existing = s_fromDocs[fieldType](existing, value);
                    return(CallPostDoc(fieldType, existing));
                }
                if (fieldType.IsArray)                      // [1,2,3,4,5] => new int[] { 1,2,3,4,5 }
                {
                    Type arrTyp = fieldType.GetElementType();
                    if (fieldType.GetArrayRank() == 2)
                    {
                        var firstList = value[0];
                        var destArr   = Array.CreateInstance(arrTyp, firstList.Count, value.Count);
                        int j         = 0;
                        foreach (DocNode subList in value.Values)
                        {
                            int i = 0;
                            foreach (var val in subList.Values.Select(item => ValueOfType(arrTyp, null, item, options)))
                            {
                                destArr.SetValue(val, new int[] { i, j });
                                i++;
                            }
                            j++;
                        }
                        return(destArr);
                    }
                    else
                    {
                        var iexisting = (Array)existing;
                        if (iexisting == null)
                        {
                            iexisting = Array.CreateInstance(arrTyp, value.Count);
                        }
                        else if (iexisting.Length != value.Count)
                        {
                            var oldArr = iexisting;
                            iexisting = Array.CreateInstance(arrTyp, value.Count);
                            for (int i = 0; i < iexisting.Length && i < oldArr.Length; i++)
                            {
                                iexisting.SetValue(oldArr.GetValue(i), i);
                            }
                        }
                        for (int i = 0; i < iexisting.Length; i++)
                        {
                            var existingElt = iexisting.GetValue(i);
                            var updatedElt  = ValueOfType(arrTyp, existingElt, value[i], options);
                            iexisting.SetValue(updatedElt, i);
                        }
                        return(iexisting);
                    }
                }
                if (fieldType.IsGenericType)
                {
                    // this chunk of code handles generic dictionaries and lists; it only
                    // works with string keys on the dictionaries, and for now any values
                    // must have zero-args constructors
                    if (fieldType.GetGenericTypeDefinition() == typeof(Dictionary <,>))
                    {
                        Type[] typeParameters = fieldType.GetGenericArguments();

                        if (existing == null)
                        {
                            existing = Activator.CreateInstance(fieldType);
                        }
                        var              iexisting = (System.Collections.IDictionary)existing;
                        Type             kType     = typeParameters[0];
                        Type             vType     = typeParameters[1];
                        ComposedDocNode  keyNode   = new ComposedDocNode(DocNodeType.Scalar); // can reuse this one object
                        HashSet <object> usedKeys  = new HashSet <object>();

                        // create/update all pairs in the doc
                        foreach (var kv in value.Pairs)
                        {
                            keyNode.StringValue = kv.Key;
                            object existingKey   = ValueOfType(kType, null, keyNode, options);
                            object existingValue = null;
                            if (iexisting.Contains(existingKey))
                            {
                                existingValue = iexisting[existingKey];
                            }
                            var updated = ValueOfType(vType, existingValue, kv.Value, options);
                            iexisting[existingKey] = updated;
                            usedKeys.Add(existingKey);
                        }
                        // remove any pairs not in the doc
                        var keysToRemove = new List <object>();
                        foreach (var k in iexisting.Keys)
                        {
                            if (!usedKeys.Contains(k))
                            {
                                keysToRemove.Add(k);
                            }
                        }
                        foreach (var k in keysToRemove)
                        {
                            iexisting.Remove(k);
                        }
                        return(iexisting);
                    }
                    if (fieldType.GetGenericTypeDefinition() == typeof(List <>))
                    {
                        Type[] typeParameters = fieldType.GetGenericArguments();
                        if (existing == null)
                        {
                            existing = Activator.CreateInstance(fieldType);
                        }

                        var iexisting = (System.Collections.IList)existing;
                        while (iexisting.Count > value.Count)
                        {
                            iexisting.RemoveAt(iexisting.Count - 1);
                        }
                        for (int i = 0; i < iexisting.Count; i++)
                        {
                            var existingElt = iexisting[i];
                            var updatedElt  = ValueOfType(typeParameters[0], existingElt, value[i], options);
                            iexisting[i] = updatedElt;
                        }
                        while (iexisting.Count < value.Count)
                        {
                            var newElt = ValueOfType(typeParameters[0], null, value[iexisting.Count], options);
                            iexisting.Add(newElt);
                        }
                        return(existing);
                    }
                }
                var fromDocMethod = ReflectionCache.GetFromDoc(fieldType);
                if (fromDocMethod != null)   // if there's a custom parser method on the class, delegate all work to that
                // TODO: this doesn't do inherited FromDoc methods properly, but it should
                {
                    try {
                        existing = fromDocMethod.Invoke(null, new[] { existing, value });
                        return(CallPostDoc(fieldType, existing));
                    } catch (System.Reflection.TargetInvocationException e) {
                        throw e.InnerException;
                    }
                }
                if (fieldType.IsClass)
                {
                    if (existing == null)
                    {
                        existing = CreateInstance(fieldType, value, options);
                        return(CallPostDoc(fieldType, existing));
                    }
                    else
                    {
                        SetFieldsOnObject(fieldType, ref existing, value, DefaultedOptions(options));
                        return(CallPostDoc(fieldType, existing));
                    }
                }
                if (fieldType.IsValueType)
                {
                    // a struct; set the members and return it
                    if (existing == null)
                    {
                        // structs can be null when boxed
                        existing = CreateInstance(fieldType, value, options);
                        return(CallPostDoc(fieldType, existing));
                    }
                    else
                    {
                        SetFieldsOnObject(fieldType, ref existing, value, DefaultedOptions(options));
                        return(CallPostDoc(fieldType, existing));
                    }
                }
            } catch (Exception e) {
                throw new ParseException("Exception based on document starting at: " + value.SourceInformation, e);
            }

            throw new System.NotSupportedException("Don't know how to update value of type " + fieldType.ToString());
        }
Ejemplo n.º 3
0
        /// Sets all members on the object *obj* based on the appropriate key from *doc*
        static void SetFieldsOnObject(Type type, ref object obj, DocNode doc, ConfigOptions options)
        {
            if (doc == null)
            {
                return;
            }

            bool caseInsensitive = (options & ConfigOptions.CaseSensitive) != ConfigOptions.CaseSensitive;
            bool checkMissing    = (options & ConfigOptions.AllowMissingFields) != ConfigOptions.AllowMissingFields;
            bool checkExtra      = (options & ConfigOptions.AllowExtraFields) != ConfigOptions.AllowExtraFields;
            var  classAttrs      = ReflectionCache.GetCustomAttributes(type);

            for (int i = 0; i < classAttrs.Length; i++)
            {
                if (classAttrs[i] is ConfigMandatoryAttribute)
                {
                    checkMissing = true; continue;
                }
                if (classAttrs[i] is ConfigAllowMissingAttribute)
                {
                    checkMissing = false; continue;
                }
            }
            if (GetFirstNonObjectBaseClass(type).ToString() == "UnityEngine.Object")
            {
                // Unity Objects have a lot of fields, it never makes sense to set most of them from configs
                checkMissing = false;
            }

            var setCopy = obj;

            if (doc.Type != DocNodeType.Dictionary)
            {
                // special-case behavior, set the first instance field on it from the doc
                var allFields = ReflectionCache.GetInstanceFields(type);
                Config.Assert(allFields.Length == 1, "Trying to set a field of type: ",
                              type, allFields.Length, "from value of wrong type:",
                              doc.Type == DocNodeType.Scalar ? doc.StringValue : doc.Type.ToString(),
                              "at",
                              doc.SourceInformation);
                SetField(allFields[0], ref setCopy, doc, options);
                obj = setCopy;
                return;
            }

            var typeFields        = new HashSet <string>();
            var usedFields        = new HashSet <string>();
            var lowercasedDocKeys = new Dictionary <string, string>();

            if (caseInsensitive)
            {
                foreach (var kv in doc.Pairs)
                {
                    lowercasedDocKeys[kv.Key.ToLower()] = kv.Key;
                }
            }

            bool anyFieldMandatory = false;

            foreach (ReflectionCache.CachedFieldInfo cf in ReflectionCache.GetStrippedFields(type, caseInsensitive))
            {
                var f = cf.field;
                // get attributes for the field
                bool isMandatory  = false;
                bool allowMissing = false;
                bool ignore       = false;
                var  attrs        = ReflectionCache.GetCustomAttributes(f);
                for (int i = 0; i < attrs.Length; i++)
                {
                    if (attrs[i] is ConfigMandatoryAttribute)
                    {
                        isMandatory = true; anyFieldMandatory = true; continue;
                    }
                    if (attrs[i] is ConfigAllowMissingAttribute)
                    {
                        allowMissing = true; continue;
                    }
                    if (attrs[i] is ConfigIgnoreAttribute)
                    {
                        ignore = true; continue;
                    }
                }
                if (IsDelegate(f.FieldType))
                {
                    ignore = true;                         // never report postdocs as present or missing
                }
                // figure out the canonical name for the field
                string strippedName = cf.strippedName;

                // do meta stuff based on attributes/validation
                if (ignore)
                {
                    usedFields.Add(strippedName); // pretend like we set it
                    continue;
                }
                if (checkMissing || isMandatory)
                {
                    typeFields.Add(strippedName);
                }

                string docKey = null;
                if (caseInsensitive)
                {
                    lowercasedDocKeys.TryGetValue(strippedName, out docKey);
                }
                if (docKey == null)
                {
                    docKey = strippedName;
                }
                if (doc.ContainsKey(docKey))
                {
                    usedFields.Add(strippedName);
                    SetField(f, ref setCopy, doc[docKey], options);
                }
                else if (allowMissing)
                {
                    usedFields.Add(strippedName);  // pretend like we set it
                }
            }

            // validation ---------
            if (checkExtra)
            {
                // check whether any fields in the doc were unused
                var extra = new List <string>();
                foreach (var kv in doc.Pairs)
                {
                    var key = kv.Key;
                    if (caseInsensitive)
                    {
                        key = ReflectionCache.GetLowercase(key);
                    }
                    if (!usedFields.Contains(key))
                    {
                        extra.Add(key);
                    }
                }
                if (extra.Count > 0)
                {
                    throw new ParseException("Extra doc fields: " + JoinList(extra, ", ") + " " + doc.SourceInformation, null);
                }
            }

            if (checkMissing || anyFieldMandatory)
            {
                // check whether any fields in the class were unset
                var missing = new List <string>();
                foreach (var typeField in typeFields)
                {
                    if (!usedFields.Contains(typeField))
                    {
                        missing.Add(typeField);
                    }
                }
                if (missing.Count > 0)
                {
                    throw new ParseException("Missing doc fields: " + JoinList(missing, ", ") + " " + doc.SourceInformation, null);
                }
            }
            obj = setCopy;
        }