<commit_before>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ExoGraph;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Web.Script.Serialization;
using ExoRule.Validation;
using ExoRule;

namespace ExoWeb
{
	public static class ExoWeb
	{
		#region Fields

		static readonly Regex dateRegex = new Regex("\"\\\\/Date\\((?<ticks>-?[0-9]+)(?:[a-zA-Z]|(?:\\+|-)[0-9]{4})?\\)\\\\/\"", RegexOptions.Compiled);
		static string cacheHash;
		static JavaScriptSerializer serializer;
		static MethodInfo deserialize;
		static long minJsonTicks = new DateTime(0x7b2, 1, 1, 0, 0, 0, DateTimeKind.Utc).Ticks;


		#endregion

		#region Constructors

		static ExoWeb()
		{
			Adapter = new ServiceAdapter();
			InitializeSerialization();
		}

		#endregion

		#region Properties

		/// <summary>
		/// Gets a unique cache key to use when referencing client scripts to ensure that application
		/// changes cause browsers to pull down new type schema information.
		/// </summary>
		public static string CacheHash
		{
			get
			{
				if (cacheHash == null)
				{
					int code = 0;

					foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies())
						code = code == 0 ? a.GetHashCode() : code ^ a.GetHashCode();

					cacheHash = code.ToString();
				}
				return cacheHash;
			}
		}

		public static ServiceAdapter Adapter { get; set; }

		/// <summary>
		/// Indicates whether all error information should be available on 
		/// the client.  This option should be used with caution.
		/// </summary>
		public static bool EnableExceptionInformation { get; set; }

		#endregion

		#region Events

		public static event EventHandler<ServiceRequestEventArgs> BeginRequest;

		public static event EventHandler<ServiceRequestEventArgs> EndRequest;

		public static event EventHandler<ServiceErrorEventArgs> Error;

		public static event EventHandler<SerializeEventArgs> Serialize;

		/// <summary>
		/// Raises the <see cref="BeginRequest"/> event for the specified <see cref="ServiceRequest"/>.
		/// </summary>
		/// <param name="error"></param>
		internal static void OnBeginRequest(ServiceRequest request)
		{
			if (BeginRequest != null)
				BeginRequest(request, new ServiceRequestEventArgs(request));
		}

		/// <summary>
		/// Raises the <see cref="EndRequest"/> event for the specified <see cref="ServiceRequest"/>.
		/// </summary>
		/// <param name="error"></param>
		internal static void OnEndRequest(ServiceRequest request)
		{
			if (EndRequest != null)
				EndRequest(request, new ServiceRequestEventArgs(request));
		}

		/// <summary>
		/// Raises the <see cref="Error"/> event for the specified <see cref="ServiceError"/>.
		/// </summary>
		/// <param name="error"></param>
		internal static void OnError(ServiceError error)
		{
			if (Error != null)
				Error(error, new ServiceErrorEventArgs(error));
		}

		/// <summary>
		/// Raises the <see cref="Serialize"/> event for the specified <see cref="GraphInstance"/>.
		/// </summary>
		/// <param name="error"></param>
		internal static void OnSerialize(GraphInstance instance)
		{
			if (Serialize != null)
				Serialize(instance, new SerializeEventArgs(instance));
		}

		#endregion

		#region Methods

		/// <summary>
		/// Determines whether a given property should be included in the
		/// model on the client.  
		/// </summary>
		/// <param name="property">The graph property that may be included.</param>
		/// <returns>A boolean value indicating whether to include the property.</returns>
		internal static bool IncludeInClientModel(GraphProperty property)
		{
			return !(property is GraphValueProperty) ||
				JsonConverter.GetJsonValueType(((GraphValueProperty)property).PropertyType) != null;
		}

		internal static string FixJsonDates(string json)
		{
			return dateRegex.Replace(json,
				(match) =>
				{
					var date = new DateTime((long.Parse(match.Groups["ticks"].Value) * 0x2710L) + minJsonTicks, DateTimeKind.Utc);
					return date.ToString(@"\""yyyy-MM-dd\THH:mm:ss.fff\Z\""");
				});
		}

		public static void RegisterForSerialization(Assembly assembly)
		{
			serializer.RegisterConverters(JsonConverter.Infer(assembly.GetTypes()).Cast<JavaScriptConverter>());
		}

		public static string Load(string type, string id, params string[] paths)
		{
			return Load(GraphContext.Current.GetGraphType(type), id, paths);
		}

		public static string Load<T>(string id, params string[] paths)
		{
			return Load(GraphContext.Current.GetGraphType<T>(), id, paths);
		}

		//TODO: augment to automatically load property paths as needed when init'ing a new entity
		static string Load(GraphType type, string id, string[] paths)
		{
			if (string.IsNullOrEmpty(id))
			{
				string path = "\"" + string.Join("\",\"", paths) + "\"";
				return "{ id: $newId(), from: \"" + type.Name + "\", and: [" + path + "] }";
			} 

			var request = new ServiceRequest(type, new string[] { id }, paths);
			return "{ id : \"" + id + "\", from: \"" + type.Name + "\", load: " + FixJsonDates(ToJson(typeof(ServiceResponse), request.Invoke())) + "}";
		}

		public static string GetTypes(params string[] types)
		{
			return GetTypes(types.Select(type => GraphContext.Current.GetGraphType(type)).ToArray());
		}

		public static string GetType<T>()
		{
			return GetTypes(GraphContext.Current.GetGraphType<T>());
		}

		static string GetTypes(params GraphType[] types)
		{
			var json = ToJson(typeof(ServiceResponse), new ServiceRequest(types).Invoke());
			return json.Substring(1, json.Length - 2);
		}<commit_msg>Accomodate calling load with no paths where the id is null or empty.<commit_after>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ExoGraph;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Web.Script.Serialization;
using ExoRule.Validation;
using ExoRule;

namespace ExoWeb
{
	public static class ExoWeb
	{
		#region Fields

		static readonly Regex dateRegex = new Regex("\"\\\\/Date\\((?<ticks>-?[0-9]+)(?:[a-zA-Z]|(?:\\+|-)[0-9]{4})?\\)\\\\/\"", RegexOptions.Compiled);
		static string cacheHash;
		static JavaScriptSerializer serializer;
		static MethodInfo deserialize;
		static long minJsonTicks = new DateTime(0x7b2, 1, 1, 0, 0, 0, DateTimeKind.Utc).Ticks;


		#endregion

		#region Constructors

		static ExoWeb()
		{
			Adapter = new ServiceAdapter();
			InitializeSerialization();
		}

		#endregion

		#region Properties

		/// <summary>
		/// Gets a unique cache key to use when referencing client scripts to ensure that application
		/// changes cause browsers to pull down new type schema information.
		/// </summary>
		public static string CacheHash
		{
			get
			{
				if (cacheHash == null)
				{
					int code = 0;

					foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies())
						code = code == 0 ? a.GetHashCode() : code ^ a.GetHashCode();

					cacheHash = code.ToString();
				}
				return cacheHash;
			}
		}

		public static ServiceAdapter Adapter { get; set; }

		/// <summary>
		/// Indicates whether all error information should be available on 
		/// the client.  This option should be used with caution.
		/// </summary>
		public static bool EnableExceptionInformation { get; set; }

		#endregion

		#region Events

		public static event EventHandler<ServiceRequestEventArgs> BeginRequest;

		public static event EventHandler<ServiceRequestEventArgs> EndRequest;

		public static event EventHandler<ServiceErrorEventArgs> Error;

		public static event EventHandler<SerializeEventArgs> Serialize;

		/// <summary>
		/// Raises the <see cref="BeginRequest"/> event for the specified <see cref="ServiceRequest"/>.
		/// </summary>
		/// <param name="error"></param>
		internal static void OnBeginRequest(ServiceRequest request)
		{
			if (BeginRequest != null)
				BeginRequest(request, new ServiceRequestEventArgs(request));
		}

		/// <summary>
		/// Raises the <see cref="EndRequest"/> event for the specified <see cref="ServiceRequest"/>.
		/// </summary>
		/// <param name="error"></param>
		internal static void OnEndRequest(ServiceRequest request)
		{
			if (EndRequest != null)
				EndRequest(request, new ServiceRequestEventArgs(request));
		}

		/// <summary>
		/// Raises the <see cref="Error"/> event for the specified <see cref="ServiceError"/>.
		/// </summary>
		/// <param name="error"></param>
		internal static void OnError(ServiceError error)
		{
			if (Error != null)
				Error(error, new ServiceErrorEventArgs(error));
		}

		/// <summary>
		/// Raises the <see cref="Serialize"/> event for the specified <see cref="GraphInstance"/>.
		/// </summary>
		/// <param name="error"></param>
		internal static void OnSerialize(GraphInstance instance)
		{
			if (Serialize != null)
				Serialize(instance, new SerializeEventArgs(instance));
		}

		#endregion

		#region Methods

		/// <summary>
		/// Determines whether a given property should be included in the
		/// model on the client.  
		/// </summary>
		/// <param name="property">The graph property that may be included.</param>
		/// <returns>A boolean value indicating whether to include the property.</returns>
		internal static bool IncludeInClientModel(GraphProperty property)
		{
			return !(property is GraphValueProperty) ||
				JsonConverter.GetJsonValueType(((GraphValueProperty)property).PropertyType) != null;
		}

		internal static string FixJsonDates(string json)
		{
			return dateRegex.Replace(json,
				(match) =>
				{
					var date = new DateTime((long.Parse(match.Groups["ticks"].Value) * 0x2710L) + minJsonTicks, DateTimeKind.Utc);
					return date.ToString(@"\""yyyy-MM-dd\THH:mm:ss.fff\Z\""");
				});
		}

		public static void RegisterForSerialization(Assembly assembly)
		{
			serializer.RegisterConverters(JsonConverter.Infer(assembly.GetTypes()).Cast<JavaScriptConverter>());
		}

		public static string Load(string type, string id, params string[] paths)
		{
			return Load(GraphContext.Current.GetGraphType(type), id, paths);
		}

		public static string Load<T>(string id, params string[] paths)
		{
			return Load(GraphContext.Current.GetGraphType<T>(), id, paths);
		}

		//TODO: augment to automatically load property paths as needed when init'ing a new entity
		static string Load(GraphType type, string id, string[] paths)
		{
			if (string.IsNullOrEmpty(id))
			{
				string path = paths.Length > 0 ? "\"" + string.Join("\",\"", paths) + "\"" : "";
				return "{ id: $newId(), from: \"" + type.Name + "\", and: [" + path + "] }";
			}

			var request = new ServiceRequest(type, new string[] { id }, paths);
			return "{ id : \"" + id + "\", from: \"" + type.Name + "\", load: " + FixJsonDates(ToJson(typeof(ServiceResponse), request.Invoke())) + "}";
		}

		public static string GetTypes(params string[] types)
		{
			return GetTypes(types.Select(type => GraphContext.Current.GetGraphType(type)).ToArray());
		}

		public static string GetType<T>()
		{
			return GetTypes(GraphContext.Current.GetGraphType<T>());
		}

		static string GetTypes(params GraphType[] types)
		{
			var json = ToJson(typeof(ServiceResponse), new ServiceRequest(types).Invoke());
			return json.Substring(1, json.Length - 2);
		}
{'fraction_non_alphanumeric': 0.1400537634408602, 'fraction_numerical': 0.004032258064516129, 'mean_word_length': 3.673785594639866, 'pattern_counts': {'":': 0, '<': 91, '<?xml version=': 0, '>': 96, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1017821', 'n_tokens_mistral': 3753, 'n_tokens_neox': 3247, 'n_words': 916}