Ejemplo n.º 1
0
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            while (reader.TokenType == JsonToken.Comment)
            {
                reader.Read();
            }
            var context = (LoadingContext)serializer.Context.Context;

            var tokenType   = reader.TokenType;
            var readerValue = reader.Value;
            var lineInfo    = reader as IJsonLineInfo;
            var defType     = objectType.GetGenericArguments()[0];

            if (tokenType == JsonToken.Null)
            {
                return(CreateRef(defType, null));
            }
            if (tokenType == JsonToken.String)
            {
                var stringRef = readerValue as string;
                if (stringRef.StartsWith("/"))
                {
                    //this is an absolute reference to a resource
                    //if I'm loading a prototype and this is not an embedded prototype then I'm loading external prototype, I should not do anything here until I encounter it again
                    //next time it won't be a child proto file
                    if (context.IsProtoChildFile && !context.ProtoStack.Peek().Embedded)
                    {
                        var rRef = CreateRef(context.IsProto, defType, stringRef);
                        return(rRef);
                    }
                    if (context.IsProto)
                    {
                        context.PushProto(false);
                        var normRef = CreateRef(context.IsProto, defType, stringRef);
                        context.PopProto();
                        return(normRef);
                    }
                    return(CreateRef(context.IsProto, defType, stringRef));
                }
                else if (stringRef.StartsWith("./"))
                {
                    //this is a relative reference to a resource
                    if (context.IsProtoChildFile && !context.ProtoStack.Peek().Embedded)
                    {
                        var rRef = CreateRef(context.IsProto, defType, stringRef, context.RootAddress);
                        return(rRef);
                    }
                    if (context.IsProto)
                    {
                        context.PushProto(false);
                        var normRef = CreateRef(context.IsProto, defType, stringRef, context.RootAddress);
                        context.PopProto();
                        return(normRef);
                    }
                    return(CreateRef(context.IsProto, defType, stringRef, context.RootAddress));
                }
                else if (stringRef.StartsWith("$"))
                {
                    //this is a local reference to a resource
                    var  id  = stringRef.Substring(1);
                    IDef res = context.GetInternalRes(id);
                    if (res != null)
                    {
                        return(CreateRef(defType, res));
                    }
                    else
                    {
                        throw new JsonException($"Reference to internal def not found {stringRef} {lineInfo.LineNumber} {lineInfo.LinePosition} {context.RootAddress}");
                    }
                }
                else if (stringRef.StartsWith("@"))
                {
                    //this a reference to a variable
                    var    name = stringRef.Substring(1);
                    Type   t;
                    object var = context.GetVar(name, out t);
                    if (var == null)
                    {
                        Logger.Warn($"Variable has no value {stringRef} {lineInfo.LineNumber} {lineInfo.LinePosition} {context.RootAddress}");
                        return(CreateRef(defType, null));
                    }
                    else if (var is IRefBase)
                    {
                        try
                        {
                            return(ConvertToProperRef(var, defType));
                        }
                        catch (Exception e)
                        {
                            throw new JsonException($"Error converting ref variable type {stringRef} at {lineInfo.LineNumber} {lineInfo.LinePosition} {context.RootAddress}", e);
                        }
                    }
                    else
                    {
                        var attr = defType.GetCustomAttribute <CanBeCreatedFromAliasedPrimitiveAttribute>(inherit: true);
                        if (attr != null)
                        {
                            var objectOfDesiredType = PrimitiveTypesConverter.Convert(var, attr.PrimitiveType);
                            var def = defType.GetMethod(attr.MethodName, BindingFlags.Static | BindingFlags.Public).Invoke(null, new[] { objectOfDesiredType });

                            return(CreateRef(defType, (IDef)def));
                        }
                    }
                }
            }
            else if (tokenType == JsonToken.StartObject)
            {
                //this is either a template or a def
                //does it make sense to write a template inside a file?
                //Well, in principle, why not?
                if (context.IsProtoChildFile)
                {
                    context.ProtoStack.Peek().Embedded = true;
                }
                var obj = serializer.Deserialize(reader, defType);
                return(CreateRef(defType, (IDef)obj));
            }

            if (tokenType == JsonToken.Boolean || tokenType == JsonToken.Float || tokenType == JsonToken.Integer || tokenType == JsonToken.String)
            {
                var attr = defType.GetCustomAttribute <CanBeCreatedFromAliasedPrimitiveAttribute>(inherit: true);
                if (attr != null)
                {
                    var objectOfDesiredType = PrimitiveTypesConverter.Convert(readerValue, attr.PrimitiveType);
                    var def = defType.GetMethod(attr.MethodName, BindingFlags.Static | BindingFlags.Public).Invoke(null, new[] { objectOfDesiredType });
                    return(CreateRef(defType, (IDef)def));
                }
            }
            //todo make proper line address
            throw new JsonException($"Reference to definition is not a string or object {lineInfo.LineNumber} {lineInfo.LinePosition} {context.RootAddress} - is {tokenType} {readerValue}");
        }