Custom evidence reader (Json) C#

// --------------------------------------------------------------------------------------------------------------------
// <copyright file="JsonReaderExample.cs" company="Bayes Server">
//   Copyright (C) Bayes Server.  All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

namespace BayesServer.HelpSamples
{ 
    using BayesServer.Data;
    using BayesServer.Inference;
    using Newtonsoft.Json;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Text;
    using System.Threading;

    /// <summary>
    /// An example of creating a custom implementation of IEvidenceReaderCommand and IEvidenceReader for reading Json.
    /// </summary>
    /// <remarks>
    /// This example makes use of the Json.Net package (http://www.newtonsoft.com/json) which can be installed via NuGet.
    /// </remarks>
    public static class JsonReaderExample
    {
        private const string HairLengthName = "Hair Length";
        private const string HeightName = "Height";

        static void Main(string[] args)
        {
            var network = new Network();

            // TODO change the path to match your version of Bayes Server
            network.Load(@"C:\ProgramData\Bayes Server 7.10\Sample Networks\IdentificationExtended.bayes");

            JsonStringExample(network);
            JsonStreamExample(network);
        }

        /// <summary>
        /// A class to hold a few properties read from Json.  See Json.Net package for more information.
        /// </summary>
        /// <remarks>
        /// Json.NET also supports the dynamic keyword which can be used instead of defining classes like this.
        /// </remarks>
        private sealed class Person
        {
            public string HairLength
            {
                get; set;
            }

            public double Height
            {
                get; set;
            }
        }

        /// <summary>
        /// A simple helper class that caches references to variables of interest and sets evidence on them.
        /// </summary>
        private sealed class JsonEvidenceMapper
        {
            private Variable hairLength;
            private Variable height;

            public JsonEvidenceMapper(Network network)
            {
                if (network == null)
                    throw new ArgumentNullException("network");

                this.hairLength = network.Variables[HairLengthName, true];
                this.height = network.Variables[HeightName, true];
            }

            public void SetEvidence(IEvidence evidence, Person person)
            {
                evidence.SetState(this.hairLength.States[person.HairLength, true]);
                evidence.Set(this.height, person.Height);
            }
             
        }

        /// <summary>
        /// Example that converts Json data to Bayes Server Evidence.
        /// </summary>
        private static void JsonStringExample(Network network)
        {
            var jsonPerson1 = "{\"HairLength\": \"Medium\",\"Height\": 110}";
            var jsonPerson2 = "{\"HairLength\": \"Long\",\"Height\": 88}";

            var hairLength = network.Variables[HairLengthName, true];
            var height = network.Variables[HeightName, true];

            var evidenceMapper = new JsonEvidenceMapper(network);
            var evidence = new Evidence(network);   // NOTE that if you performing inference, the Inference Engine has a property called Evidence which can be used instead

            {
                var person1 = JsonConvert.DeserializeObject<Person>(jsonPerson1);
                evidenceMapper.SetEvidence(evidence, person1);

                // Test the evidence ...
                PrintPersonEvidence(evidence, hairLength, height);
            }

            {
                var person2 = JsonConvert.DeserializeObject<Person>(jsonPerson2);
                evidenceMapper.SetEvidence(evidence, person2);

                // Test the evidence ...
                PrintPersonEvidence(evidence, hairLength, height);
            }

        }

        /// <summary>
        /// Custom implementation of IEvidenceReaderCommand that is a factory for creating IEvidenceReader instances.
        /// </summary>
        private class JsonEvidenceReaderCommand<T> : IEvidenceReaderCommand
        {
            private Action<IEvidence, T> setEvidence;
            private Func<Stream> newStream;

            public JsonEvidenceReaderCommand(Action<IEvidence, T> setEvidence, Func<Stream> newStream)
            {
                if (setEvidence == null)
                    throw new ArgumentNullException("setEvidence");

                if (newStream == null)
                    throw new ArgumentNullException("newStream");

                this.setEvidence = setEvidence;
                this.newStream = newStream;
            }

            public IEvidenceReader ExecuteReader()
            {
                return new JsonEvidenceReader<T>(this.setEvidence, this.newStream());
            }
        }

        /// <summary>
        /// A custom implementation of IEvidenceReader.
        /// </summary>
        private class JsonEvidenceReader<T> : IEvidenceReader
        {
            private Stream stream;
            private IEnumerator<T> enumerator;
            private Action<IEvidence, T> setEvidence;

            private const int DisposedTrue = -1;
            private const int DisposedFalse = 0;
            private int disposed = DisposedFalse;

            public JsonEvidenceReader(Action<IEvidence, T> setEvidence, Stream stream)
            {
                if (stream == null)
                    throw new ArgumentNullException("stream");

                if (setEvidence == null)
                    throw new ArgumentNullException("setEvidence");

                this.stream = stream;
                this.enumerator = EnumerateJson<T>(this.stream).GetEnumerator();
                this.setEvidence = setEvidence; 

            }

            public void Dispose()
            {
                Dispose(true);
            }

            private void Dispose(bool disposing)
            {
                if (Interlocked.Exchange(ref this.disposed, DisposedTrue) == DisposedFalse)
                {
                    if(this.stream != null)
                    {
                        this.stream.Close();
                        this.stream = null;
                    }
                }
            }

            ~JsonEvidenceReader()
            {
                Dispose(false);
                GC.SuppressFinalize(this);
            }

            public bool Read(IEvidence evidence, IReadOptions readOptions)
            {
                if (this.disposed == DisposedTrue)
                {
                    throw new ObjectDisposedException(this.GetType().Name);
                }

                var result = this.enumerator.MoveNext();

                if (result)
                {
                    this.setEvidence(evidence, this.enumerator.Current);
                }

                return result;
            }
        }

        private static IEnumerable<TResult> EnumerateJson<TResult>(Stream stream)
        {
            var serializer = new JsonSerializer();

            using (var reader = new StreamReader(stream))
            {
                using (var jsonReader = new JsonTextReader(reader))
                {
                    jsonReader.SupportMultipleContent = true;

                    while (jsonReader.Read())
                    {
                        yield return serializer.Deserialize<TResult>(jsonReader);
                    }
                }
            }
        }

        /// <summary>
        /// Returns a stream of Json data.
        /// </summary>
        private static Stream GetJsonStream()
        {
            // We are using an in memory stream built from a string here to keep the example simple,
            // but this could be returned from an HttpClient or from an ElasticSearch client etc...

            var jsonString = "{\"HairLength\":\"Long\",\"Height\":100.0}{\"HairLength\":\"Medium\",\"Height\":120.0}{\"HairLength\":\"Medium\",\"Height\":99.0}";

            return new MemoryStream(Encoding.UTF8.GetBytes(jsonString));

        }

        private static void JsonStreamExample(Network network)
        {
            var mapper = new JsonEvidenceMapper(network);

            var evidenceReaderCommand = new JsonEvidenceReaderCommand<Person>(
                (evidence, p) => mapper.SetEvidence(evidence, p),
                () => GetJsonStream()    // instead could use HttpClient or ElasticSearch client here for example
                );

            // the above evidenceReaderCommand can be passed to various Bayes Server API calls such as 
            // parameter learning

            TestEvidenceReader(network, evidenceReaderCommand);
           
        }

        /// <summary>
        /// Test our custom implementation if IEvidenceReaderCommand and IEvidenceReader are working.
        /// </summary>
        private static void TestEvidenceReader(Network network, IEvidenceReaderCommand evidenceReaderCommand)
        {
            var readOptions = new ReadOptions();

            var hairLength = network.Variables[HairLengthName, true];
            var height = network.Variables[HeightName, true];

            var testEvidence = new Evidence(network);

            using (var evidenceReader = evidenceReaderCommand.ExecuteReader())
            {
                while (evidenceReader.Read(testEvidence, readOptions))
                {
                    PrintPersonEvidence(testEvidence, hairLength, height);
                }
            }
        }

        private static void PrintPersonEvidence(IEvidence evidence, Variable hairLength, Variable height)
        {
            Console.WriteLine("Person...");
            Console.WriteLine("\t" + hairLength.States[evidence.GetState(hairLength).Value].Name);
            Console.WriteLine("\t" + evidence.Get(height).Value);
        }

       
    }
}