using System;
using System.Collections.Generic;
using Substrate.Core;
namespace Substrate.Nbt
{
///
/// Indicates how an event processor should respond to returning event handler.
///
public enum TagEventCode
{
///
/// The event processor should process the next event in the chian.
///
NEXT,
///
/// The event processor should ignore the verification failure and stop processing any remaining events.
///
PASS,
///
/// The event processor should fail and stop processing any remaining events.
///
FAIL,
}
///
/// Event arguments for failure events.
///
public class TagEventArgs : EventArgs
{
private string _tagName;
private TagNode _parent;
private TagNode _tag;
private SchemaNode _schema;
///
/// Gets the expected name of the referenced by this event.
///
public string TagName
{
get { return _tagName; }
}
///
/// Gets the parent of the referenced by this event, if it exists.
///
public TagNode Parent
{
get { return _parent; }
}
///
/// Gets the referenced by this event.
///
public TagNode Tag
{
get { return _tag; }
}
///
/// Gets the corresponding to the referenced by this event.
///
public SchemaNode Schema
{
get { return _schema; }
}
///
/// Constructs a new event argument set.
///
/// The expected name of a .
public TagEventArgs (string tagName)
: base()
{
_tagName = tagName;
}
///
/// Constructs a new event argument set.
///
/// The expected name of a .
/// The involved in this event.
public TagEventArgs (string tagName, TagNode tag)
: base()
{
_tag = tag;
_tagName = tagName;
}
///
/// Constructs a new event argument set.
///
/// The corresponding to the involved in this event.
/// The involved in this event.
public TagEventArgs (SchemaNode schema, TagNode tag)
: base()
{
_tag = tag;
_schema = schema;
}
}
///
/// An event handler for intercepting and responding to verification failures of NBT trees.
///
/// Information relating to a verification event.
/// A determining how the event processor should respond.
public delegate TagEventCode VerifierEventHandler (TagEventArgs eventArgs);
///
/// Verifies the integrity of an NBT tree against a schema definition.
///
public class NBTVerifier
{
private TagNode _root;
private SchemaNode _schema;
///
/// An event that gets fired whenever an expected is not found.
///
public static event VerifierEventHandler MissingTag;
///
/// An event that gets fired whenever an expected is of the wrong type and cannot be cast.
///
public static event VerifierEventHandler InvalidTagType;
///
/// An event that gets fired whenever an expected has a value that violates the schema.
///
public static event VerifierEventHandler InvalidTagValue;
private Dictionary _scratch = new Dictionary();
///
/// Constructs a new object for a given NBT tree and schema.
///
/// A representing the root of an NBT tree.
/// A representing the root of a schema definition for the NBT tree.
public NBTVerifier (TagNode root, SchemaNode schema)
{
_root = root;
_schema = schema;
}
///
/// Invokes the verifier.
///
/// Status indicating whether the NBT tree is valid for the given schema.
public virtual bool Verify ()
{
return Verify(null, _root, _schema);
}
private bool Verify (TagNode parent, TagNode tag, SchemaNode schema)
{
if (tag == null) {
return OnMissingTag(new TagEventArgs(schema.Name));
}
SchemaNodeScaler scaler = schema as SchemaNodeScaler;
if (scaler != null) {
return VerifyScaler(tag, scaler);
}
SchemaNodeString str = schema as SchemaNodeString;
if (str != null) {
return VerifyString(tag, str);
}
SchemaNodeArray array = schema as SchemaNodeArray;
if (array != null) {
return VerifyArray(tag, array);
}
SchemaNodeList list = schema as SchemaNodeList;
if (list != null) {
return VerifyList(tag, list);
}
SchemaNodeCompound compound = schema as SchemaNodeCompound;
if (compound != null) {
return VerifyCompound(tag, compound);
}
return OnInvalidTagType(new TagEventArgs(schema.Name, tag));
}
private bool VerifyScaler (TagNode tag, SchemaNodeScaler schema)
{
if (!tag.IsCastableTo(schema.Type)) {
if (!OnInvalidTagType(new TagEventArgs(schema.Name, tag))) {
return false;
}
}
return true;
}
private bool VerifyString (TagNode tag, SchemaNodeString schema)
{
TagNodeString stag = tag as TagNodeString;
if (stag == null) {
if (!OnInvalidTagType(new TagEventArgs(schema, tag))) {
return false;
}
}
if (schema.Length > 0 && stag.Length > schema.Length) {
if (!OnInvalidTagValue(new TagEventArgs(schema, tag))) {
return false;
}
}
if (schema.Value != null && stag.Data != schema.Value) {
if (!OnInvalidTagValue(new TagEventArgs(schema, tag))) {
return false;
}
}
return true;
}
private bool VerifyArray (TagNode tag, SchemaNodeArray schema)
{
TagNodeByteArray atag = tag as TagNodeByteArray;
if (atag == null) {
if (!OnInvalidTagType(new TagEventArgs(schema, tag))) {
return false;
}
}
if (schema.Length > 0 && atag.Length != schema.Length) {
if (!OnInvalidTagValue(new TagEventArgs(schema, tag))) {
return false;
}
}
return true;
}
private bool VerifyList (TagNode tag, SchemaNodeList schema)
{
TagNodeList ltag = tag as TagNodeList;
if (ltag == null) {
if (!OnInvalidTagType(new TagEventArgs(schema, tag))) {
return false;
}
}
if (ltag.Count > 0 && ltag.ValueType != schema.Type) {
if (!OnInvalidTagValue(new TagEventArgs(schema, tag))) {
return false;
}
}
if (schema.Length > 0 && ltag.Count != schema.Length) {
if (!OnInvalidTagValue(new TagEventArgs(schema, tag))) {
return false;
}
}
// Patch up empty lists
//if (schema.Length == 0) {
// tag = new NBT_List(schema.Type);
//}
bool pass = true;
// If a subschema is set, test all items in list against it
if (schema.SubSchema != null) {
foreach (TagNode v in ltag) {
pass = Verify(tag, v, schema.SubSchema) && pass;
}
}
return pass;
}
private bool VerifyCompound (TagNode tag, SchemaNodeCompound schema)
{
TagNodeCompound ctag = tag as TagNodeCompound;
if (ctag == null) {
if (!OnInvalidTagType(new TagEventArgs(schema, tag))) {
return false;
}
}
bool pass = true;
foreach (SchemaNode node in schema) {
TagNode value;
ctag.TryGetValue(node.Name, out value);
if (value == null) {
if ((node.Options & SchemaOptions.CREATE_ON_MISSING) == SchemaOptions.CREATE_ON_MISSING) {
_scratch[node.Name] = node.BuildDefaultTree();
continue;
}
else if ((node.Options & SchemaOptions.OPTIONAL) == SchemaOptions.OPTIONAL) {
continue;
}
}
pass = Verify(tag, value, node) && pass;
}
foreach (KeyValuePair item in _scratch) {
ctag[item.Key] = item.Value;
}
_scratch.Clear();
return pass;
}
#region Event Handlers
///
/// Processes registered events for whenever an expected is not found.
///
/// Arguments for this event.
/// Status indicating whether this event can be ignored.
protected virtual bool OnMissingTag (TagEventArgs e)
{
if (MissingTag != null) {
foreach (VerifierEventHandler func in MissingTag.GetInvocationList()) {
TagEventCode code = func(e);
switch (code) {
case TagEventCode.FAIL:
return false;
case TagEventCode.PASS:
return true;
}
}
}
return false;
}
///
/// Processes registered events for whenever an expected is of the wrong type and cannot be cast.
///
/// Arguments for this event.
/// Status indicating whether this event can be ignored.
protected virtual bool OnInvalidTagType (TagEventArgs e)
{
if (InvalidTagType != null) {
foreach (VerifierEventHandler func in InvalidTagType.GetInvocationList()) {
TagEventCode code = func(e);
switch (code) {
case TagEventCode.FAIL:
return false;
case TagEventCode.PASS:
return true;
}
}
}
return false;
}
///
/// Processes registered events for whenever an expected has a value that violates the schema.
///
/// Arguments for this event.
/// Status indicating whether this event can be ignored.
protected virtual bool OnInvalidTagValue (TagEventArgs e)
{
if (InvalidTagValue != null) {
foreach (VerifierEventHandler func in InvalidTagValue.GetInvocationList()) {
TagEventCode code = func(e);
switch (code) {
case TagEventCode.FAIL:
return false;
case TagEventCode.PASS:
return true;
}
}
}
return false;
}
#endregion
}
}