Example #1
0
        internal static void ComposeElement(WriterNode node, object value, Type fieldType, bool isRootDec = false)
        {
            // Handle Dec types, if this isn't a root (otherwise we'd just reference ourselves and that's kind of pointless)
            if (!isRootDec && value is Dec)
            {
                // Dec types are special in a few ways.
                // First off, they don't include their type data, because we assume it's of a type provided by the structure.
                // Second, we represent null values as an empty string, not as a null tag.
                // (We'll accept the null tag if you insist, we just have a cleaner special case.)
                // Null tag stuff is done further down, in the null check.

                var rootType = value.GetType().GetDecRootType();
                if (!rootType.IsAssignableFrom(fieldType))
                {
                    // fieldType isn't a root type that we expect.
                    // We could put our root type here so we could re-parse it later on. But this is actually a huge problem.
                    // What if we move the Dec to another hierarchy entirely? Or just rename a hierarchy?
                    // We won't be able to find it given the root type. Do we search *all* Decs for it? This is a nightmare case.
                    // So instead we yell at the user and tell them this won't work (then save it anyway.)
                    Dbg.Err($"Attempting to save {value} into type {fieldType}, which is not a valid Dec root type");
                    node.TagClass(rootType);
                }

                node.WriteDec(value as Dec);

                return;
            }

            // Everything represents "null" with an explicit XML tag, so let's just do that
            // Maybe at some point we want to special-case this for the empty Dec link
            if (value == null)
            {
                if (typeof(Dec).IsAssignableFrom(fieldType))
                {
                    node.WriteDec(null);
                }
                else
                {
                    node.WriteExplicitNull();
                }

                return;
            }

            var valType = value.GetType();

            // This is our value's type, but we may need a little bit of tinkering to make it useful.
            // The current case I know of is System.RuntimeType, which appears if we call .GetType() on a Type.
            // I assume there is a complicated internal reason for this; good news, we can ignore it and just pretend it's a System.Type.
            // Bad news: it's actually really hard to detect this case because System.RuntimeType is private.
            // That's why we have the annoying `static` up above.
            if (valType == TypeSystemRuntimeType)
            {
                valType = typeof(Type);
            }

            // If we have a type that isn't the expected type, tag it. We need to do this before any further handling because everything fits in `object`.
            if (valType != fieldType)
            {
                node.TagClass(valType);
            }

            // Do all our unreferencables first
            if (valType.IsPrimitive)
            {
                node.WritePrimitive(value);

                return;
            }

            if (value is System.Enum)
            {
                node.WriteEnum(value);

                return;
            }

            if (value is string)
            {
                node.WriteString(value as string);

                return;
            }

            if (value is Type)
            {
                node.WriteType(value as Type);

                return;
            }

            // Check to see if we should make this into a ref
            if (!valType.IsValueType)
            {
                if (node.WriteReference(value))
                {
                    // The ref system has set up the appropriate tagging, so we're done!
                    return;
                }

                // Either this isn't a reference yet, or we don't even support references in this mode. So keep on processing.
            }

            if (valType.IsArray)
            {
                node.WriteArray(value as Array);

                return;
            }

            if (valType.IsGenericType && valType.GetGenericTypeDefinition() == typeof(List <>))
            {
                node.WriteList(value as IList);

                return;
            }

            if (valType.IsGenericType && valType.GetGenericTypeDefinition() == typeof(Dictionary <,>))
            {
                node.WriteDictionary(value as IDictionary);

                return;
            }

            if (valType.IsGenericType && valType.GetGenericTypeDefinition() == typeof(HashSet <>))
            {
                node.WriteHashSet(value as IEnumerable);

                return;
            }

            if (value is IRecordable)
            {
                node.WriteRecord(value as IRecordable);

                return;
            }

            {
                // Look for a converter; that's the only way to handle this before we fall back to reflection
                var converter = Serialization.Converters.TryGetValue(fieldType);
                if (converter != null)
                {
                    node.WriteConvertible(converter, value, fieldType);
                    return;
                }
            }

            if (!node.AllowReflection)
            {
                Dbg.Err($"Couldn't find a composition method for type {fieldType}; do you need a Converter?");
                return;
            }

            // We absolutely should not be doing reflection when in recorder mode; that way lies madness.

            foreach (var field in valType.GetSerializableFieldsFromHierarchy())
            {
                ComposeElement(node.CreateMember(field), field.GetValue(value), field.FieldType);
            }

            return;
        }
Example #2
0
 internal RecorderWriter(WriterNode node)
 {
     this.node = node;
 }