using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
namespace UniJSON
{
///
/// http://json-schema.org/latest/json-schema-validation.html#rfc.section.6.4
///
public class JsonArrayValidator : IJsonSchemaValidator
{
///
/// http://json-schema.org/latest/json-schema-validation.html#rfc.section.6.4.1
///
public JsonSchema Items
{
get; set;
}
// additionalItems
///
/// http://json-schema.org/latest/json-schema-validation.html#rfc.section.6.4.3
///
public int? MaxItems
{
get; set;
}
///
/// http://json-schema.org/latest/json-schema-validation.html#rfc.section.6.4.4
///
public int? MinItems
{
get; set;
}
// uniqueItems
// contains
public override int GetHashCode()
{
return 5;
}
public override bool Equals(object obj)
{
var rhs = obj as JsonArrayValidator;
if (rhs == null) return false;
if (Items != rhs.Items) return false;
if (MaxItems != rhs.MaxItems) return false;
if (MinItems != rhs.MinItems) return false;
return true;
}
public void Merge(IJsonSchemaValidator rhs)
{
throw new NotImplementedException();
}
public bool FromJsonSchema(IFileSystemAccessor fs, string key, ListTreeNode value)
{
switch (key)
{
case "items":
if (value.IsArray())
{
throw new NotImplementedException();
}
else
{
var sub = new JsonSchema();
sub.Parse(fs, value, "items");
Items = sub;
}
return true;
case "additionalItems":
return true;
case "maxItems":
MaxItems = value.GetInt32();
return true;
case "minItems":
MinItems = value.GetInt32();
return true;
case "uniqueItems":
return true;
case "contains":
return true;
}
return false;
}
static class GenericCounter
{
delegate int Counter(T value);
static Counter s_counter;
public static int Count(T value)
{
if (s_counter == null)
{
var t = typeof(T);
if (t.IsArray)
{
var pi = t.GetProperty("Length");
var compiled = (Func)((T array) =>
{
return (int)pi.GetValue(array, null);
});
s_counter = new Counter(compiled);
}
else if (t.GetIsGenericList())
{
var pi = t.GetProperty("Count");
var compiled = (Func)((T list) =>
{
return (int)pi.GetValue(list, null);
});
s_counter = new Counter(compiled);
}
else
{
throw new NotImplementedException();
}
}
return s_counter(value);
}
}
public JsonSchemaValidationException Validate(JsonSchemaValidationContext context, T o)
{
if (o == null)
{
return new JsonSchemaValidationException(context, "null");
}
var count = GenericCounter.Count(o);
// Empty array is valid
/*if (count == 0)
{
return new JsonSchemaValidationException(context, "empty");
}*/
if (MaxItems.HasValue && count > MaxItems.Value)
{
return new JsonSchemaValidationException(context, "maxOtems");
}
if (MinItems.HasValue && count < MinItems.Value)
{
return new JsonSchemaValidationException(context, "minItems");
}
if (Items == null)
{
return null; // There are no json schema for items, success
}
var v = Items.Validator;
var t = o.GetType();
IEnumerable iter = null;
if (t.IsArray)
{
iter = o as Array;
}
else if (t.GetIsGenericList())
{
iter = o as IList;
}
else
{
return new JsonSchemaValidationException(context, "non iterable object");
}
foreach(var e in iter)
{
var ex = v.Validate(context, e);
if (ex != null)
{
return ex;
}
};
return null;
}
static void ArraySerializer(IJsonSchemaValidator v, IFormatter f, JsonSchemaValidationContext c, U[] array)
{
f.BeginList(array.Length);
{
//int i = 0;
foreach (var x in array)
{
//using (c.Push(i++))
{
v.Serialize(f, c, x);
}
}
}
f.EndList();
}
static void ListSerializer(IJsonSchemaValidator v, IFormatter f, JsonSchemaValidationContext c, List list)
{
f.BeginList(list.Count);
{
//int i = 0;
foreach (var x in list)
{
//using (c.Push(i++))
{
v.Serialize(f, c, x);
}
}
}
f.EndList();
}
static class GenericSerializer
{
delegate void Serializer(IJsonSchemaValidator v, IFormatter f, JsonSchemaValidationContext c, T o);
static Serializer s_serializer;
public static void Serialize(IJsonSchemaValidator v, IFormatter f, JsonSchemaValidationContext c, T o)
{
if (s_serializer == null)
{
var t = typeof(T);
MethodInfo g = null;
if (t.IsArray)
{
var mi = typeof(JsonArrayValidator).GetMethod("ArraySerializer",
BindingFlags.Static | BindingFlags.NonPublic);
g = mi.MakeGenericMethod(t.GetElementType());
}
else if (t.GetIsGenericList())
{
// ToDo: IList
var mi = typeof(JsonArrayValidator).GetMethod("ListSerializer",
BindingFlags.Static | BindingFlags.NonPublic);
g = mi.MakeGenericMethod(t.GetGenericArguments());
}
else
{
throw new NotImplementedException();
}
var compiled = (Action<
IJsonSchemaValidator,
IFormatter,
JsonSchemaValidationContext,
T>)
GenericInvokeCallFactory.StaticAction<
IJsonSchemaValidator,
IFormatter,
JsonSchemaValidationContext,
T>(g);
s_serializer = new Serializer(compiled);
}
s_serializer(v, f, c, o);
}
}
public void Serialize(IFormatter f, JsonSchemaValidationContext c, T o)
{
GenericSerializer.Serialize(Items.Validator, f, c, o);
}
public void ToJsonScheama(IFormatter f)
{
f.Key("type"); f.Value("array");
if (Items != null)
{
f.Key("items");
Items.ToJson(f);
}
}
public void Deserialize(ListTreeNode src, ref U dst)
where T : IListTreeItem, IValue
{
src.Deserialize(ref dst);
}
}
}