text:
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Framework.Test")]

namespace Framework.Json
{
    using Framework.DataAccessLayer;
    using System;
    using System.Collections;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.IO;
    using System.Reflection;
    using System.Runtime.Serialization;
    using System.Text;
    using System.Text.Json;

    /// <summary>
    /// By default, properties and fields are serialized to jsonSession (for server session store) and jsonClient (for Angular client).
    /// References to ComponentJson are not sent to client by default. But can be enabled exclusively with serialize client attribute.
    /// If ComponentJson.IsHidden is true, it is not sent to jsonClient.
    /// </summary>
    [Flags]
    internal enum SerializeEnum
    {
        /// <summary>
        /// Do not send property or field to client and do not store in session.
        /// </summary>
        None = 0,

        /// <summary>
        /// Store property or field value in session on server.
        /// </summary>
        Session = 1,

        /// <summary>
        /// Send property or field to client. Use for example for render output.
        /// </summary>
        Client = 2,

        /// <summary>
        /// Send property or field to client and do store in session. Attribute can be omitted.
        /// </summary>
        Both = 3,
    }

    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
    internal class SerializeAttribute : Attribute
    {
        public SerializeAttribute(SerializeEnum serializeEnum)
        {
            this.SerializeEnum = serializeEnum;
        }

        public readonly SerializeEnum SerializeEnum;
    }

    /// <summary>
    /// Write session and client json. See method Return(); for jsonSession and jsonClient return values.
    /// </summary>
    internal sealed class Writer : IDisposable
    {
        public Writer(JsonWriterOptions options)
        {
            this.streamSession = new MemoryStream();
            this.writerSession = new Utf8JsonWriter(streamSession, options);
            this.streamClient = new MemoryStream();
            this.writerClient = new Utf8JsonWriter(streamClient, options);
        }

        private Stack<(bool? IsSerializeSession, bool? IsSerializeClient)> serializeStack = new Stack<(bool? isSerializeSession, bool? isSerializeClient)>();

        public int SerializeStackCount
        {
            get
            {
                return serializeStack.Count;
            }
        }

        private int? stackRootIndexSession;
        private int? stackRootIndexClient;

        /// <summary>
        /// Make sure max one object is written to root.
        /// </summary>
        public void StackRootValidate()
        {
            if (IsSerializeSession)
            {
                if (stackRootIndexSession == null)
                {
                    stackRootIndexSession = serializeStack.Count - 1;
                }
                else
                {
                    if (stackRootIndexSession.Value == serializeStack.Count - 1)
                    {
                        throw new Exception("JsonSession can only have one ComponentJson graph!");
                    }
                }
            }
            if (IsSerializeClient)
            {
                if (stackRootIndexClient == null)
                {
                    stackRootIndexClient = serializeStack.Count - 1;
                }
                else
                {
                    if (stackRootIndexClient.Value == serializeStack.Count - 1)
                    {
                        throw new Exception("JsonClient can only have one ComponentJson graph!");
                    }
                }
            }
        }

        /// <summary>
        /// Gets IsSerializeSession. If true, writer writes to jsonSession. See also method SerializeStart();
        /// </summary>
        public bool IsSerializeSession { get; private set; }

        /// <summary>
        /// Gets IsSerializeClient. If true, writer writes to jsonClient. See also method SerializeStart();
        /// </summary>
        public bool IsSerializeClient { get; private set; }

        /// <summary>
        /// Configure writer. Method SerializeStart(); has to be followed by method SerializeEnd();
        /// </summary>
        public void SerializeStart(bool? isSerializeSession, bool? isSerializeClient)
        {
            var result = (IsSerializeSession: (bool?)null, IsSerializeClient: (bool?)null);
            if (serializeStack.TryPeek(out var serialize))
            {
                result = serialize;
            }
            if (isSerializeSession != null && result.IsSerializeSession != false)
            {
                result.IsSerializeSession = isSerializeSession;
            }
            if (isSerializeClient != null && result.IsSerializeClient != false)
            {
                result.IsSerializeClient = isSerializeClient;
            }
            serializeStack.Push(result);
            IsSerializeSession = result.IsSerializeSession == true;
            IsSerializeClient = result.IsSerializeClient == true;
        }

        public void SerializeEnd()
        {
            serializeStack.Pop();
            if (serializeStack.TryPeek(out var result))
            {
                IsSerializeSession = result.IsSerializeSession == true;
                IsSerializeClient = result.IsSerializeClient == true;
            }
        }

        private MemoryStream streamSession;

        private readonly Utf8JsonWriter writerSession;

        private MemoryStream streamClient;

        private readonly Utf8JsonWriter writerClient;

        public void WritePropertyName(string propertyName)
        {
            if (IsSerializeSession)
                writerSession.WritePropertyName(propertyName);
            if (IsSerializeClient)
                writerClient.WritePropertyName(propertyName);
        }

        public void WriteNumber(string propertyName, int value)
        {
            if (IsSerializeSession)
                writerSession.WriteNumber(propertyName, value);
            if (IsSerializeClient)
                writerClient.WriteNumber(propertyName, value);
        }

        public void WriteNull(string propertyName)
        {
            if (IsSerializeSession)
                writerSession.WriteNull(propertyName);
            if (IsSerializeClient)
                writerClient.WriteNull(propertyName);
        }

        public void WriteStartObject()
        {
            if (IsSerializeSession)
                writerSession.WriteStartObject();
            if (IsSerializeClient)
                writerClient.WriteStartObject();
        }

        public void WriteStartArray()
        {
            if (IsSerializeSession)
                writerSession.WriteStartArray();
            if (IsSerializeClient)
                writerClient.WriteStartArray();
        }

        public void WriteEndArray()
        {
            if (IsSerializeSession)
                writerSession.WriteEndArray();
            if (IsSerializeClient)
                writerClient.WriteEndArray();
        }

        public void WriteEndObject()
        {
            if (IsSerializeSession)
                writerSession.WriteEndObject();
            if (IsSerializeClient)
                writerClient.WriteEndObject();
        }

        public void WriteNullValue()
        {
            if (IsSerializeSession)
                writerSession.WriteNullValue();
            if (IsSerializeClient)
                writerClient.WriteNullValue();
        }

        public void WriteNumberValue(int value)
        {
            if (IsSerializeSession)
                writerSession.WriteNumberValue(value);
            if (IsSerializeClient)
                writerClient.WriteNumberValue(value);
        }

        internal void WriteNumberValue(double value)
        {
            if (IsSerializeSession)
                writerSession.WriteNumberValue(value);
            if (IsSerializeClient)
                writerClient.WriteNumberValue(value);
        }

        public void WriteStringValue(string value)
        {
            if (IsSerializeSession)
                writerSession.WriteStringValue(value);
            if (IsSerializeClient)
                writerClient.WriteStringValue(value);
        }

        public void WriteBooleanValue(bool value)
        {
            if (IsSerializeSession)
                writerSession.WriteBooleanValue(value);
            if (IsSerializeClient)
                writerClient.WriteBooleanValue(value);
        }

        public void WriteString(string propertyName, string value)
        {
            if (IsSerializeSession)
                writerSession.WriteString(propertyName, value);
            if (IsSerializeClient)
                writerClient.WriteString(propertyName, value);
        }

        internal void Serialize(object value)
        {
            if (IsSerializeSession)
                JsonSerializer.Serialize(writerSession, value, value.GetType());
            if (IsSerializeClient)
                JsonSerializer.Serialize(writerClient, value, value.GetType());
        }

        public void Return(out string jsonSession, out string jsonClient)
        {
            UtilFramework.Assert(serializeStack.Count == 0);
            writerSession.Flush();
            writerClient.Flush();
            jsonSession = Encoding.UTF8.GetString(streamSession.ToArray());
            jsonClient = Encoding.UTF8.GetString(streamClient.ToArray());
        }

        public void Dispose()
        {
            writerClient.Dispose();
            streamClient.Dispose();
            writerSession.Dispose();
            streamSession.Dispose();
        }
    }

    internal static class UtilJson
    {
        /// <summary>
        /// (TypeName, DeclarationObject)
        /// </summary>
        private static readonly ConcurrentDictionary<string, DeclarationObject> declarationObjectList = new ConcurrentDictionary<string, DeclarationObject>();

        private static DeclarationObject DeclarationObjectGet(string typeName)
        {
            return declarationObjectList.GetOrAdd(typeName, (key) =>
            {
                Type type = Type.GetType(typeName);
                bool isComponentJson = UtilFramework.IsSubclassOf(type, typeof(ComponentJson));
                bool isRow = UtilFramework.IsSubclassOf(type, typeof(Row));
                bool isDto = type.Assembly == typeof(UtilFramework).Assembly; // Dto declared in UtilFramework
                isDto = isDto || type.Namespace.StartsWith("Framework.Test"); // Dto declared in Framework.Test
                UtilFramework.Assert(isComponentJson | isRow | isDto);
                return new DeclarationObject(type);
            });
        }

        private static DeclarationObject DeclarationObjectGet(Type type)
        {
            return DeclarationObjectGet(UtilFramework.TypeToName(type, true));
        }

        private class DeclarationObject
        {
            public DeclarationObject(Type type)
            {
                this.Type = type;
                this.TypeName = type.FullName;
                // Property
                foreach (var propertyInfo in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
                {
                    if (propertyInfo.CanWrite) // If property has no setter do not add it.
                    {
                        if (SerializeAttribute(propertyInfo.GetCustomAttribute<SerializeAttribute>(), out bool isSerializeSession, out bool isSerializeClient, out bool isAttribute)) // If SerializeEnum.Both, do not add property
                        {
                            DeclarationProperty property = new DeclarationProperty(propertyInfo, isSerializeSession, isSerializeClient, isAttribute);
                            PropertyList.Add(property.PropertyName, property);
                        }
                    }
                }
                // Field
                foreach (var fieldInfo in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
                {
                    if (SerializeAttribute(fieldInfo.GetCustomAttribute<SerializeAttribute>(), out bool isSerializeSession, out bool isSerializeClient, out bool isAttribute)) // If SerializeEnum.Both, do not add property
                    {
                        if (fieldInfo.Attributes != FieldAttributes.Private)
                        {
                            DeclarationProperty property = new DeclarationProperty(fieldInfo, isSerializeSession, isSerializeClient, isAttribute);
                            PropertyList.Add(property.PropertyName, property);
                        }
                    }
                }
            }

            private bool SerializeAttribute(SerializeAttribute attribute, out bool isSerializeSession, out bool isSerializeClient, out bool isAttribute)
            {
                SerializeEnum serializeEnum = SerializeEnum.Both;
                isAttribute = false;
                if (attribute != null)
                {
                    isAttribute = true;
                    serializeEnum = attribute.SerializeEnum;
                }
                isSerializeSession = (serializeEnum & SerializeEnum.Session) > 0;
                isSerializeClient = (serializeEnum & SerializeEnum.Client) > 0;
                return (serializeEnum != SerializeEnum.None);
            }

            public readonly Type Type;

            public readonly string TypeName;

            /// <summary>
            /// (PropertyName, DeclarationProperty).
            /// </summary>
            public Dictionary<string, DeclarationProperty> PropertyList = new Dictionary<string, DeclarationProperty>();
        }

        internal class DeclarationProperty
        {
            public DeclarationProperty(PropertyInfo propertyInfo, bool isSerializeSession, bool isSerializeClient, bool isAttribute)
            {
                this.PropertyInfo = propertyInfo;
                this.PropertyName = propertyInfo.Name;
                this.PropertyType = propertyInfo.PropertyType;
                this.IsSerializeSession = isSerializeSession;
                this.IsSerializeClient = isSerializeClient;
                this.IsAttribute = isAttribute;
                this.IsSerializeClientExclusive = IsSerializeClient && IsAttribute;

                Constructor(ref this.PropertyType, ref this.IsList);

                this.Converter = ConverterGet(this.PropertyType);
            }

            public DeclarationProperty(FieldInfo fieldInfo, bool isSerializeSession, bool isSerializeClient, bool isAttribute)
            {
                this.FieldInfo = fieldInfo;
                this.PropertyName = fieldInfo.Name;
                this.PropertyType = fieldInfo.FieldType;
                this.IsSerializeSession = isSerializeSession;
                this.IsSerializeClient = isSerializeClient;
                this.IsAttribute = isAttribute;
                this.IsSerializeClientExclusive = IsSerializeClient && IsAttribute;

                Constructor(ref this.PropertyType, ref this.IsList);

                this.Converter = ConverterGet(this.PropertyType);
            }

            private void Constructor(ref Type propertyType, ref bool isList)
            {
                if (propertyType.IsGenericType && (propertyType.GetGenericTypeDefinition() == typeof(List<>) || propertyType.GetGenericTypeDefinition() == typeof(IReadOnlyList<>)))
                {
                    isList = true;
                    propertyType = propertyType.GetGenericArguments()[0];
                }

                if (propertyType.IsArray)
                {
                    isList = true;
                    propertyType = this.PropertyType.GetElementType();
                }
            }

            public readonly FieldInfo FieldInfo;

            public readonly PropertyInfo PropertyInfo;

            public readonly string PropertyName;

            /// <summary>
            /// Gets PropertyType. Declaring type for property and list.
            /// </summary>
            public readonly Type PropertyType;

            public readonly ConverterBase Converter;

            /// <summary>
            /// Gets IsList. Field is either single field or list.
            /// </summary>
            public readonly bool IsList;

            public readonly bool IsSerializeSession;

            public readonly bool IsSerializeClient;

            /// <summary>
            /// Gets IsSerializeClientExclusive. If true, property has client serialization attribute declared exclusively.
            /// </summary>
            public readonly bool IsSerializeClientExclusive;

            /// <summary>
            /// Gets IsAttribute. If true, serialize attribute is explicitly declared on this property.
            /// </summary>
            public readonly bool IsAttribute;

            public object ValueGet(object obj)
            {
                UtilFramework.Assert(this.IsList == false);
                object result;
                if (PropertyInfo != null)
                {
                    result = PropertyInfo.GetValue(obj);
                }
                else
                {
                    result = FieldInfo.GetValue(obj);

                    // TypedReference typedReference = __makeref(obj);
                    // result = FieldInfo.GetValueDirect(typedReference);
                }
                return result;
            }

            public void ValueSet(object obj, object value)
            {
                UtilFramework.Assert(this.IsList == false);
                if (PropertyInfo != null)
                {
                    PropertyInfo.SetValue(obj, value);
                }
                else
                {
                    FieldInfo.SetValue(obj, value);
                }
            }

            public IList ValueListGet(object obj)
            {
                UtilFramework.Assert(this.IsList == true);
                IList result;
                if (PropertyInfo != null)
                {
                    result = (IList)PropertyInfo.GetValue(obj);
                }
                else
                {
                    result = (IList)FieldInfo.GetValue(obj);
                }
                return result;
            }

            public void ValueListSet(object obj, IList valueList)
            {
                UtilFramework.Assert(this.IsList == true);

                if (PropertyInfo?.PropertyType.IsArray == true || FieldInfo?.FieldType.IsArray == true)
                {
                    var valueListArray = Array.CreateInstance(PropertyType, valueList.Count);
                    valueList.CopyTo(valueListArray, 0);
                    valueList = valueListArray;
                }

                if (PropertyInfo != null)
                {
                    PropertyInfo.SetValue(obj, valueList);
                }
                else
                {
                    FieldInfo.SetValue(obj, valueList);
                }
            }
        }

        /// <summary>
        /// (PropertyType, Converter)
        /// </summary>
        private static readonly ConcurrentDictionary<Type, ConverterBase> converterList = new ConcurrentDictionary<Type, ConverterBase>(new KeyValuePair<Type, ConverterBase>[] {
            // Value types
            new KeyValuePair<Type, ConverterBase>(new ConverterInt().PropertyType, new ConverterInt()),
            new KeyValuePair<Type, ConverterBase>(new ConverterIntNullable().PropertyType, new ConverterIntNullable()),
            new KeyValuePair<Type, ConverterBase>(new ConverterBoolean().PropertyType, new ConverterBoolean()),
            new KeyValuePair<Type, ConverterBase>(new ConverterBooleanNullable().PropertyType, new ConverterBooleanNullable()),
            new KeyValuePair<Type, ConverterBase>(new ConverterDouble().PropertyType, new ConverterDouble()),
            new KeyValuePair<Type, ConverterBase>(new ConverterDoubleNullable().PropertyType, new ConverterDoubleNullable()),
            new KeyValuePair<Type, ConverterBase>(new ConverterDateTime().PropertyType, new ConverterDateTime()),
            new KeyValuePair<Type, ConverterBase>(new ConverterDateTimeNullable().PropertyType, new ConverterDateTimeNullable()),
            new KeyValuePair<Type, ConverterBase>(new ConverterString().PropertyType, new ConverterString()),

            // Special types
            new KeyValuePair<Type, ConverterBase>(new ConverterObjectValue().PropertyType, new ConverterObjectValue()), // Value object
            new KeyValuePair<Type, ConverterBase>(new ConverterType().PropertyType, new ConverterType()), // Type
        });

        private static readonly ConverterObjectRoot converterObjectRoot = new ConverterObjectRoot();
        private static readonly ConverterObjectDto converterObjectDto = new ConverterObjectDto();
        private static readonly ConverterObjectRow converterObjectRow = new ConverterObjectRow();
        private static readonly ConverterObjectComponentJson converterObjectComponentJson = new ConverterObjectComponentJson();
        private static readonly ConverterEnum converterEnum = new ConverterEnum();
        private static readonly ConverterEnumNullable converterEnumNullable = new ConverterEnumNullable();

        /// <summary>
        /// (Type, typeof(<PropertyType>))
        /// </summary>
        private static readonly ConcurrentDictionary<Type, Type> TypeGenericList = new ConcurrentDictionary<Type, Type>();

        /// <summary>
        /// Returns Converter.
        /// </summary>
        /// <param name="propertyType">Property type</param>
        private static ConverterBase ConverterGet(Type propertyType)
        {
            if (!converterList.TryGetValue(propertyType, out ConverterBase result)) // Value type
                if (propertyType.IsEnum) // Emum
                    result = converterEnum;
                else
                    if (UtilFramework.TypeUnderlying(propertyType).IsEnum) // EnumNullable
                        result = converterEnumNullable;
                else
                    if (UtilFramework.IsSubclassOf(propertyType, typeof(Row))) // Row
                        result = converterObjectRow;
                else
                    if (UtilFramework.IsSubclassOf(propertyType, typeof(ComponentJson))) // ComponentJson
                        result = converterObjectComponentJson;
                else
                    if (propertyType.Assembly == typeof(UtilFramework).Assembly || propertyType.Namespace.StartsWith("Framework.Test")) // Dto
                        result = converterObjectDto;
            UtilFramework.Assert(result != null, "Type not supported!");
            return result;
        }

        internal abstract class ConverterBase
        {
            public ConverterBase(Type propertyType, Type propertyTypeGeneric, bool isObject, object valueDefault)
            {
                this.PropertyType = propertyType;
                this.propertyTypeGeneric = propertyTypeGeneric;
                this.IsObject = isObject;
                this.ValueDefault = valueDefault;

                UtilFramework.Assert(!(this.PropertyType == null ^ this.propertyTypeGeneric == null));
            }

            public ConverterBase(bool isObject, object valueDefault)
                : this(null, null, isObject, valueDefault)
            {

            }

            protected virtual bool IsValueDefault(object value)
            {
                return object.Equals(value, ValueDefault);
            }

            protected virtual void SerializeValue(object obj, DeclarationProperty property, object value, Writer writer)
            {
                // writer.WriteStringValue(string.Format("{0}", value));
            }

            protected virtual void SerializeObjectType(DeclarationProperty property, object obj, Writer writer)
            {
                // writer.WriteString("$typeRoot", UtilFramework.TypeToName(obj.GetType()));
            }

            /// <summary>
            /// Returns true if 'property' and 'value' is a reference to a ComponentJson and serializes it to jsonSession.
            /// </summary>
            private bool ReferenceSerialize(object obj, DeclarationProperty property, object value, ref ComponentJson componentJsonRoot, Writer writer)
            {
                bool result = false;
                if (value is ComponentJson valueComponentJson)
                {
                    if (obj is ComponentJson objComponentJson)
                    {
                        if (property.PropertyName == nameof(ComponentJson.List))
                        {
                            // ComponentJson.List
                        }
                        else
                        {
                            // ComponentJson.Property
                            result = true;
                        }
                    }
                    else
                    {
                        if (componentJsonRoot == null)
                        {
                            UtilFramework.Assert(valueComponentJson.Owner == null, "Referenced ComponentJson not root!");
                            componentJsonRoot = valueComponentJson;
                        }
                        else
                        {
                            // Dto referenced ComponentJson in object graph.
                            result = true;
                        }
                    }

                    if (result)
                    {
                        int? id = valueComponentJson.Id;
                        if (valueComponentJson.IsRemoved)
                        {
                            // UtilFramework.Assert(false, string.Format("Reference to removed ComponentJson! ({0}.{1})", UtilFramework.TypeToName(obj.GetType()), property.PropertyName));
                            id = null;
                        }
                        UtilFramework.Assert(valueComponentJson.Root == componentJsonRoot, "Referenced ComponentJson not in same object graph!");
                        result = true;
                        writer.SerializeStart(null, false); // Do not serialize reference to client
                        writer.WriteStartObject();
                        if (id != null)
                        {
                            writer.WriteNumber("$referenceId", id.Value);
                        }
                        else
                        {
                            writer.WriteNull("$referenceId");
                        }
                        writer.WriteEndObject();
                        writer.SerializeEnd();
                    }
                }
                return result;
            }

            private void SerializeObject(object obj, DeclarationProperty property, object value, ComponentJson componentJsonRoot, Writer writer)
            {
                bool isReference = ReferenceSerialize(obj, property, value, ref componentJsonRoot, writer);
                bool isSerializeClientExclusive = (property?.IsSerializeClientExclusive).GetValueOrDefault();
                if (isReference && !isSerializeClientExclusive)
                {
                    return;
                }
                DeclarationObject declarationObject;
                declarationObject = DeclarationObjectGet(value.GetType());
                bool? isSerializeSessionObject = isReference && isSerializeClientExclusive ? false : (bool?)null; // Session reference has already been serialized by method ReferenceSerialize();
                bool? isSerializeClientObject = (bool?)isSerializeClientExclusive | ((value is ComponentJson) ? true : (bool?)null); // Serialize to client if client attribute is declared on property.
                writer.SerializeStart(isSerializeSessionObject, isSerializeClientObject);
                writer.StackRootValidate();

                writer.WriteStartObject();
                SerializeObjectType(property, value, writer);
                foreach (var valueProperty in declarationObject.PropertyList.Values)
                {
                    ConverterBase converter = valueProperty.Converter;
                    bool? isSerializeClient = null;
                    bool? isSerializeSession = null;
                    if (converter is ConverterObjectRow)
                    {
                        isSerializeClient = false; // Do not send data row to client.
                    }
                    if (converter is ConverterObjectComponentJson)
                    {
                        if (!(value is ComponentJson)) // Property references ComponentJson
                        {
                            if (writer.IsSerializeClient && !valueProperty.IsSerializeClientExclusive)
                            {
                                isSerializeClient = false; // Do not send ComponentJson reference to client.
                            }
                        }
                        if (value is ComponentJson && valueProperty.PropertyName != nameof(ComponentJson.List)) // ComponentJson references ComponentJson
                        {
                            isSerializeClient = false; // Do not send ComponentJson reference to client.
                        }
                    }

                    // SerializeAttribute
                    if (valueProperty.IsSerializeSession == false)
                    {
                        isSerializeSession = false;
                    }
                    if (valueProperty.IsSerializeClient == false)
                    {
                        isSerializeClient = false;
                    }

                    // Serialize property, list
                    if (valueProperty.IsList == false)
                    {
                        // Serialize property
                        object propertyValue = valueProperty.ValueGet(value);
                        if (!converter.IsValueDefault(propertyValue))
                        {
                            if (propertyValue is ComponentJson componentJson && componentJson.IsHide && !valueProperty.IsSerializeClientExclusive)
                            {
                                isSerializeClient = false; // No list entry for hidden object.
                            }
                            writer.SerializeStart(isSerializeSession, isSerializeClient);
                            writer.WritePropertyName(valueProperty.PropertyName);
                            converter.Serialize(value, valueProperty, propertyValue, componentJsonRoot, writer);
                            writer.SerializeEnd();
                        }
                    }
                    else
                    {
                        // Serialize list
                        IList propertyValueList = valueProperty.ValueListGet(value);
                        if (propertyValueList?.Count > 0)
                        {
                            writer.WritePropertyName(valueProperty.PropertyName);
                            writer.WriteStartArray();
                            foreach (var propertyValue in propertyValueList)
                            {
                                var isSerializeClientLocal = isSerializeClient;
                                if (propertyValue is IHide hide && hide.IsHide)
                                {
                                    isSerializeClientLocal = false; // No list entry for hidden object.
                                }
                                writer.SerializeStart(isSerializeSession, isSerializeClientLocal);
                                if (!converter.IsValueDefault(propertyValue))
                                {
                                    converter.Serialize(value, valueProperty, propertyValue, componentJsonRoot, writer);
                                }
                                else
                                {
                                    if (converter.ValueDefault == null)
                                    {
                                        writer.WriteNullValue(); // Serialize null
                                    }
                                    else
                                    {
                                        converter.Serialize(value, valueProperty, propertyValue, componentJsonRoot, writer);
                                    }
                                }
                                writer.SerializeEnd();
                            }
                            writer.WriteEndArray();
                        }
                    }
                }
                writer.WriteEndObject();
                writer.SerializeEnd();
            }

            internal void Serialize(object obj, DeclarationProperty property, object value, ComponentJson componentJsonRoot, Writer writer)
            {
                if (IsObject == false)
                {
                    SerializeValue(obj, property, value, writer);
                }
                else
                {
                    SerializeObject(obj, property, value, componentJsonRoot, writer);
                }
            }

            protected virtual object Utf8JsonReaderDeserializeValue(object obj, DeclarationProperty property, Utf8JsonReader reader)
            {
                throw new NotImplementedException(); // return reader.GetString();
            }

            protected virtual object DeserializeValue(object obj, DeclarationProperty property, JsonElement jsonElement)
            {
                throw new NotImplementedException(); // return jsonElement.GetString();
            }

            protected virtual string DeserializeObjectType(DeclarationProperty property, JsonElement jsonElement)
            {
                // return jsonElement.GetProperty("$typeRoot").GetString();
                return null;
            }

            protected virtual string Utf8JsonReaderDeserializeObjectType(DeclarationProperty property, Utf8JsonReader reader)
            {
                reader.Read();
                UtilFramework.Assert(reader.TokenType == JsonTokenType.PropertyName);
                string propertyName = reader.GetString();
                UtilFramework.Assert(propertyName == "$typeRoot");
                reader.Read();
                UtilFramework.Assert(reader.TokenType == JsonTokenType.String);
                string result = reader.GetString();
                return result;
            }

            private object Utf8JsonReaderDeserializeObject(DeclarationProperty property, Utf8JsonReader reader)
            {
                reader.Read();
                UtilFramework.Assert(reader.TokenType == JsonTokenType.StartObject);
                Type type = PropertyType;
                string typeName = Utf8JsonReaderDeserializeObjectType(property, reader);
                if (typeName != null)
                {
                    type = UtilFramework.TypeFromName(typeName);
                }
                var result = FormatterServices.GetUninitializedObject(type);
                var declarationObject = DeclarationObjectGet(type);
                return result;
            }

            private bool ReferenceDeserialize(object obj, DeclarationProperty property, JsonElement jsonElement, out object result, ComponentJson componentJsonRoot)
            {
                bool resultReturn = false;
                result = null;
                if (jsonElement.TryGetProperty("$referenceId", out JsonElement jsonElementReference))
                {
                    if (jsonElementReference.ValueKind != JsonValueKind.Null)
                    {
                        int id = jsonElementReference.GetInt32();
                        componentJsonRoot.RootReferenceList.Add((obj, property, id)); // Register reference to solve later.
                    }
                    resultReturn = true;
                }
                return resultReturn;
            }

            private void DeserializeObjectComponentJsonConstructor(object obj, DeclarationProperty property, object value)
            {
                if (value is ComponentJson componentJson)
                {
                    ComponentJson owner = null;
                    if (obj is ComponentJson && property.PropertyName == nameof(ComponentJson.List))
                    {
                        owner = (ComponentJson)obj;
                    }
                    componentJson.Constructor(owner, isDeserialize: true);
                }
            }

            private object DeserializeObject(object obj, DeclarationProperty property, JsonElement jsonElement, ComponentJson componentJsonRoot)
            {
                if (ReferenceDeserialize(obj, property, jsonElement, out object result, componentJsonRoot))
                {
                    return result;
                }

                Type type = PropertyType;
                string typeName = DeserializeObjectType(property, jsonElement);
                if (typeName != null)
                {
                    type = UtilFramework.TypeFromName(typeName);
                }
                if (type == null)
                {
                    type = property.PropertyType; // Dto
                }
                result = FormatterServices.GetUninitializedObject(type);
                DeserializeObjectComponentJsonConstructor(obj, property, result);
                bool isReferenceSolve = false;
                if (result is ComponentJson componentJson && componentJson.Owner == null)
                {
                    componentJsonRoot = componentJson;
                    isReferenceSolve = true;
                }

                var declarationObject = DeclarationObjectGet(type);

                // Create empty lists.
                Dictionary<string, IList> valueListList = new Dictionary<string, IList>();
                foreach (var item in declarationObject.PropertyList.Values)
                {
                    if (item.IsList)
                    {
                        Type typeGeneric = item.Converter.PropertyTypeGeneric(item);
                        IList valueList = (IList)Activator.CreateInstance(typeGeneric);
                        valueListList.Add(item.PropertyName, valueList);
                        item.ValueListSet(result, valueList);
                    }
                }

                // Loop through json properties
                foreach (var jsonElementProperty in jsonElement.EnumerateObject())
                {
                    string propertyName = jsonElementProperty.Name;
                    if (declarationObject.PropertyList.TryGetValue(propertyName, out var item)) // Could be "$type"
                    {
                        if (item.IsList == false)
                        {
                            object value = item.Converter.Deserialize(result, item, jsonElementProperty.Value, componentJsonRoot);
                            item.ValueSet(result, value);
                        }
                        else
                        {
                            IList valueList = valueListList[item.PropertyName];
                            foreach (var jsonElementValue in jsonElementProperty.Value.EnumerateArray())
                            {
                                object value = null;
                                if (jsonElementValue.ValueKind == JsonValueKind.Null)
                                {
                                    UtilFramework.Assert(item.Converter.ValueDefault == null);
                                }
                                else
                                {
                                    value = item.Converter.Deserialize(result, item, jsonElementValue, componentJsonRoot);
                                }
                                valueList.Add(value);
                            }
                            item.ValueListSet(result, valueList);
                        }
                    }
                }

                if (result is ComponentJson resultComponentJson)
                {
                    resultComponentJson.Root.RootComponentJsonList.Add(resultComponentJson.Id, resultComponentJson);
                }
                if (isReferenceSolve)
                {
                    componentJsonRoot.RootReferenceSolve();
                }
                return result;
            }

            /// <summary>
            /// Deserialize value or object.
            /// </summary>
            /// <param name="obj">Object on which property is declared.</param>
            /// <param name="property">Property of object <paramref name="obj"/></param>
            internal object Deserialize(object obj, DeclarationProperty property, JsonElement jsonElement, ComponentJson componentJsonRoot)
            {
                if (IsObject == false)
                {
                    return DeserializeValue(obj, property, jsonElement);
                }
                else
                {
                    return DeserializeObject(obj, property, jsonElement, componentJsonRoot);
                }
            }

            internal object Utf8JsonReaderDeserialize(object obj, DeclarationProperty property, Utf8JsonReader reader)
            {
                if (IsObject == false)
                {
                    return Utf8JsonReaderDeserializeValue(obj, property, reader);
                }
                else
                {
                    return Utf8JsonReaderDeserializeObject(property, reader);
                }
            }

            /// <summary>
            /// Gets PropertyType. Declaring type. Can be null for example for Enum, Dto, Row and ComponentJson.
            /// </summary>
            public readonly Type PropertyType;

            /// <summary>
            /// Gets PropertyTypeGeneric. This is typeof(<PropertyType>).
            /// </summary>
            private readonly Type propertyTypeGeneric;

            public Type PropertyTypeGeneric(DeclarationProperty property)
            {
                var result = propertyTypeGeneric;
                if (result == null)
                {
                    result = TypeGenericList.GetOrAdd(property.PropertyType, (Type type) => typeof(List<>).MakeGenericType(property.PropertyType));
                }
                return result;
            }

            /// <summary>
            /// Gets IsObject. If false, it is a value type. If true it is an object.
            /// </summary>
            public readonly bool IsObject;

            /// <summary>
            /// Gets ValueDefault. Used to ignore default values.
            /// </summary>
            public readonly object ValueDefault;
        }

        private abstract class ConverterBase<T> : ConverterBase
        {
            public ConverterBase(bool isObject)
                : base(typeof(T), typeof(List<T>), isObject, default(T))
            {

            }
        }

        private sealed class ConverterInt : ConverterBase<int>
        {
            public ConverterInt()
                : base(false)
            {

            }

            protected override void SerializeValue(object obj, DeclarationProperty property, object value, Writer writer)
            {
                writer.WriteNumberValue((int)value);
            }

            protected override object DeserializeValue(object obj, DeclarationProperty property, JsonElement jsonElement)
            {
                return jsonElement.GetInt32();
            }
        }

        private sealed class ConverterIntNullable : ConverterBase<int?>
        {
            public ConverterIntNullable()
                : base(false)
            {

            }

            protected override void SerializeValue(object obj, DeclarationProperty property, object value, Writer writer)
            {
                writer.WriteNumberValue((int)value);
            }

            protected override object DeserializeValue(object obj, DeclarationProperty property, JsonElement jsonElement)
            {
                return jsonElement.GetInt32();
            }
        }

        private sealed class ConverterString : ConverterBase<string>
        {
            public ConverterString()
                : base(false)
            {

            }

            protected override void SerializeValue(object obj, DeclarationProperty property, object value, Writer writer)
            {
                writer.WriteStringValue((string)value);
            }

            protected override object DeserializeValue(object obj, DeclarationProperty property, JsonElement jsonElement)
            {
                return jsonElement.GetString();
            }
        }

        private sealed class ConverterBoolean : ConverterBase<bool>
        {
            public ConverterBoolean()
                : base(false)
            {

            }

            protected override void SerializeValue(object obj, DeclarationProperty property, object value, Writer writer)
            {
                writer.WriteBooleanValue((bool)value);
            }

            protected override object DeserializeValue(object obj, DeclarationProperty property, JsonElement jsonElement)
            {
                return jsonElement.GetBoolean();
            }
        }

        private sealed class ConverterBooleanNullable : ConverterBase<bool?>
        {
            public ConverterBooleanNullable()
                : base(false)
            {

            }

            protected override void SerializeValue(object obj, DeclarationProperty property, object value, Writer writer)
            {
                writer.WriteBooleanValue((bool)value);
            }

            protected override object DeserializeValue(object obj, DeclarationProperty property, JsonElement jsonElement)
            {
                return jsonElement.GetBoolean();
            }
        }

        private sealed class ConverterDouble : ConverterBase<double>
        {
            public ConverterDouble()
                : base(false)
            {

            }

            protected override void SerializeValue(object obj, DeclarationProperty property, object value, Writer writer)
            {
                writer.WriteNumberValue((double)value);
            }

            protected override object DeserializeValue(object obj, DeclarationProperty property, JsonElement jsonElement)
            {
                return jsonElement.GetDouble();
            }
        }

        private sealed class ConverterDoubleNullable : ConverterBase<double?>
        {
            public ConverterDoubleNullable()
                : base(false)
            {

            }

            protected override void SerializeValue(object obj, DeclarationProperty property, object value, Writer writer)
            {
                writer.WriteNumberValue((double)value);
            }

            protected override object DeserializeValue(object obj, DeclarationProperty property, JsonElement jsonElement)
            {
                return jsonElement.GetDouble();
            }
        }

        private sealed class ConverterDateTime : ConverterBase<DateTime>
        {
            public ConverterDateTime()
                : base(false)
            {

            }

            protected override void SerializeValue(object obj, DeclarationProperty property, object value, Writer writer)
            {
                writer.WriteStringValue(UtilFramework.DateTimeToText((DateTime)value));
            }

            protected override object DeserializeValue(object obj, DeclarationProperty property, JsonElement jsonElement)
            {
                return UtilFramework.DateTimeFromText(jsonElement.GetString());
            }
        }

        private sealed class ConverterDateTimeNullable : ConverterBase<DateTime?>
        {
            public ConverterDateTimeNullable()
                : base(false)
            {

            }

            protected override void SerializeValue(object obj, DeclarationProperty property, object value, Writer writer)
            {
                writer.WriteStringValue(UtilFramework.DateTimeToText((DateTime?)value));
            }

            protected override object DeserializeValue(object obj, DeclarationProperty property, JsonElement jsonElement)
            {
                return UtilFramework.DateTimeFromText(jsonElement.GetString());
            }
        }

        private sealed class ConverterType : ConverterBase<Type>
        {
            public ConverterType()
                : base(false)
            {

            }

            protected override void SerializeValue(object obj, DeclarationProperty property, object value, Writer writer)
            {
                string typeName = UtilFramework.TypeToName((Type)value, true);
                writer.WriteStringValue(typeName);
            }

            protected override object DeserializeValue(object obj, DeclarationProperty property, JsonElement jsonElement)
            {
                string typeName = jsonElement.GetString();
                var result = UtilFramework.TypeFromName(typeName);
                return result;
            }
        }

        private sealed class ConverterObjectRow : ConverterBase
        {
            public ConverterObjectRow()
                : base(false, null)
            {

            }

            protected override void SerializeValue(object obj, DeclarationProperty property, object value, Writer writer)
            {
                if (writer.IsSerializeClient)
                {
                    throw new Exception("Can not send data row to client!");
                }
                writer.WriteStartObject();
                writer.WriteString("$typeRow", UtilFramework.TypeToName(value.GetType(), true));
                writer.WritePropertyName("Row");
                writer.Serialize(value);
                writer.WriteEndObject();
            }

            protected override object DeserializeValue(object obj, DeclarationProperty property, JsonElement jsonElement)
            {
                string typeRowName = jsonElement.GetProperty("$typeRow").GetString();
                Type typeRow = UtilFramework.TypeFromName(typeRowName);
                string json = jsonElement.GetProperty("Row").GetRawText();
                var result = JsonSerializer.Deserialize(json, typeRow);
                return result;
            }
        }

        private sealed class ConverterObjectComponentJson : ConverterBase
        {
            public ConverterObjectComponentJson()
                : base(true, null)
            {

            }

            protected override void SerializeObjectType(DeclarationProperty property, object obj, Writer writer)
            {
                writer.SerializeStart(null, false);
                writer.WriteString("$typeComponent", UtilFramework.TypeToName(obj.GetType(), true));
                writer.SerializeEnd();
            }

            protected override string DeserializeObjectType(DeclarationProperty property, JsonElement jsonElement)
            {
                return jsonElement.GetProperty("$typeComponent").GetString();
            }
        }

        /// <summary>
        /// Serialize an object. Property type is object. Supports inheritance.
        /// </summary>
        private sealed class ConverterObjectValue : ConverterBase<object>
        {
            public ConverterObjectValue()
                : base(false)
            {

            }

            protected override void SerializeValue(object obj, DeclarationProperty property, object value, Writer writer)
            {
                Type propertyType = value.GetType();
                ConverterBase converter = ConverterGet(propertyType);
                UtilFramework.Assert(converter.IsObject == false, "Property of type object needs to store a value type!");
                UtilFramework.Assert(!(converter.GetType() == typeof(ConverterEnum) || converter.GetType() == typeof(ConverterEnumNullable)), "Enum not allowed in property of type object!");

                writer.WriteStartObject();
                writer.WriteString("$typeValue", UtilFramework.TypeToName(propertyType, true));
                writer.WritePropertyName("Value");
                converter.Serialize(obj, property, value, null, writer);
                writer.WriteEndObject();
                if (writer.IsSerializeClient == true)
                {
                    throw new Exception(); // Property of type object not supported for client serialization.
                }
            }

            protected override object DeserializeValue(object obj, DeclarationProperty property, JsonElement jsonElement)
            {
                string typeName = jsonElement.GetProperty("$typeValue").GetString();
                Type type = UtilFramework.TypeFromName(typeName);
                var converter = ConverterGet(type);
                var result = converter.Deserialize(obj, property, jsonElement.GetProperty("Value"), null);
                return result;
            }
        }

        private sealed class ConverterObjectRoot : ConverterBase
        {
            public ConverterObjectRoot()
                : base(true, null)
            {

            }

            protected override void SerializeObjectType(DeclarationProperty property, object obj, Writer writer)
            {
                writer.SerializeStart(null, false);
                writer.WriteString("$typeRoot", UtilFramework.TypeToName(obj.GetType(), true));
                writer.SerializeEnd();
            }

            protected override string DeserializeObjectType(DeclarationProperty property, JsonElement jsonElement)
            {
                return jsonElement.GetProperty("$typeRoot").GetString();
            }
        }

        /// <summary>
        /// Serialize a dto object. Property type and dto object type need to be identical.
        /// </summary>
        private sealed class ConverterObjectDto : ConverterBase
        {
            public ConverterObjectDto()
                : base(true, null)
            {

            }

            protected override void SerializeObjectType(DeclarationProperty property, object obj, Writer writer)
            {
                UtilFramework.Assert(property.PropertyType == obj.GetType(), "Property type and object type not equal!");
            }
        }

        private sealed class ConverterEnum : ConverterBase
        {
            public ConverterEnum()
                : base(false, 0)
            {

            }

            protected override bool IsValueDefault(object value)
            {
                return object.Equals((int)value, ValueDefault);
            }

            protected override void SerializeValue(object obj, DeclarationProperty property, object value, Writer writer)
            {
                writer.WriteNumberValue((int)value);
            }

            protected override object DeserializeValue(object obj, DeclarationProperty property, JsonElement jsonElement)
            {
                var resultInt = jsonElement.GetInt32();
                var resultEnum = Enum.ToObject(property.PropertyType, resultInt);
                return resultEnum;
            }
        }

        private sealed class ConverterEnumNullable: ConverterBase
        {
            public ConverterEnumNullable()
                : base(false, null)
            {

            }

            protected override void SerializeValue(object obj, DeclarationProperty property, object value, Writer writer)
            {
                writer.WriteNumberValue((int)value);
            }

            protected override object DeserializeValue(object obj, DeclarationProperty property, JsonElement jsonElement)
            {
                var resultInt = jsonElement.GetInt32();
                var resultEnum = Enum.ToObject(UtilFramework.TypeUnderlying(property.PropertyType), resultInt);
                return resultEnum;
            }
        }

        private static readonly JsonWriterOptions options = new JsonWriterOptions { };
        // private static readonly JsonWriterOptions options = new JsonWriterOptions { Indented = true }; // For debug only

        /// <summary>
        /// Serializes public and internal properties and fiels.
        /// </summary>
        /// <param name="obj">Object to serialize.</param>
        /// <param name="json">Json stored in server session.</param>
        /// <param name="jsonClient">Json sent to Angular client.</param>
        public static void Serialize(object obj, out string jsonSession, out string jsonClient)
        {
            using (var writer = new Writer(options))
            {
                if (obj is ComponentJson componentJson && componentJson.IsHide)
                {
                    writer.SerializeStart(true, false); // If root ComponentJson.IsHide = true
                }
                else
                {
                    writer.SerializeStart(true, obj is ComponentJson ? true : (bool?)null);
                }

                converterObjectRoot.Serialize(obj: null, property: null, obj, componentJsonRoot: null, writer);
                writer.SerializeEnd();

                writer.Return(out jsonSession, out jsonClient);
            }

            // DebugValidateJson(obj, json);
        }

        /// <summary>
        /// Validate json by deserializing it and comparing to obj.
        /// </summary>
        public static void DebugValidateJson(object obj, string json)
        {
            // Needs Newtonsoft.Json

            //string jsonSource = Newtonsoft.Json.JsonConvert.SerializeObject(obj, new Newtonsoft.Json.JsonSerializerSettings() { NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore, DefaultValueHandling = Newtonsoft.Json.DefaultValueHandling.Ignore });
            //object objDest = Deserialize(json);
            //string jsonDest = Newtonsoft.Json.JsonConvert.SerializeObject(objDest, new Newtonsoft.Json.JsonSerializerSettings() { NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore, DefaultValueHandling = Newtonsoft.Json.DefaultValueHandling.Ignore });
            //UtilFramework.Assert(jsonSource == jsonDest);
        }

        public static void DebugValidateJson(object source, object dest)
        {
            // Needs Newtonsoft.Json

            //string jsonSource = Newtonsoft.Json.JsonConvert.SerializeObject(source, new Newtonsoft.Json.JsonSerializerSettings() { TypeNameHandling = Newtonsoft.Json.TypeNameHandling.All });
            //string jsonDest = Newtonsoft.Json.JsonConvert.SerializeObject(dest, new Newtonsoft.Json.JsonSerializerSettings() { TypeNameHandling = Newtonsoft.Json.TypeNameHandling.All });
            //UtilFramework.Assert(jsonSource == jsonDest);
        }

        /// <summary>
        /// Deserialize server session json.
        /// </summary>
        /// <param name="json">Server session json.</param>
        /// <returns>Returns deserialized session object.</returns>
        public static object Deserialize(string json)
        {
            bool isUtf8JsonReader = false; // Use JsonDocument or Utf8JsonReader

            object result;
            if (isUtf8JsonReader == false)
            {
                JsonDocument document = JsonDocument.Parse(json);
                result = converterObjectRoot.Deserialize(null, null, document.RootElement, null);
            }
            else
            {
                var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(json));
                result = converterObjectRoot.Utf8JsonReaderDeserialize(null, null, reader);
            }

            return result;
        }
    }
}


text:
#ifndef ANDROID_CEDARTPLAYER_H
#define ANDROID_CEDARTPLAYER_H

#include <media/MediaPlayerInterface.h>

namespace android {

struct CedarXPlayer;

class CedarPlayer : public MediaPlayerInterface {
public:
    CedarPlayer();
    virtual ~CedarPlayer();

    virtual status_t initCheck();

    virtual status_t setUID(uid_t uid);

    virtual status_t setDataSource(
            const char *url, const KeyedVector<String8, String8> *headers);

    virtual status_t setDataSource(int fd, int64_t offset, int64_t length);

    virtual status_t setDataSource(const sp<IStreamSource> &source);

    virtual status_t setVideoSurface(const sp<Surface> &surface);
    virtual status_t setVideoSurfaceTexture(
            const sp<IGraphicBufferProducer> &surfaceTexture);
    virtual status_t prepare();
    virtual status_t prepareAsync();
    virtual status_t start();
    virtual status_t stop();
    virtual status_t pause();
    virtual bool isPlaying();
    virtual status_t seekTo(int msec);
    virtual status_t getCurrentPosition(int *msec);
    virtual status_t getDuration(int *msec);
    virtual status_t reset();
    virtual status_t setLooping(int loop);
    virtual player_type playerType();
    virtual status_t invoke(const Parcel &request, Parcel *reply);
    virtual void setAudioSink(const sp<AudioSink> &audioSink);
    virtual status_t setParameter(int key, const Parcel &request);
    virtual status_t getParameter(int key, Parcel *reply);
    virtual status_t setScreen(int screen);
    virtual int getMeidaPlayerState();

    virtual status_t getMetadata(
            const media::Metadata::Filter& ids, Parcel *records);

    //virtual status_t dump(int fd, const Vector<String16> &args) const;
    virtual int getSubCount();
    virtual int getSubList(MediaPlayer_SubInfo *infoList, int count);
    virtual int getCurSub();
    virtual status_t switchSub(int index);
    virtual status_t setSubGate(bool showSub);
    virtual bool getSubGate();
    virtual status_t setSubColor(int color);
    virtual int getSubColor();
    virtual status_t setSubFrameColor(int color);
    virtual int getSubFrameColor();
    virtual status_t setSubFontSize(int size);
    virtual int getSubFontSize();
    virtual status_t setSubCharset(const char *charset);
    virtual status_t getSubCharset(char *charset);
    virtual status_t setSubPosition(int percent);
    virtual int getSubPosition();
    virtual status_t setSubDelay(int time);
    virtual int getSubDelay();
    virtual int getTrackCount();
    virtual int getTrackList(MediaPlayer_TrackInfo *infoList, int count);
    virtual int getCurTrack();
    virtual status_t switchTrack(int index);
    virtual status_t setInputDimensionType(int type);
    virtual status_t setInputDimensionValue(int type, int value);
    virtual int getInputDimensionType();
    virtual status_t setOutputDimensionType(int type);
    virtual status_t setOutputDimensionValue(int type, int value);
    virtual int getOutputDimensionType();
    virtual status_t setAnaglaghType(int type);
    virtual int getAnaglaghType();
    virtual status_t getVideoEncode(char *encode);
    virtual int getVideoFrameRate();
    virtual status_t getAudioEncode(char *encode);
    virtual int getAudioBitRate();
    virtual int getAudioSampleRate();

    virtual status_t        enableScaleMode(bool enable, int width, int height);
    virtual status_t        setVppGate(bool enableVpp);
    virtual status_t        setLumaSharp(int value);
    virtual status_t        setChromaSharp(int value);
    virtual status_t        setWhiteExtend(int value);
    virtual status_t        setBlackExtend(int value);
    virtual status_t 		extensionControl(int command, int para0, int para1);
    virtual status_t 		generalInterface(int cmd, int int1, int int2, int int3, void *p);
    //don't add any extension interface in future!!

private:
    CedarXPlayer *mPlayer;

    CedarPlayer(const CedarPlayer &);
    CedarPlayer &operator=(const CedarPlayer &);
};

}  // namespace android

#endif  // ANDROID_CedarPlayer_H

redpajama_set_name: RedPajamaGithub


text:
<?php

namespace spec\Sylius\Bundle\CoreBundle\Provider;

use PhpSpec\ObjectBehavior;
use Sylius\Bundle\SettingsBundle\Model\SettingsInterface;
use Sylius\Component\Addressing\Model\ZoneInterface;
use Sylius\Component\Core\Model\ChannelInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Provider\ZoneProviderInterface;

/**
 * @author Mateusz Zalewski <mateusz.zalewski@lakion.com>
 */
final class ChannelBasedDefaultTaxZoneProviderSpec extends ObjectBehavior
{
    function it_is_initializable()
    {
        $this->shouldHaveType('Sylius\Bundle\CoreBundle\Provider\ChannelBasedDefaultTaxZoneProvider');
    }

    function it_implements_default_tax_zone_provider_interface()
    {
        $this->shouldImplement(ZoneProviderInterface::class);
    }

    function it_provides_default_tax_zone_from_order_channel(
        ChannelInterface $channel,
        OrderInterface $order,
        ZoneInterface $defaultTaxZone
    ) {
        $order->getChannel()->willReturn($channel);
        $channel->getDefaultTaxZone()->willReturn($defaultTaxZone);

        $this->getZone($order)->shouldReturn($defaultTaxZone);
    }
}


text:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>com.hpe.caf.worker.queue.rabbit Class Hierarchy (worker-framework 10.13.0-SNAPSHOT API)</title>
<meta name="date" content="2016-10-11">
<link rel="stylesheet" type="text/css" href="../../../../../../stylesheet.css" title="Style">

JavaScript is disabled on your browser.

Skip navigation links

Overview

Package

Class

Use

Tree

Deprecated

Index

Help

Prev

Next

Frames

No Frames

All Classes

Hierarchy For Package com.hpe.caf.worker.queue.rabbit

Package Hierarchies:

All Packages

Class Hierarchy

java.lang.Object

com.hpe.caf.worker.queue.rabbit.RabbitMetricsReporter (implements com.hpe.caf.api.worker.WorkerQueueMetricsReporter)

com.hpe.caf.worker.queue.rabbit.RabbitWorkerQueue (implements com.hpe.caf.api.worker.ManagedWorkerQueue)

com.hpe.caf.worker.queue.rabbit.RabbitWorkerQueueConfiguration

com.hpe.caf.worker.queue.rabbit.RabbitWorkerQueueProvider (implements com.hpe.caf.api.worker.WorkerQueueProvider)

com.hpe.caf.worker.queue.rabbit.WorkerConnectionListener (implements net.jodah.lyra.event.ConnectionListener)

com.hpe.caf.worker.queue.rabbit.WorkerPublisherImpl (implements com.hpe.caf.worker.queue.rabbit.WorkerPublisher)

com.hpe.caf.worker.queue.rabbit.WorkerPublishQueueEvent (implements com.hpe.caf.util.rabbitmq.Event<T>)

com.hpe.caf.worker.queue.rabbit.WorkerQueueConsumerImpl (implements com.hpe.caf.util.rabbitmq.QueueConsumer)

Interface Hierarchy

com.hpe.caf.worker.queue.rabbit.WorkerPublisher

Skip navigation links

Overview

Package

Class

Use

Tree

Deprecated

Index

Help

Prev

Next

Frames

No Frames

All Classes

Copyright © 2016. All rights reserved.


text:
<?php
namespace Eloquence\Database\Traits;

trait CamelCaseModel
{
    /**
     * Alter eloquent model behaviour so that model attributes can be accessed via camelCase, but more importantly,
     * attributes also get returned as camelCase fields.
     *
     * @var bool
     */
    public $enforceCamelCase = true;

    /**
     * Overloads the eloquent setAttribute method to ensure that fields accessed
     * in any case are converted to snake_case, which is the defacto standard
     * for field names in databases.
     *
     * @param string $key
     * @param mixed  $value
     */
    public function setAttribute($key, $value)
    {
        parent::setAttribute($this->getSnakeKey($key), $value);
    }

    /**
     * Retrieve a given attribute but allow it to be accessed via alternative case methods (such as camelCase).
     *
     * @param  string $key
     * @return mixed
     */
    public function getAttribute($key)
    {
        if (method_exists($this, $key)) {
            return $this->getRelationshipFromMethod($key);
        }

        return parent::getAttribute($this->getSnakeKey($key));
    }

    /**
     * Return the attributes for the model, converting field casing if necessary.
     *
     * @return array
     */
    public function attributesToArray()
    {
        return $this->toCamelCase(parent::attributesToArray());
    }

    /**
     * Converts the attributes to a camel-case version, if applicable.
     *
     * @return array
     */
    public function getAttributes()
    {
        return $this->attributesToArray();
    }

    /**
     * Get the model's relationships, converting field casing if necessary.
     *
     * @return array
     */
    public function relationsToArray()
    {
        return $this->toCamelCase(parent::relationsToArray());
    }

    /**
     * Converts a given array of attribute keys to the casing required by CamelCaseModel.
     *
     * @param mixed $attributes
     * @return array
     */
    public function toCamelCase($attributes)
    {
        $convertedAttributes = [];

        foreach ($attributes as $key => $value) {
            $key = $this->getTrueKey($key);
            $convertedAttributes[$key] = $value;
        }

        return $convertedAttributes;
    }

    /**
     * Get the model's original attribute values.
     *
     * @param  string  $key
     * @param  mixed   $default
     * @return array
     */
    public function getOriginal($key = null, $default = null)
    {
        return array_get($this->toCamelCase($this->original), $key, $default);
    }

    /**
     * Converts a given array of attribute keys to the casing required by CamelCaseModel.
     *
     * @param $attributes
     * @return array
     */
    private function toSnakeCase($attributes)
    {
        $convertedAttributes = [];

        foreach ($attributes as $key => $value) {
            $convertedAttributes[$this->getSnakeKey($key)] = $value;
        }

        return $convertedAttributes;
    }

    /**
     * Retrieves the true key name for a key.
     *
     * @param $key
     * @return string
     */
    public function getTrueKey($key)
    {
        // If the key is a pivot key, leave it alone - this is required internal behaviour
        // of Eloquent for dealing with many:many relationships.
        if ($this->isCamelCase() && strpos($key, 'pivot_') === false) {
            $key = camel_case($key);
        }

        return $key;
    }

    /**
     * Determines whether the model (or its parent) requires camelcasing. This is required
     * for pivot models whereby they actually depend on their parents for this feature.
     *
     * @return bool
     */
    public function isCamelCase()
    {
        return $this->enforceCamelCase or (isset($this->parent) && method_exists($this->parent, 'isCamelCase') && $this->parent->isCamelCase());
    }

	/**
	 * If the field names need to be converted so that they can be accessed by camelCase, then we can do that here.
	 *
	 * @param $key
	 * @return string
	 */
	protected function getSnakeKey($key)
	{
		return snake_case($key);
	}

    /**
     * Because we are changing the case of keys and want to use camelCase throughout the application, whenever
     * we do isset checks we need to ensure that we check using snake_case.
     *
     * @param $key
     * @return mixed
     */
    public function __isset($key)
    {
        $key = snake_case($key);

        return parent::__isset($key);
    }
}


text:
![](https://img.shields.io/github/license/mashape/apistatus.svg) ![](https://travis-ci.org/disassemble-io/agent-callback.svg)

A Java-Agent for inspecting method calls and their arguments at runtime


text:
import {Events} from "./Events";
import {ISeadragonExtension} from "./ISeadragonExtension";
import {ShareDialogue as BaseShareDialogue} from "../../modules/uv-dialogues-module/ShareDialogue";

export class ShareDialogue extends BaseShareDialogue {

    constructor($element: JQuery) {
        super($element);

        this.component.subscribe(Events.SEADRAGON_OPEN, () => {
            this.update();
        });

        this.component.subscribe(Events.SEADRAGON_ANIMATION_FINISH, () => {
            this.update();
        });
    }

    create(): void {
        this.setConfig('shareDialogue');
        super.create();
    }

    update(): void {

        super.update();

        const xywh: string = <string>(<ISeadragonExtension>this.extension).getViewportBounds();
        const rotation: number = <number>(<ISeadragonExtension>this.extension).getViewerRotation();

        this.code = (<ISeadragonExtension>this.extension).getEmbedScript(
            this.options.embedTemplate,
            this.currentWidth,
            this.currentHeight,
            xywh,
            rotation);

        this.$code.val(this.code);
    }

    resize(): void {
        super.resize();
    }
}


text:
package scorex.network

import akka.actor.{ActorRef, Props}
import akka.testkit.TestProbe
import org.h2.mvstore.MVStore
import scorex.ActorTestingCommons
import scorex.block.Block
import scorex.block.Block._
import scorex.network.NetworkController.DataFromPeer
import scorex.settings.SettingsMock
import scorex.transaction.{BlockStorage, History}

import scala.concurrent.duration.{FiniteDuration, _}
import scala.language.{implicitConversions, postfixOps}
import scala.util.Random

class BlockchainSynchronizerSpecification extends ActorTestingCommons {

  import BlockchainSynchronizer._
  import scorex.network.Coordinator._

  private def mockHistory(last: Int): History = {
    val history = mock[History]
    (history.contains(_: BlockId)) expects * onCall { id: BlockId => id(0) <= last } anyNumberOfTimes()
    history
  }

  private case object BlacklistAssertion
  private def setBlacklistExpectations(blacklist: Boolean): Unit = {
    (peer.blacklist _).when().onCall {
      _ => if (blacklist) self ! BlacklistAssertion else fail("No blacklisting should be in this case")
    }
  }

  private val lastHistoryBlockId = 10
  private val testHistory = mockHistory(lastHistoryBlockId)

  private val initialScore = BigInt(1000)

  testHistory.scoreOf _ expects * onCall {
    blockId: BlockId =>
      assert(BlockIdExtraction.extract(blockId) == lastHistoryBlockId, s"defined only for block id $lastHistoryBlockId")
      initialScore
  } noMoreThanOnce()

  private val testCoordinator = TestProbe("Coordinator")

  private val entireForkLoad = mockFunction[Boolean]
  private def setloadEntireForkChunk(value: Boolean) = entireForkLoad expects() returns value anyNumberOfTimes

  object TestSettings extends SettingsMock {
    override lazy val historySynchronizerTimeout: FiniteDuration = testDuration * 2
    override lazy val MaxRollback: Int = lastHistoryBlockId - 1
    override lazy val retriesBeforeBlacklisted: Int = 0
    override lazy val operationRetries: Int = retriesBeforeBlacklisted + 13930975
    override lazy val pinToInitialPeer: Boolean = true
    override lazy val loadEntireChain: Boolean = entireForkLoad()
  }

  private val blockScore = BigInt(100)

  private trait App extends ApplicationMock {

    private val testBlockStorage = mock[BlockStorage]

    consensusModule.blockScore _ when * returns blockScore

    override lazy val settings = TestSettings
    override lazy val coordinator: ActorRef = testCoordinator.ref
    override lazy val history: History = testHistory
    override val blockStorage: BlockStorage = testBlockStorage
  }

  private val app = stub[App]

  import app.basicMessagesSpecsRepo._

  private def reasonableTimeInterval = (TestSettings.historySynchronizerTimeout.toMillis / 2) millis

  private def validateStatus(status: Status): Unit = {
    actorRef ! GetSyncStatus
    expectMsg(status)
  }

  private def assertLatestBlockFromNonSyncPeer(): Unit = {
    val peer = stub[ConnectedPeer]

    val block = blockMock(lastHistoryBlockId + 3729047)
    actorRef ! DataFromPeer(BlockMessageSpec.messageCode, block, peer)
    testCoordinator.expectMsg(AddBlock(block, Some(peer)))
  }

  private def assertThatPeerGotBlacklisted(): Unit = {
    setBlacklistExpectations(true)
    testCoordinator.expectMsg(reasonableTimeInterval, SyncFinished.unsuccessfully)
    expectMsg(BlacklistAssertion)
  }

  private def assertPeerNeverGotBlacklisted(): Unit = setBlacklistExpectations(false)

  private def expectedGetSignaturesSpec(blockIds: Int*): Unit = expectNetworkMessage(GetSignaturesSpec, blockIds.toSeq)
  private def sendBlock(block: Block): Unit = dataFromNetwork(BlockMessageSpec, block)
  private def sendSignatures(blockIds: BlockId*): Unit = dataFromNetwork(SignaturesSpec, blockIds.toSeq)

  protected override val actorRef = system.actorOf(Props(classOf[BlockchainSynchronizer], app))

  testSafely {

    validateStatus(Idle)
    assertLatestBlockFromNonSyncPeer()

    val t = System.currentTimeMillis()

    def adjustedTimeout(correction: Float): Long = {
      val withElapsedTime = TestSettings.historySynchronizerTimeout.toMillis - (System.currentTimeMillis() - t)
      withElapsedTime * correction toLong
    }
    def aBitLessThanTimeout = adjustedTimeout(0.9f) millis
    def aBitLongerThanTimeout = adjustedTimeout(1.1f) millis

    testHistory.lastBlockIds _ expects TestSettings.MaxRollback returns blockIds(lastHistoryBlockId, 9) // ids come in reverse order
    actorRef ! GetExtension(Map(peer -> 0))
    expectedGetSignaturesSpec(lastHistoryBlockId, 9)

    validateStatus(GettingExtension)

    "one block is a sign to start getting blocks" in {
      sendSignatures(lastHistoryBlockId, 11)
      expectNetworkMessage(GetBlockSpec, 11)
      validateStatus(GettingBlocks)
    }

    "at least one block id in response must be among requested ones" in {
      val notInTheHistoryBlockId = lastHistoryBlockId + 1
      val notRequestedBlockFromHistoryBeginning = 1

      assertPeerNeverGotBlacklisted()

      sendSignatures(notRequestedBlockFromHistoryBeginning, notInTheHistoryBlockId)

      testCoordinator.expectMsg(reasonableTimeInterval, SyncFinished.unsuccessfully)
    }

    "become idle on timeout in GettingExtension" in {
      assertPeerNeverGotBlacklisted()

      testCoordinator.expectNoMsg(aBitLessThanTimeout)
      testCoordinator.expectMsg(SyncFinished.unsuccessfully)

      validateStatus(Idle)
    }

    "go to GettingExtension" - {

      assertLatestBlockFromNonSyncPeer()

      sendSignatures(9, lastHistoryBlockId, 11, 12, 13)

      expectedGetSignaturesSpec(13, 12)

      "sending same signatures twice should not lead to blacklisting" in {
        assertPeerNeverGotBlacklisted()
        sendSignatures(9, lastHistoryBlockId, 11, 12, 13)
      }

      "go to GettingExtensionTail" - {

        validateStatus(GettingExtensionTail)

        val validBlockIds = blockIds(13, 14, 15)

        "extension tail from another peer(s) should not lead to the peers blacklisting" in {
          assertPeerNeverGotBlacklisted()
          dataFromNetwork(SignaturesSpec, validBlockIds, stub[ConnectedPeer])
        }

        "blacklist on timeout in states following GettingExtension" in {
          testCoordinator.expectNoMsg(aBitLessThanTimeout)

          assertThatPeerGotBlacklisted()

          validateStatus(Idle)
        }

        "follow ledger download scenario" - {

          sendSignatures(validBlockIds: _*)

          expectedGetSignaturesSpec(15, 14)

          sendSignatures(14, 15)

          val finalBlockIdInterval = 11 to 15

          validateStatus(GettingBlocks)

          "react on GetExtension in the Idle state only" in {
            actorRef ! GetExtension(Map(peer -> 10000))

            validateStatus(GettingBlocks)
          }

          "blocks loading" - {

            assertLatestBlockFromNonSyncPeer()

            val numberOfBlocks = finalBlockIdInterval.size

            def setHistoryScoreExpectations(delta: BigInt): Unit =
              testHistory.score _ expects() returns (initialScore + (numberOfBlocks * blockScore) + delta) repeat (0 to numberOfBlocks)

            def sendBlocks(): Unit = {
              finalBlockIdInterval foreach { expectNetworkMessage(GetBlockSpec, _) }
              Random.shuffle(finalBlockIdInterval) foreach { id => sendBlock(blockMock(id)) }
            }

            def assertThatBlocksLoaded(): Unit = {
              assertPeerNeverGotBlacklisted()

              testCoordinator.expectMsgPF(hint = s"$numberOfBlocks fork blocks") {
                case SyncFinished(true, Some((lastCommonBlockId, blockIterator, Some(connectedPeer)))) =>
                  connectedPeer shouldBe peer
                  BlockIdExtraction.extract(lastCommonBlockId) shouldBe lastHistoryBlockId

                  val forkStorageBlockIds = blockIterator.map(id => InnerId(id.uniqueId)).toSeq
                  forkStorageBlockIds shouldBe blockIds(finalBlockIdInterval: _*).map(InnerId)
              }

              validateStatus(Idle)
            }

            "entire fork loading" - {

              setloadEntireForkChunk(true)

              "fork has two blocks better score" in {
                setHistoryScoreExpectations(-(blockScore * 2 + 1))

                sendBlocks()

                assertThatBlocksLoaded()
              }
            }

            "partial fork loading" - {

              setloadEntireForkChunk(false)

              "fork has lower score" in {
                setHistoryScoreExpectations(1)

                assertPeerNeverGotBlacklisted()

                sendBlocks()

                testCoordinator.expectMsg(reasonableTimeInterval, SyncFinished.unsuccessfully)

                validateStatus(Idle)
              }

              "fork has better score" - {

                setHistoryScoreExpectations(-1)

                "same block twice should not reset timeout" in {
                  val firstSubsequentBlockId = finalBlockIdInterval.head

                  sendBlock(blockMock(firstSubsequentBlockId))

                  Thread sleep aBitLessThanTimeout.toMillis

                  sendBlock(blockMock(firstSubsequentBlockId))

                  assertThatPeerGotBlacklisted()
                }

                "happy path" in {
                  sendBlocks()
                  assertThatBlocksLoaded()
                }
              }
            }
          }
        }
      }
    }

    "a (sub)sequience of block ids to download" - {

      implicit def toInnerIds(i: Seq[Int]): InnerIds = i.map(toInnerId)
      implicit def toInnerId(i: Int): InnerId = InnerId(Array(i.toByte))

      def historyContaining(blockIds: Int*): History = {
        val history = mock[History]
        (history.contains(_: BlockId)) expects * onCall { id: BlockId => blockIds.contains(id.head.toInt) } anyNumberOfTimes()
        history
      }

      def test(blockIds: InnerIds, h: History, expectedLastCommon: InnerId, expected: Seq[Int]): Unit = {
        val Some((commonId, tail)) = BlockchainSynchronizer.blockIdsToStartDownload(blockIds, h)
        commonId shouldBe expectedLastCommon
        tail should contain theSameElementsInOrderAs toInnerIds(expected)
      }

      "a sample sequience" in {
        test(Seq(1, 2, 3, 4), historyContaining(1, 2), 2, Seq(3, 4))
      }

      "all blocks are in history" in {
        test(Seq(1, 2, 3, 4), historyContaining(1, 2, 3, 4), 4, Seq())
      }

      "suspicious block id" in {
        test(Seq(1, 2, 3, 4), historyContaining(1, 3), 1, Seq(2, 3, 4))
      }

      "first block(s) are not in history" in {
        blockIdsToStartDownload(Seq(10000, 2, 3, 4), historyContaining(1, 2, 3)) shouldEqual None
      }
    }
  }
}


text:
{% extends 'core/base.html' %}

{% block content %}

State Export/Import

It is possible to export and import "states" in Combine, including the hierarchy of Organizations and
                Record Groups, Jobs, Records (including Validations and Mapped Fields), and even reusable configuration
                scenarios. Exporting and Importing state is a great way to:

export resources from Combine in preparation for reinstalling or upgrading, so they can be imported
                    again

export interconnected groups of Jobs, or even full Organizations or Record Groups, to import to
                    another installation of Combine

share and/or backup configurations (e.g. Validation or Transformation Scenarios, or OAI endpoints
                    configurations)

The tables below show state Exports and Imports that are running or
                completed.

Exports

                    Task ID
                    Export ID
                    Export Name
                    Export Path
                    Status
                    Exported Objects
                    Manifests
                    Actions

                {% for export in stateio_exports %}

                        {{ export.id }}
                        {{ export.export_id }}
                        {{ export.name }}
                        {{ export.export_path }}

                                {{ export.progress_bar_status }}

                        {{ export.export_manifest.exports.count }}

                            <button type="button" class="btn btn-outline-info btn-sm">Export Manifest</button>

                            <button type="button" class="btn btn-outline-info btn-sm">Details</button>

                            <button type="button" class="btn btn-outline-success btn-sm">Download </button>
                         {% if not export.finished %}
                            <button type="button" class="btn btn-outline-danger btn-sm">Stop
                            </button>
                        {% else %}
                            <button type="button" class="btn btn-outline-danger btn-sm">Delete </button>
                        {% endif %}

                {% endfor %}

                <button type="button" class="btn btn-success btn-sm">Export State
                </button>

Imports

                    Task ID
                    Export ID
                    Export Name
                    Import Name
                    Import Path
                    Status
                    Imported Objects
                    Manifests
                    Actions

                {% for import in stateio_imports %}

                        {{ import.id }}
                        {{ import.export_manifest.export_id }}
                        {{ import.export_manifest.export_name }}
                        {{ import.name }}
                        {{ import.import_path }}

                                {{ import.progress_bar_status }}

                        {{ import.import_manifest.imports.count }}

                            <button type="button" class="btn btn-outline-info btn-sm">Export Manifest</button>

                            <button type="button" class="btn btn-outline-info btn-sm">Import Manifest</button>

                            <button type="button" class="btn btn-outline-info btn-sm">Details</button>
                         {% if not import.finished %}
                            <button type="button" class="btn btn-outline-danger btn-sm">Stop
                            </button>
                        {% else %}
                            <button type="button" class="btn btn-outline-danger btn-sm">Delete </button>
                        {% endif %}

                {% endfor %}

                <button type="button" class="btn btn-success btn-sm">Import State </button>

{% endblock %}


text:
package pl.jojczykp.cdstore.client.tracks;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.GenericType;
import lombok.AllArgsConstructor;
import pl.jojczykp.cdstore.albums.AlbumId;
import pl.jojczykp.cdstore.client.Request;
import pl.jojczykp.cdstore.tracks.Track;

import java.util.List;

import static javax.ws.rs.core.Response.Status.OK;
import static lombok.AccessLevel.PRIVATE;
import static org.assertj.core.api.Assertions.assertThat;
import static pl.jojczykp.cdstore.tracks.TracksResource.TRACK_LIST_MEDIA_TYPE;

@AllArgsConstructor(access = PRIVATE)
public class GetTracksRequest extends Request {

	private AlbumId albumId;

	public static GetTracksRequest aGetTracksRequest(AlbumId albumId) {
		return new GetTracksRequest(albumId);
	}

	public List<Track> makeSuccessfully() {
		ClientResponse response = make();
		assertThat(response.getStatus()).isEqualTo(OK.getStatusCode());

		return response.getEntity(new GenericType<List<Track>>() {});
	}

	public ClientResponse make() {
		Client client = Client.create();

		ClientResponse response = client
				.resource(String.format("%s/albums/%s/tracks", serverUrl, albumId))
				.accept(TRACK_LIST_MEDIA_TYPE)
				.get(ClientResponse.class);

		response.bufferEntity();

		return response;
	}

}


text:
package com.tritondigital.counters
package datadog

import java.net.InetSocketAddress
import java.util.concurrent.TimeUnit

import _root_.akka.actor._
import _root_.akka.io.UdpConnected._
import _root_.akka.io.{IO, UdpConnected}
import _root_.akka.pattern._
import _root_.akka.util.Timeout

import scala.concurrent.duration._
import scala.language.postfixOps

class DatadogPublisher(system: ActorSystem, metricsSystem: Metrics, commonTags: Seq[Tag], filter: ScalaMetricFilter, providers: MetricsProvider*) extends MetricsPublisher with Logging {
  require(system != null, "system must not be null")
  require(metricsSystem != null, "metricsSystem must not be null")
  require(commonTags != null, "commonTags must not be null")
  require(filter != null, "filter must not be null")

  private [this] val config =
    system.settings.config

  private [this] val address =
    new InetSocketAddress(config.getString("tritondigital_counters.datadog.host"), config.getInt("tritondigital_counters.datadog.port"))

  private [this] var datadogConnection =
    Option.empty[ActorRef]

  private [this] var connectionIteration =
    1

  private [this] implicit val timeout =
    Timeout(90, TimeUnit.SECONDS)

  private [this] val commonTagsToSend =
    commonTags.filter(_.key != "host") // host is added naturally by Datadog, this is unnecessary

  private [datadog] case class Publish(metrics: Iterable[Metric])
  private [datadog] case class Ack(remaining: Iterable[Metric], count: Int, timeoutHandler: Cancellable) extends Event
  private [datadog] case class SuspiciousConnection(replyTo: ActorRef) // When OS is taking a bit too much time to ack one write. Better restart the connection.
  private [datadog] case object CleanupQuarantineFinished

  def publish(metrics: Iterable[Metric]) = {
    (ensureConnection ? Publish(metrics)).mapTo[Unit]
  }

  private [datadog] def ensureConnection = {
    this.synchronized {
      if (datadogConnection.isEmpty) {
        datadogConnection = Some(system.actorOf(Props(new DatadogConnection), s"datadog-connection-$connectionIteration"))
        connectionIteration += 1
      }
      datadogConnection.get
    }
  }

  /** Handles non reliable connection to Datadog agent **/
  private [datadog] class DatadogConnection extends Actor {
    import context.dispatcher

    private [this] var publishedKeys = Set.empty[MetricKey]

    // This connection is a finite state machine with 4 states (initial, waiting for connection with metrics, connected, sending).
    // Did not used Akka FSM because I am not sure it would be more readable.

    val receive = sleeping

    def sleeping: Receive = {
      case Publish(metrics) =>
        initiateConnection() // Wait an actual publication request to initiate a connection
        context become waitForConnection(metrics, sender())

      case Ack(_, _, timeoutHandler) =>
        timeoutHandler.cancel()

      case other =>
        errorManagement("sleeping")(other)
    }

    def waitForConnection(toPublish: Iterable[Metric], replyTo: ActorRef): Receive = {
      case _: Publish =>
        log.warn("[wait for connection] Busy connecting. Ignoring publication request.")

      case Ack(_, _, timeoutHandler) =>
        timeoutHandler.cancel()

      case _: Connected =>
        startSending("wait for connection", registerConnection, toPublish, replyTo)

      case other =>
        errorManagement("wait for connection")(other)
    }

    def connected(socket: ActorRef): Receive = {
      case Publish(metrics) =>
        startSending("connected", socket, metrics, sender())

      case other =>
        errorManagement("connected", Some(socket))(other)
    }

    def sending(socket: ActorRef, replyTo: ActorRef): Receive = {
      case _: Publish =>
        log.warn("[sending] Busy writing to socket. Ignoring publication request.")

      case Ack(remaining, count, timeoutHandler) =>
        timeoutHandler.cancel()

        if (remaining.isEmpty) {
          if (log.isDebugEnabled) log.debug(s"[sending] Published $count metrics.")
          context become connected(socket)

          metricsSystem.setGaugeValue("datadog.timeseries.count", publishedKeys.size)

          replyTo ! (())
        }
        else {
          socket ! prepareForWrite(remaining, count, replyTo)
        }

      case other =>
        errorManagement("sending", Some(socket))(other)
    }

    def cleaningConnection(timeoutHandler: Cancellable): Receive = {
      case Disconnected =>
        timeoutHandler.cancel()
        log.debug("[cleaning connection] Received proper disconnection.")
        context become sleeping

      case CleanupQuarantineFinished =>
        log.warn("[cleaning connection] Received no disconnection after 200ms. Restarting process from scratch.")
        context become sleeping
    }

    def errorManagement(stateName: String, socket: Option[ActorRef] = None): Receive = {
      case _: Connected =>
        log.warn(s"[$stateName] Received an unexpected connection confirmation. Ignoring it.")
        () // Ignore

      case CommandFailed(_: Connect) =>
        metricsSystem.markMeter("datadog.failed_connection")
        giveUp(s"[$stateName] Connection to Datadog agent failed. Are you sure '${address.getHostName}' is resolving to the right host? In particular, if you are on Mac, 'localhost' is sometimes resolving on a IPv6 address. If you are still experiencing issues, you can hard code the desired IP in the tritondigital_counters.datadog.host configuration property.")

      case CommandFailed(Send(payload, _)) =>
        throw new Exception(s"[$stateName] Received OS overflow for ${payload.utf8String}. Not supposed to since we are waiting for a ACK before sending next metrics.")

      case Received(payload) =>
        // Datadog agent is replying only to signal errors
        socket.foreach(_ ! Disconnect)
        giveUp(s"[$stateName] Datadog agent reported an error: ${payload.utf8String}. Restarting connection.")

      case PoisonPill =>
        log.debug(s"[$stateName] Datadog agent connection is being killed.")
        socket.foreach(_ ! Disconnect)

      case SuspiciousConnection(replyTo) =>
        replyTo ! (())
        giveUp(s"[$stateName] Datadog connection seem to hang.")

      case Disconnected =>
        giveUp(s"[$stateName] Datadog agent connection has been closed.")
    }

    private def initiateConnection() {
      log.info(s"Trying to connect to Datadog agent at ${address.getHostName}:${address.getPort} ....")
      IO(UdpConnected)(context.system) ! UdpConnected.Connect(self, address)
    }

    private def registerConnection = {
      val socket = sender()
      log.info("[wait for connection] Successfully connected to Datadog agent.")
      metricsSystem.markMeter("datadog.connection")
      socket
    }

    private def startSending(stateName: String, socket: ActorRef, metrics: Iterable[Metric], replyTo: ActorRef) {
      log.debug(s"[$stateName] Publishing metrics to Datadog.")
      metricsSystem.markMeter("datadog.publication")

      val deduplicated = filter.filter(metrics)
      socket ! prepareForWrite(deduplicated, 1, replyTo)

      context become sending(socket, replyTo)
    }

    private def prepareForWrite(metrics: Iterable[Metric], count: Int, replyTo: ActorRef) = {
      val metric = metrics.head

      publishedKeys += metric.key
      Send(
        metric.copy(tags = metric.tags ++ commonTagsToSend).toDatadogBytes,
        Ack(metrics.tail, count + 1, buildSendAckTimeout(replyTo))
      )
    }

    private def buildSendAckTimeout(replyTo: ActorRef) =
      system.scheduler.scheduleOnce(500 millis, self, SuspiciousConnection(replyTo))

    private def buildConnectionCleanupTimeout =
      system.scheduler.scheduleOnce(200 millis, self, CleanupQuarantineFinished)

    private def giveUp(msg: String): Unit = {
      log.warn(msg)

      // Restart the process
      context become cleaningConnection(buildConnectionCleanupTimeout)
    }
  }}