diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -15,3 +15,6 @@ Implab.Diagnostics.Interactive/bin/ Implab.Diagnostics.Interactive/obj/ MonoPlay/bin/ MonoPlay/obj/ +Implab.Test/Implab.Format.Test/bin/ +Implab.Test/Implab.Format.Test/obj/ +*.suo diff --git a/Implab.Test/Implab.Format.Test/Implab.Format.Test.csproj b/Implab.Test/Implab.Format.Test/Implab.Format.Test.csproj --- a/Implab.Test/Implab.Format.Test/Implab.Format.Test.csproj +++ b/Implab.Test/Implab.Format.Test/Implab.Format.Test.csproj @@ -10,6 +10,7 @@ Implab.Format.Test Implab.Format.Test v4.5 + 0.2 true @@ -32,7 +33,7 @@ - ..\..\packages\NUnit.3.0.1\lib\net45\nunit.framework.dll + ..\..\packages\NUnit.2.6.4\lib\nunit.framework.dll @@ -40,6 +41,12 @@ + + {F550F1F8-8746-4AD0-9614-855F4C4B7F05} + Implab + + + \ No newline at end of file diff --git a/Implab.Test/Implab.Format.Test/JsonTests.cs b/Implab.Test/Implab.Format.Test/JsonTests.cs --- a/Implab.Test/Implab.Format.Test/JsonTests.cs +++ b/Implab.Test/Implab.Format.Test/JsonTests.cs @@ -1,11 +1,87 @@ using NUnit.Framework; using System; +using Implab.Formats.JSON; +using Implab.Automaton; namespace Implab.Format.Test { - [TestFixture()] + [TestFixture] public class JsonTests { - [Test()] - public void TestCase() { + [Test] + public void TestScannerValidTokens() { + using (var scanner = new JSONScanner(@"9123, -123, 0, 0.1, -0.2, -0.1e3, 1.3E-3, ""some \t\n\u0020 text"", literal []{}:")) { + + Tuple[] expexted = { + new Tuple(JsonTokenType.Number, 9123d), + new Tuple(JsonTokenType.ValueSeparator, ", "), + new Tuple(JsonTokenType.Number, -123d), + new Tuple(JsonTokenType.ValueSeparator, ", "), + new Tuple(JsonTokenType.Number, 0d), + new Tuple(JsonTokenType.ValueSeparator, ", "), + new Tuple(JsonTokenType.Number, 0.1d), + new Tuple(JsonTokenType.ValueSeparator, ", "), + new Tuple(JsonTokenType.Number, -0.2d), + new Tuple(JsonTokenType.ValueSeparator, ", "), + new Tuple(JsonTokenType.Number, -0.1e3d), + new Tuple(JsonTokenType.ValueSeparator, ", "), + new Tuple(JsonTokenType.Number, 1.3E-3d), + new Tuple(JsonTokenType.ValueSeparator, ", "), + new Tuple(JsonTokenType.String, "some \t\n text"), + new Tuple(JsonTokenType.ValueSeparator, ", "), + new Tuple(JsonTokenType.Literal, "literal"), + new Tuple(JsonTokenType.BeginArray, " ["), + new Tuple(JsonTokenType.EndArray, "]"), + new Tuple(JsonTokenType.BeginObject, "{"), + new Tuple(JsonTokenType.EndObject, "}"), + new Tuple(JsonTokenType.NameSeparator, ":") + }; + + object value; + JsonTokenType tokenType; + for (var i = 0; i < expexted.Length; i++) { + + Assert.IsTrue(scanner.ReadToken(out value, out tokenType)); + Assert.AreEqual(expexted[i].Item1, tokenType); + Assert.AreEqual(expexted[i].Item2, value); + } + + Assert.IsFalse(scanner.ReadToken(out value, out tokenType)); + } + } + + [Test] + public void TestScannerBadTokens() { + var bad = new [] { + " 1", + " literal", + " \"", + "\"unclosed string", + "1.bad", + "001", // should be read as three numbers + "--10", + "+10", + "1.0.0", + "1e1.0", + "l1teral0", + ".123", + "-.123" + }; + + foreach (var json in bad) + using (var scanner = new JSONScanner(json)) { + try { + object value; + JsonTokenType token; + scanner.ReadToken(out value, out token); + if (!Object.Equals(value,json)) { + Console.WriteLine("'{0}' is read as {1}", json, value is String ? String.Format("'{0}'", value) : value ); + continue; + } + Assert.Fail("Token '{0}' shouldn't pass", json); + } catch (ParserException e) { + Console.WriteLine(e.Message); + } + } + } } } diff --git a/Implab.Test/Implab.Format.Test/packages.config b/Implab.Test/Implab.Format.Test/packages.config --- a/Implab.Test/Implab.Format.Test/packages.config +++ b/Implab.Test/Implab.Format.Test/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/Implab.Test/Implab.Test.csproj b/Implab.Test/Implab.Test.csproj --- a/Implab.Test/Implab.Test.csproj +++ b/Implab.Test/Implab.Test.csproj @@ -62,8 +62,10 @@ + + diff --git a/Implab.Test/Implab.Test.mono.csproj b/Implab.Test/Implab.Test.mono.csproj --- a/Implab.Test/Implab.Test.mono.csproj +++ b/Implab.Test/Implab.Test.mono.csproj @@ -58,6 +58,7 @@ + diff --git a/Implab.Test/RunnableComponentTests.cs b/Implab.Test/RunnableComponentTests.cs new file mode 100644 --- /dev/null +++ b/Implab.Test/RunnableComponentTests.cs @@ -0,0 +1,195 @@ +using System; +using System.Reflection; +using System.Threading; +using Implab.Parallels; +using Implab.Components; + +#if MONO + +using NUnit.Framework; +using TestClassAttribute = NUnit.Framework.TestFixtureAttribute; +using TestMethodAttribute = NUnit.Framework.TestAttribute; +using AssertFailedException = NUnit.Framework.AssertionException; +#else + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +#endif + +namespace Implab.Test { + [TestClass] + public class RunnableComponentTests { + + static void ShouldThrow(Action action) { + try { + action(); + Assert.Fail(); + } catch (AssertFailedException) { + throw; + } catch { + } + } + + class Runnable : RunnableComponent { + public Runnable(bool initialized) : base(initialized) { + } + + public Action MockInit { + get; + set; + } + + public Func MockStart { + get; + set; + } + + public Func MockStop { + get; + set; + } + + protected override IPromise OnStart() { + return MockStart != null ? MockStart() : base.OnStart(); + } + + protected override IPromise OnStop() { + return MockStop != null ? MockStop() : base.OnStart(); + } + + protected override void OnInitialize() { + if (MockInit != null) + MockInit(); + } + } + + [TestMethod] + public void NormalFlowTest() { + var comp = new Runnable(false); + + Assert.AreEqual(ExecutionState.Created, comp.State); + + comp.Init(); + + Assert.AreEqual(ExecutionState.Ready, comp.State); + + comp.Start().Join(1000); + + Assert.AreEqual(ExecutionState.Running, comp.State); + + comp.Stop().Join(1000); + + Assert.AreEqual(ExecutionState.Disposed, comp.State); + + } + + [TestMethod] + public void InitFailTest() { + var comp = new Runnable(false) { + MockInit = () => { + throw new Exception("BAD"); + } + }; + + ShouldThrow(() => comp.Start()); + ShouldThrow(() => comp.Stop()); + Assert.AreEqual(ExecutionState.Created, comp.State); + + ShouldThrow(comp.Init); + + Assert.AreEqual(ExecutionState.Failed, comp.State); + + ShouldThrow(() => comp.Start()); + ShouldThrow(() => comp.Stop()); + Assert.AreEqual(ExecutionState.Failed, comp.State); + + comp.Dispose(); + Assert.AreEqual(ExecutionState.Disposed, comp.State); + } + + [TestMethod] + public void DisposedTest() { + + var comp = new Runnable(false); + comp.Dispose(); + + ShouldThrow(() => comp.Start()); + ShouldThrow(() => comp.Stop()); + ShouldThrow(comp.Init); + + Assert.AreEqual(ExecutionState.Disposed, comp.State); + } + + [TestMethod] + public void StartCancelTest() { + var comp = new Runnable(true) { + MockStart = () => PromiseHelper.Sleep(100000, 0) + }; + + var p = comp.Start(); + Assert.AreEqual(ExecutionState.Starting, comp.State); + p.Cancel(); + ShouldThrow(() => p.Join(1000)); + Assert.AreEqual(ExecutionState.Failed, comp.State); + + Assert.IsInstanceOfType(comp.LastError, typeof(OperationCanceledException)); + + comp.Dispose(); + } + + [TestMethod] + public void StartStopTest() { + var stop = new Signal(); + var comp = new Runnable(true) { + MockStart = () => PromiseHelper.Sleep(100000, 0), + MockStop = () => AsyncPool.RunThread(stop.Wait) + }; + + var p1 = comp.Start(); + var p2 = comp.Stop(); + // should enter stopping state + + ShouldThrow(p1.Join); + Assert.IsTrue(p1.IsCancelled); + Assert.AreEqual(ExecutionState.Stopping, comp.State); + + stop.Set(); + p2.Join(1000); + Assert.AreEqual(ExecutionState.Disposed, comp.State); + } + + [TestMethod] + public void StartStopFailTest() { + var comp = new Runnable(true) { + MockStart = () => PromiseHelper.Sleep(100000, 0).Then(null,null,x => { throw new Exception("I'm dead"); }) + }; + + comp.Start(); + var p = comp.Stop(); + // if Start fails to cancel, should fail to stop + ShouldThrow(() => p.Join(1000)); + Assert.AreEqual(ExecutionState.Failed, comp.State); + Assert.IsNotNull(comp.LastError); + Assert.AreEqual("I'm dead", comp.LastError.Message); + } + + [TestMethod] + public void StopCancelTest() { + var comp = new Runnable(true) { + MockStop = () => PromiseHelper.Sleep(100000, 0) + }; + + comp.Start(); + var p = comp.Stop(); + Assert.AreEqual(ExecutionState.Stopping, comp.State); + p.Cancel(); + ShouldThrow(() => p.Join(1000)); + Assert.AreEqual(ExecutionState.Failed, comp.State); + Assert.IsInstanceOfType(comp.LastError, typeof(OperationCanceledException)); + + comp.Dispose(); + } + + } +} + diff --git a/Implab/AbstractEvent.cs b/Implab/AbstractEvent.cs --- a/Implab/AbstractEvent.cs +++ b/Implab/AbstractEvent.cs @@ -78,13 +78,9 @@ namespace Implab { /// Данное обещание уже выполнено protected void SetError(Exception error) { if (BeginTransit()) { - if (error is OperationCanceledException) { - m_error = error.InnerException; - CompleteTransit(CANCELLED_STATE); - } else { - m_error = error is PromiseTransientException ? error.InnerException : error; - CompleteTransit(REJECTED_STATE); - } + m_error = error; + CompleteTransit(REJECTED_STATE); + Signal(); } else { WaitTransition(); @@ -139,11 +135,11 @@ namespace Implab { case SUCCEEDED_STATE: return; case CANCELLED_STATE: - throw new OperationCanceledException(); + throw new OperationCanceledException("The operation has been cancelled", m_error); case REJECTED_STATE: throw new TargetInvocationException(m_error); default: - throw new ApplicationException(String.Format("Invalid promise state {0}", m_state)); + throw new ApplicationException(String.Format("The promise state {0} is invalid", m_state)); } } #endregion diff --git a/Implab/AbstractPromise.cs b/Implab/AbstractPromise.cs --- a/Implab/AbstractPromise.cs +++ b/Implab/AbstractPromise.cs @@ -134,8 +134,8 @@ namespace Implab { } protected void SetResult() { - BeginSetResult(); - EndSetResult(); + if(BeginSetResult()) + EndSetResult(); } } } diff --git a/Implab/AbstractTask.cs b/Implab/AbstractTask.cs new file mode 100644 --- /dev/null +++ b/Implab/AbstractTask.cs @@ -0,0 +1,45 @@ +using System; +using System.Threading; + +namespace Implab { + /// + /// Базовый класс для реализации задачь. Задача представляет собой некторое + /// действие, которое можно иницировать и обработать результат его выполнения + /// в виде обещания, для этого оно реализует интерфейс . + /// + /// + /// Данный класс определяет стандартное поведение при обработки результатов, в частности + /// обработку и + /// + public abstract class AbstractTask : AbstractPromise { + int m_cancelationLock; + + /// + /// Получает эксклюзивное право отмены задания, используется для отмены задания до начала его выполнения. + /// + /// true, if cancelation was locked, false otherwise. + protected bool LockCancelation() { + return 0 == Interlocked.CompareExchange(ref m_cancelationLock, 1, 0); + } + + + + protected void SetErrorInternal(Exception error) { + // unwrap + while (error is PromiseTransientException && error.InnerException != null) + error = error.InnerException; + + if (error is OperationCanceledException) + SetCancelled(error); + else + SetError(error); + } + + protected void SetCancelledInternal(Exception reason) { + SetCancelled( + reason == null ? new OperationCanceledException() : reason is OperationCanceledException ? reason : new OperationCanceledException(null, reason) + ); + } + } +} + diff --git a/Implab/AbstractTaskT.cs b/Implab/AbstractTaskT.cs new file mode 100644 --- /dev/null +++ b/Implab/AbstractTaskT.cs @@ -0,0 +1,36 @@ +using System; +using System.Threading; + +namespace Implab { + public abstract class AbstractTask : AbstractPromise { + int m_cancelationLock; + + /// + /// Получает эксклюзивное право отмены задания, используется для отмены задания до начала его выполнения. + /// + /// true, if cancelation was locked, false otherwise. + protected bool LockCancelation() { + return 0 == Interlocked.CompareExchange(ref m_cancelationLock, 1, 0); + } + + + + protected void SetErrorInternal(Exception error) { + // unwrap + while (error is PromiseTransientException && error.InnerException != null) + error = error.InnerException; + + if (error is OperationCanceledException) + SetCancelled(error); + else + SetError(error); + } + + protected void SetCancelledInternal(Exception reason) { + SetCancelled( + reason == null ? new OperationCanceledException() : reason is OperationCanceledException ? reason : new OperationCanceledException(null, reason) + ); + } + } +} + diff --git a/Implab/ActionChainTask.cs b/Implab/ActionChainTask.cs --- a/Implab/ActionChainTask.cs +++ b/Implab/ActionChainTask.cs @@ -4,6 +4,15 @@ namespace Implab { public class ActionChainTask : ActionChainTaskBase, IDeferred { readonly Func m_task; + /// + /// Initializes a new instance of the class. + /// + /// The operation which will be performed when the is called. + /// The error handler which will invoke when the is called or when the task fails with an error. + /// The cancellation handler. + /// If set to true will automatically accept + /// all cancel requests before the task is started with , + /// after that all requests are directed to the task. public ActionChainTask(Func task, Func error, Func cancel, bool autoCancellable) : base(error,cancel, autoCancellable) { m_task = task; } @@ -12,8 +21,10 @@ namespace Implab { if (m_task != null && LockCancelation()) { try { var p = m_task(); - p.On(SetResult, HandleErrorInternal, SetCancelled); + p.On(SetResult, HandleErrorInternal, HandleCancelInternal); CancellationRequested(p.Cancel); + } catch (OperationCanceledException reason){ + HandleCancelInternal(reason); } catch(Exception err) { HandleErrorInternal(err); } diff --git a/Implab/ActionChainTaskBase.cs b/Implab/ActionChainTaskBase.cs --- a/Implab/ActionChainTaskBase.cs +++ b/Implab/ActionChainTaskBase.cs @@ -2,12 +2,10 @@ using System.Threading; namespace Implab { - public class ActionChainTaskBase : AbstractPromise { + public class ActionChainTaskBase : AbstractTask { readonly Func m_error; readonly Func m_cancel; - int m_cancelationLock; - protected ActionChainTaskBase(Func error, Func cancel, bool autoCancellable) { m_error = error; m_cancel = cancel; @@ -20,19 +18,28 @@ namespace Implab { HandleErrorInternal(error); } - + public override void CancelOperation(Exception reason) { + if (LockCancelation()) + // отмена вызвана до начала выполнения задачи + HandleCancelInternal(reason); + } - public override void CancelOperation(Exception reason) { - if (LockCancelation()) { - if (m_cancel != null) { - try { - m_cancel(reason).On(SetResult, SetError, SetCancelled); - } catch (Exception err) { - HandleErrorInternal(err); - } - } else { - SetCancelled(reason); + protected void HandleCancelInternal(Exception reason) { + if (m_cancel != null) { + try { + // вызываем обработчик отмены + var p = m_cancel(reason); + p.On(SetResult, HandleErrorInternal, SetCancelledInternal); + // сообщаем асинхронной операции, что клиент уже не хочет получать результат + // т.е. если он инициировал отмену, задача отменилась, вызвался обрабочик отмены + // отбработчику сообщили, что результат уже не нужен и уже сам обработчик решает + // отдавать ли результат или подтвердить отмену (или вернуть ошибку). + CancellationRequested(p.Cancel); + } catch (Exception err) { + HandleErrorInternal(err); } + } else { + HandleErrorInternal(reason ?? new OperationCanceledException()); } } @@ -40,19 +47,16 @@ namespace Implab { if (m_error != null) { try { var p = m_error(error); - p.On(SetResult,SetError,SetCancelled); + p.On(SetResult, SetErrorInternal, SetCancelledInternal); CancellationRequested(p.Cancel); } catch (Exception err) { - SetError(err); + SetErrorInternal(error); } } else { - SetError(error); + SetErrorInternal(error); } } - protected bool LockCancelation() { - return 0 == Interlocked.CompareExchange(ref m_cancelationLock, 1, 0); - } } } diff --git a/Implab/ActionChainTaskT.cs b/Implab/ActionChainTaskT.cs --- a/Implab/ActionChainTaskT.cs +++ b/Implab/ActionChainTaskT.cs @@ -12,8 +12,10 @@ namespace Implab { if (m_task != null && LockCancelation()) { try { var p = m_task(value); - p.On(SetResult, HandleErrorInternal, SetCancelled); + p.On(SetResult, HandleErrorInternal, HandleCancelInternal); CancellationRequested(p.Cancel); + } catch (OperationCanceledException reason) { + HandleCancelInternal(reason); } catch(Exception err) { HandleErrorInternal(err); } diff --git a/Implab/ActionTask.cs b/Implab/ActionTask.cs --- a/Implab/ActionTask.cs +++ b/Implab/ActionTask.cs @@ -12,6 +12,8 @@ namespace Implab { try { m_task(); SetResult(); + } catch(OperationCanceledException reason) { + HandleCancelInternal(reason); } catch(Exception err) { HandleErrorInternal(err); } diff --git a/Implab/ActionTaskBase.cs b/Implab/ActionTaskBase.cs --- a/Implab/ActionTaskBase.cs +++ b/Implab/ActionTaskBase.cs @@ -1,13 +1,10 @@ using System; -using System.Threading; namespace Implab { - public class ActionTaskBase : AbstractPromise { + public class ActionTaskBase : AbstractTask { readonly Action m_cancel; readonly Action m_error; - int m_cancelationLock; - protected ActionTaskBase( Action error, Action cancel, bool autoCancellable) { m_error = error; m_cancel = cancel; @@ -21,37 +18,36 @@ namespace Implab { HandleErrorInternal(error); } + public override void CancelOperation(Exception reason) { + if (LockCancelation()) + HandleCancelInternal(reason); + } + protected void HandleErrorInternal(Exception error) { if (m_error != null) { try { m_error(error); SetResult(); } catch(Exception err) { - SetError(err); + SetErrorInternal(err); } } else { - SetError(error); + SetErrorInternal(error); } } - public override void CancelOperation(Exception reason) { - if (LockCancelation()) { - if (m_cancel != null) { - try { - m_cancel(reason); - SetResult(); - } catch (Exception err) { - HandleErrorInternal(err); - } - } else { - SetCancelled(reason); + protected void HandleCancelInternal(Exception error) { + if (m_cancel != null) { + try { + m_cancel(error); + SetResult(); + } catch(Exception err) { + HandleErrorInternal(err); } + } else { + HandleErrorInternal(error ?? new OperationCanceledException()); } } - - protected bool LockCancelation() { - return 0 == Interlocked.CompareExchange(ref m_cancelationLock, 1, 0); - } } } diff --git a/Implab/ActionTaskT.cs b/Implab/ActionTaskT.cs --- a/Implab/ActionTaskT.cs +++ b/Implab/ActionTaskT.cs @@ -12,6 +12,8 @@ namespace Implab { try { m_task(value); SetResult(); + } catch(OperationCanceledException reason) { + HandleCancelInternal(reason); } catch(Exception err) { HandleErrorInternal(err); } diff --git a/Implab/Automaton/AutomatonConst.cs b/Implab/Automaton/AutomatonConst.cs new file mode 100644 --- /dev/null +++ b/Implab/Automaton/AutomatonConst.cs @@ -0,0 +1,9 @@ + +namespace Implab.Automaton { + public static class AutomatonConst { + public const int UNREACHABLE_STATE = -1; + + public const int UNCLASSIFIED_INPUT = 0; + } +} + diff --git a/Implab/Automaton/AutomatonTransition.cs b/Implab/Automaton/AutomatonTransition.cs new file mode 100644 --- /dev/null +++ b/Implab/Automaton/AutomatonTransition.cs @@ -0,0 +1,33 @@ +using System; + +namespace Implab.Automaton { + public struct AutomatonTransition : IEquatable { + public readonly int s1; + public readonly int s2; + public readonly int edge; + + public AutomatonTransition(int s1, int s2, int edge) { + this.s1 = s1; + this.s2 = s2; + this.edge = edge; + } + + + #region IEquatable implementation + public bool Equals(AutomatonTransition other) { + return other.s1 == s1 && other.s2 == s2 && other.edge == edge ; + } + #endregion + + public override bool Equals(object obj) { + if (obj is AutomatonTransition) + return Equals((AutomatonTransition)obj); + return base.Equals(obj); + } + + public override int GetHashCode() { + return s1 + s2 + edge; + } + } +} + diff --git a/Implab/Automaton/DFATable.cs b/Implab/Automaton/DFATable.cs new file mode 100644 --- /dev/null +++ b/Implab/Automaton/DFATable.cs @@ -0,0 +1,348 @@ +using Implab; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Diagnostics; +using System.IO; +using System.CodeDom.Compiler; +using System.CodeDom; + +namespace Implab.Automaton { + public class DFATable : IDFATableBuilder { + int m_stateCount; + int m_symbolCount; + int m_initialState; + + readonly HashSet m_finalStates = new HashSet(); + readonly HashSet m_transitions = new HashSet(); + + + #region IDFADefinition implementation + + public bool IsFinalState(int s) { + Safe.ArgumentInRange(s, 0, m_stateCount, "s"); + + return m_finalStates.Contains(s); + } + + public IEnumerable FinalStates { + get { + return m_finalStates; + } + } + + public int StateCount { + get { return m_stateCount; } + } + + public int AlphabetSize { + get { return m_symbolCount; } + } + + public int InitialState { + get { return m_initialState; } + } + + #endregion + + public void SetInitialState(int s) { + Safe.ArgumentAssert(s >= 0, "s"); + m_stateCount = Math.Max(m_stateCount, s + 1); + m_initialState = s; + } + + public void MarkFinalState(int state) { + m_stateCount = Math.Max(m_stateCount, state + 1); + m_finalStates.Add(state); + } + + public void Add(AutomatonTransition item) { + Safe.ArgumentAssert(item.s1 >= 0, "item"); + Safe.ArgumentAssert(item.s2 >= 0, "item"); + Safe.ArgumentAssert(item.edge >= 0, "item"); + + m_stateCount = Math.Max(m_stateCount, Math.Max(item.s1, item.s2) + 1); + m_symbolCount = Math.Max(m_symbolCount, item.edge + 1); + + m_transitions.Add(item); + } + + public void Clear() { + m_stateCount = 0; + m_symbolCount = 0; + m_finalStates.Clear(); + m_transitions.Clear(); + } + + public bool Contains(AutomatonTransition item) { + return m_transitions.Contains(item); + } + + public void CopyTo(AutomatonTransition[] array, int arrayIndex) { + m_transitions.CopyTo(array, arrayIndex); + } + + public bool Remove(AutomatonTransition item) { + return m_transitions.Remove(item); + } + + public int Count { + get { + return m_transitions.Count; + } + } + + public bool IsReadOnly { + get { + return false; + } + } + + public IEnumerator GetEnumerator() { + return m_transitions.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return GetEnumerator(); + } + + public void AddSymbol(int symbol) { + Safe.ArgumentAssert(symbol >= 0, "symbol"); + m_symbolCount = Math.Max(symbol + 1, m_symbolCount); + } + + public int[,] CreateTransitionTable() { + var table = new int[StateCount,AlphabetSize]; + + for (int i = 0; i < StateCount; i++) + for (int j = 0; j < AlphabetSize; j++) + table[i, j] = AutomatonConst.UNREACHABLE_STATE; + + foreach (var t in this) + table[t.s1,t.edge] = t.s2; + + return table; + } + + public bool[] CreateFinalStateTable() { + var table = new bool[StateCount]; + + foreach (var s in FinalStates) + table[s] = true; + + return table; + } + + /// Формирует множества конечных состояний перед началом работы алгоритма минимизации. + /// + /// В процессе построения минимального автомата требуется разделить множество состояний, + /// на два подмножества - конечные состояния и все остальные, после чего эти подмножества + /// будут резделены на более мелкие. Иногда требуется гарантировать различия конечных сосотяний, + /// для этого необходимо переопределить даннцю фукнцию, для получения множеств конечных состояний. + /// + /// The final states. + protected virtual IEnumerable> SplitFinalStates(IEnumerable states) { + return new [] { new HashSet(states) }; + } + + protected void Optimize( + IDFATableBuilder optimalDFA, + IDictionary alphabetMap, + IDictionary stateMap + ) { + Safe.ArgumentNotNull(optimalDFA, "dfa"); + Safe.ArgumentNotNull(alphabetMap, "alphabetMap"); + Safe.ArgumentNotNull(stateMap, "stateMap"); + + + var setComparer = new CustomEqualityComparer>( + (x, y) => x.SetEquals(y), + s => s.Sum(x => x.GetHashCode()) + ); + + var optimalStates = new HashSet>(setComparer); + var queue = new HashSet>(setComparer); + + optimalStates.Add(new HashSet(FinalStates)); + + var state = new HashSet( + Enumerable + .Range(0, m_stateCount) + .Where(i => !m_finalStates.Contains(i)) + ); + + optimalStates.Add(state); + queue.Add(state); + + var rmap = m_transitions + .GroupBy(t => t.s2) + .ToDictionary( + g => g.Key, // s2 + g => g.ToLookup(t => t.edge, t => t.s1)//.ToDictionary(p => p.Key) + ); + + while (queue.Count > 0) { + var stateA = queue.First(); + queue.Remove(stateA); + + for (int c = 0; c < m_symbolCount; c++) { + var stateX = new HashSet(); + foreach(var a in stateA.Where(rmap.ContainsKey)) + stateX.UnionWith(rmap[a][c]); // all states from wich the symbol 'c' leads to the state 'a' + + var tmp = optimalStates.ToArray(); + foreach (var stateY in tmp) { + var stateR1 = new HashSet(stateY); + var stateR2 = new HashSet(stateY); + + stateR1.IntersectWith(stateX); + stateR2.ExceptWith(stateX); + + if (stateR1.Count > 0 && stateR2.Count > 0) { + + + optimalStates.Remove(stateY); + optimalStates.Add(stateR1); + optimalStates.Add(stateR2); + + if (queue.Contains(stateY)) { + queue.Remove(stateY); + queue.Add(stateR1); + queue.Add(stateR2); + } else { + queue.Add(stateR1.Count <= stateR2.Count ? stateR1 : stateR2); + } + } + } + } + } + + // дополнительно разбиваем конечные состояния + foreach (var final in optimalStates.Where(s => s.Overlaps(m_finalStates)).ToArray()) { + optimalStates.Remove(final); + foreach (var split in SplitFinalStates(final)) + optimalStates.Add(split); + } + + + // карта получения оптимального состояния по соотвествующему ему простому состоянию + var nextState = 0; + foreach (var item in optimalStates) { + var id = nextState++; + foreach (var s in item) + stateMap[s] = id; + } + + // получаем минимальный алфавит + // входные символы не различимы, если Move(s,a1) == Move(s,a2), для любого s + // для этого используем алгоритм кластеризации, сначала + // считаем, что все символы не различимы + + var minClasses = new HashSet>(setComparer); + var alphaQueue = new Queue>(); + alphaQueue.Enqueue(new HashSet(Enumerable.Range(0,AlphabetSize))); + + // для всех состояний, будем проверять каждый класс на различимость, + // т.е. символы различимы, если они приводят к разным состояниям + for (int s = 0 ; s < optimalStates.Count; s++) { + var newQueue = new Queue>(); + + foreach (var A in alphaQueue) { + // классы из одного символа делить бесполезно, переводим их сразу в + // результирующий алфавит + if (A.Count == 1) { + minClasses.Add(A); + continue; + } + + // различаем классы символов, которые переводят в различные оптимальные состояния + // optimalState -> alphaClass + var classes = new Dictionary>(); + + foreach (var term in A) { + // ищем все переходы класса по символу term + var s2 = m_transitions.Where(t => stateMap[t.s1] == s && t.edge == term).Select(t => stateMap[t.s2]).DefaultIfEmpty(-1).First(); + + HashSet a2; + if (!classes.TryGetValue(s2, out a2)) { + a2 = new HashSet(); + newQueue.Enqueue(a2); + classes[s2] = a2; + } + a2.Add(term); + } + } + + if (newQueue.Count == 0) + break; + alphaQueue = newQueue; + } + + // после окончания работы алгоритма в очереди останутся минимальные различимые классы + // входных символов + foreach (var A in alphaQueue) + minClasses.Add(A); + + // построение отображения алфавитов входных символов. + // поскольку символ DFAConst.UNCLASSIFIED_INPUT может иметь + // специальное значение, тогда сохраним минимальный класс, + // содержащий этот символ на томже месте. + + var nextCls = 0; + foreach (var item in minClasses) { + if (nextCls == AutomatonConst.UNCLASSIFIED_INPUT) + nextCls++; + + // сохраняем DFAConst.UNCLASSIFIED_INPUT + var cls = item.Contains(AutomatonConst.UNCLASSIFIED_INPUT) ? AutomatonConst.UNCLASSIFIED_INPUT : nextCls++; + optimalDFA.AddSymbol(cls); + + foreach (var a in item) + alphabetMap[a] = cls; + } + + // построение автомата + optimalDFA.SetInitialState(stateMap[m_initialState]); + + foreach (var sf in m_finalStates.Select(s => stateMap[s]).Distinct()) + optimalDFA.MarkFinalState(sf); + + foreach (var t in m_transitions.Select(t => new AutomatonTransition(stateMap[t.s1],stateMap[t.s2],alphabetMap[t.edge])).Distinct()) + optimalDFA.Add(t); + } + + protected string PrintDFA(IAlphabet inputAlphabet, IAlphabet stateAlphabet) { + Safe.ArgumentNotNull(inputAlphabet, "inputAlphabet"); + Safe.ArgumentNotNull(stateAlphabet, "stateAlphabet"); + + var data = new List(); + + data.Add("digraph dfa {"); + + foreach (var final in m_finalStates) + data.Add(String.Format("{0} [shape=box];",String.Join("", stateAlphabet.GetSymbols(final)))); + + foreach (var t in m_transitions) + data.Add(String.Format( + "{0} -> {2} [label={1}];", + String.Join("", stateAlphabet.GetSymbols(t.s1)), + ToLiteral(ToLiteral(String.Join("", t.edge == AutomatonConst.UNCLASSIFIED_INPUT ? new [] { "@" } : inputAlphabet.GetSymbols(t.edge).Select(x => x.ToString())))), + String.Join("", stateAlphabet.GetSymbols(t.s2)) + )); + data.Add("}"); + return String.Join("\n", data); + } + + static string ToLiteral(string input) + { + using (var writer = new StringWriter()) + { + using (var provider = CodeDomProvider.CreateProvider("CSharp")) + { + provider.GenerateCodeFromExpression(new CodePrimitiveExpression(input), writer, null); + return writer.ToString(); + } + } + } + } +} diff --git a/Implab/Automaton/EnumAlphabet.cs b/Implab/Automaton/EnumAlphabet.cs new file mode 100644 --- /dev/null +++ b/Implab/Automaton/EnumAlphabet.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Diagnostics.CodeAnalysis; + +namespace Implab.Automaton { + /// + /// Алфавит символами которого являются элементы перечислений. + /// + /// Тип перечислений + public class EnumAlphabet : IndexedAlphabetBase where T : struct, IConvertible { + [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")] + static readonly Lazy _symbols = new Lazy(GetSymbols); + + [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")] + static readonly Lazy> _fullAlphabet = new Lazy>(CreateEnumAlphabet); + + static EnumAlphabet CreateEnumAlphabet() { + var symbols = _symbols.Value; + + if ( + symbols[symbols.Length - 1].ToInt32(CultureInfo.InvariantCulture) >= symbols.Length + || symbols[0].ToInt32(CultureInfo.InvariantCulture) != 0 + ) + throw new InvalidOperationException("The specified enumeration must be zero-based and continuously numbered"); + + return new EnumAlphabet(symbols.Select(x => x.ToInt32(CultureInfo.InvariantCulture)).ToArray()); + } + + static T[] GetSymbols() { + if (!typeof(T).IsEnum) + throw new InvalidOperationException("Invalid generic parameter, enumeration is required"); + + if (Enum.GetUnderlyingType(typeof(T)) != typeof(Int32)) + throw new InvalidOperationException("Only enums based on Int32 are supported"); + + return ((T[])Enum.GetValues(typeof(T))) + .OrderBy(x => x.ToInt32(CultureInfo.InvariantCulture)) + .ToArray(); + } + + public static EnumAlphabet FullAlphabet { + get { + return _fullAlphabet.Value; + } + } + + + public EnumAlphabet() + : base(_symbols.Value.Length) { + } + + public EnumAlphabet(int[] map) + : base(map) { + Debug.Assert(map.Length == _symbols.Value.Length); + } + + + public override int GetSymbolIndex(T symbol) { + return symbol.ToInt32(CultureInfo.InvariantCulture); + } + + } +} diff --git a/Implab/Automaton/IAlphabet.cs b/Implab/Automaton/IAlphabet.cs new file mode 100644 --- /dev/null +++ b/Implab/Automaton/IAlphabet.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Implab.Automaton { + /// + /// Алфавит. Множество символов, которые разбиты на классы, при этом классы имеют непрерывную нумерацию, + /// что позволяет использовать их в качестве индексов массивов. + /// + /// + /// Алфавит является сюрьективным отображением множества символов в множество индексов, это позволяет сократить размер таблицы переходов автомата + /// для входных символов, которые для него не различимы. + /// + /// Тип символов. + public interface IAlphabet { + /// + /// Количество классов символов в алфавите. + /// + int Count { get; } + + /// + /// Преобразует входной символ в индекс символа из алфавита. + /// + /// Исходный символ + /// Индекс в алфавите + int Translate(TSymbol symobl); + + bool Contains(TSymbol symbol); + + IEnumerable GetSymbols(int cls); + } +} diff --git a/Implab/Automaton/IAlphabetBuilder.cs b/Implab/Automaton/IAlphabetBuilder.cs new file mode 100644 --- /dev/null +++ b/Implab/Automaton/IAlphabetBuilder.cs @@ -0,0 +1,26 @@ + +using System.Collections.Generic; + +namespace Implab.Automaton { + public interface IAlphabetBuilder : IAlphabet { + /// + /// Добавляет новый символ в алфавит, если символ уже был добавлен, то + /// возвращается ранее сопоставленный с символом класс. + /// + /// Символ для добавления. + /// Индекс класса, который попоставлен с символом. + int DefineSymbol(TSymbol symbol); + + int DefineSymbol(TSymbol symbol, int cls); + /// + /// Доабвляем класс символов. Множеству указанных исходных символов + /// будет сопоставлен символ в алфавите. + /// + /// Множестов исходных символов + /// Идентификатор символа алфавита. + int DefineClass(IEnumerable symbols); + + int DefineClass(IEnumerable symbols, int cls); + } +} + diff --git a/Implab/Automaton/IDFATable.cs b/Implab/Automaton/IDFATable.cs new file mode 100644 --- /dev/null +++ b/Implab/Automaton/IDFATable.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; + + +namespace Implab.Automaton { + /// + /// Полностью описывает DFA автомат, его поведение, состояние и входные символы. + /// + /// + /// class MyAutomaton { + /// int m_current; + /// readonly DFAStateDescriptor[] m_automaton; + /// readonly IAlphabet m_commands; + /// + /// public MyAutomaton(IDFADefinition<MyCommands,MyStates,string> definition) { + /// m_current = definition.StateAlphabet.Translate(MyStates.Initial); + /// m_automaton = definition.GetTransitionTable(); + /// m_commands = definition.InputAlphabet; + /// } + /// + /// // defined a method which will move the automaton to the next state + /// public void Move(MyCommands cmd) { + /// // use transition map to determine the next state + /// var next = m_automaton[m_current].transitions[m_commands.Translate(cmd)]; + /// + /// // validate that we aren't in the unreachable state + /// if (next == DFAConst.UNREACHABLE_STATE) + /// throw new InvalidOperationException("The specified command is invalid"); + /// + /// // if everything is ok + /// m_current = next; + /// } + /// } + /// + public interface IDFATable : IEnumerable { + int StateCount { + get; + } + + int AlphabetSize { + get; + } + + int InitialState { + get; + } + + bool IsFinalState(int s); + + IEnumerable FinalStates { + get; + } + } +} diff --git a/Implab/Automaton/IDFATableBuilder.cs b/Implab/Automaton/IDFATableBuilder.cs new file mode 100644 --- /dev/null +++ b/Implab/Automaton/IDFATableBuilder.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; + +namespace Implab.Automaton { + public interface IDFATableBuilder : IDFATable, ICollection { + /// + /// Marks the state as final. + /// + /// State. + void MarkFinalState(int state); + + void SetInitialState(int s); + + /// + /// Increases if needed the input alphabet size to hold the specified symbol. + /// + /// + /// + /// AlphabetSize = Math.Max(AlphabetSize, symbol + 1) + /// + /// + /// Symbol. + void AddSymbol(int symbol); + } +} + diff --git a/Implab/Automaton/IndexedAlphabetBase.cs b/Implab/Automaton/IndexedAlphabetBase.cs new file mode 100644 --- /dev/null +++ b/Implab/Automaton/IndexedAlphabetBase.cs @@ -0,0 +1,50 @@ +using Implab; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace Implab.Automaton { + /// + /// Indexed alphabet is the finite set of symbols where each symbol has a zero-based unique index. + /// + /// + /// Indexed alphabets are usefull in bulting efficient translations from source alphabet + /// to the input alphabet of the automaton. It's assumed that the index to the symbol match + /// is well known and documented. + /// + public abstract class IndexedAlphabetBase : MapAlphabet { + + protected IndexedAlphabetBase() :base(true, null) { + } + + public abstract int GetSymbolIndex(T symbol); + + /// + /// Gets the translation map from the index of the symbol to it's class this is usefull for the optimized input symbols transtaion. + /// + /// + /// The map is continous and start from the symbol with zero code. The last symbol + /// in the map is the last classified symbol in the alphabet, i.e. the map can be + /// shorter then the whole alphabet. + /// + /// The translation map. + public int[] GetTranslationMap() { + var map = new Dictionary(); + + int max = 0; + foreach (var p in Mappings) { + var index = GetSymbolIndex(p.Key); + max = Math.Max(max, index); + map[index] = p.Value; + } + + var result = new int[max + 1]; + + for (int i = 0; i < result.Length; i++) + map.TryGetValue(i, out result[i]); + + return result; + } + } +} diff --git a/Implab/Automaton/MapAlphabet.cs b/Implab/Automaton/MapAlphabet.cs new file mode 100644 --- /dev/null +++ b/Implab/Automaton/MapAlphabet.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Implab.Automaton { + public class MapAlphabet : IAlphabetBuilder { + readonly Dictionary m_map; + int m_nextCls; + readonly bool m_supportUnclassified; + + public MapAlphabet(bool supportUnclassified, IEqualityComparer comparer) { + m_map = comparer != null ? new Dictionary(comparer) : new Dictionary(); + m_supportUnclassified = supportUnclassified; + m_nextCls = supportUnclassified ? 1 : 0; + } + + #region IAlphabetBuilder implementation + + public int DefineSymbol(T symbol) { + int cls; + return m_map.TryGetValue(symbol, out cls) ? cls : DefineSymbol(symbol, m_nextCls); + } + + public int DefineSymbol(T symbol, int cls) { + Safe.ArgumentAssert(cls >= 0, "cls"); + + m_nextCls = Math.Max(cls + 1, m_nextCls); + m_map.Add(symbol, cls); + return cls; + } + + public int DefineClass(IEnumerable symbols) { + return DefineClass(symbols, m_nextCls); + } + + public int DefineClass(IEnumerable symbols, int cls) { + Safe.ArgumentAssert(cls >= 0, "cls"); + Safe.ArgumentNotNull(symbols, "symbols"); + + m_nextCls = Math.Max(cls + 1, m_nextCls); + + foreach (var symbol in symbols) + m_map[symbol] = cls; + return cls; + } + + #endregion + + #region IAlphabet implementation + + public int Translate(T symbol) { + int cls; + if (m_map.TryGetValue(symbol, out cls)) + return cls; + if (!m_supportUnclassified) + throw new ArgumentOutOfRangeException("symbol", "The specified symbol isn't in the alphabet"); + return AutomatonConst.UNCLASSIFIED_INPUT; + } + + public int Count { + get { + return m_nextCls; + } + } + + public bool Contains(T symbol) { + return m_supportUnclassified || m_map.ContainsKey(symbol); + } + + + public IEnumerable GetSymbols(int cls) { + Safe.ArgumentAssert(!m_supportUnclassified || cls > 0, "cls"); + return m_map.Where(p => p.Value == cls).Select(p => p.Key); + } + #endregion + + public IEnumerable> Mappings { + get { + return m_map; + } + } + } +} + diff --git a/Implab/Automaton/ParserException.cs b/Implab/Automaton/ParserException.cs new file mode 100644 --- /dev/null +++ b/Implab/Automaton/ParserException.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Implab.Automaton { + [Serializable] + public class ParserException : Exception { + public ParserException() { } + public ParserException(string message) : base(message) { } + public ParserException(string message, Exception inner) : base(message, inner) { } + protected ParserException( + System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } + } +} diff --git a/Implab/Automaton/RegularExpressions/AltToken.cs b/Implab/Automaton/RegularExpressions/AltToken.cs new file mode 100644 --- /dev/null +++ b/Implab/Automaton/RegularExpressions/AltToken.cs @@ -0,0 +1,17 @@ +using System; + +namespace Implab.Automaton.RegularExpressions { + public class AltToken: BinaryToken { + public AltToken(Token left, Token right) + : base(left, right) { + } + + public override void Accept(IVisitor visitor) { + Safe.ArgumentNotNull(visitor, "visitor"); + visitor.Visit(this); + } + public override string ToString() { + return String.Format(Right is BinaryToken ? "{0}|({1})" : "{0}|{1}", Left, Right); + } + } +} diff --git a/Implab/Automaton/RegularExpressions/BinaryToken.cs b/Implab/Automaton/RegularExpressions/BinaryToken.cs new file mode 100644 --- /dev/null +++ b/Implab/Automaton/RegularExpressions/BinaryToken.cs @@ -0,0 +1,21 @@ +using Implab; + +namespace Implab.Automaton.RegularExpressions { + public abstract class BinaryToken: Token { + readonly Token m_left; + readonly Token m_right; + + public Token Left { + get { return m_left; } + } + + public Token Right { + get { return m_right; } + } + + protected BinaryToken(Token left, Token right) { + Safe.ArgumentNotNull(m_left = left, "left"); + Safe.ArgumentNotNull(m_right = right, "right"); + } + } +} diff --git a/Implab/Automaton/RegularExpressions/CatToken.cs b/Implab/Automaton/RegularExpressions/CatToken.cs new file mode 100644 --- /dev/null +++ b/Implab/Automaton/RegularExpressions/CatToken.cs @@ -0,0 +1,22 @@ +using System; + +namespace Implab.Automaton.RegularExpressions { + public class CatToken : BinaryToken { + public CatToken(Token left, Token right) + : base(left, right) { + } + + public override void Accept(IVisitor visitor) { + Safe.ArgumentNotNull(visitor, "visitor"); + visitor.Visit(this); + } + + public override string ToString() { + return String.Format("{0}{1}", FormatToken(Left), FormatToken(Right)); + } + + static string FormatToken(Token token) { + return String.Format(token is AltToken ? "({0})" : "{0}", token); + } + } +} diff --git a/Implab/Automaton/RegularExpressions/EmptyToken.cs b/Implab/Automaton/RegularExpressions/EmptyToken.cs new file mode 100644 --- /dev/null +++ b/Implab/Automaton/RegularExpressions/EmptyToken.cs @@ -0,0 +1,13 @@ +using Implab; + +namespace Implab.Automaton.RegularExpressions { + public class EmptyToken: Token { + public override void Accept(IVisitor visitor) { + Safe.ArgumentNotNull(visitor, "visitor"); + visitor.Visit(this); + } + public override string ToString() { + return "$"; + } + } +} diff --git a/Implab/Automaton/RegularExpressions/EndToken.cs b/Implab/Automaton/RegularExpressions/EndToken.cs new file mode 100644 --- /dev/null +++ b/Implab/Automaton/RegularExpressions/EndToken.cs @@ -0,0 +1,18 @@ +using Implab; + +namespace Implab.Automaton.RegularExpressions { + /// + /// Конечный символ расширенного регулярного выражения, при построении ДКА + /// используется для определения конечных состояний. + /// + public class EndToken: Token { + + public override void Accept(IVisitor visitor) { + Safe.ArgumentNotNull(visitor, "visitor"); + visitor.Visit(this); + } + public override string ToString() { + return "#"; + } + } +} diff --git a/Implab/Automaton/RegularExpressions/EndTokenT.cs b/Implab/Automaton/RegularExpressions/EndTokenT.cs new file mode 100644 --- /dev/null +++ b/Implab/Automaton/RegularExpressions/EndTokenT.cs @@ -0,0 +1,23 @@ +namespace Implab.Automaton.RegularExpressions { + /// + /// Конечный символ расширенного регулярного выражения, при построении ДКА + /// используется для определения конечных состояний. + /// + public class EndToken: EndToken { + + readonly TTag m_tag; + + public EndToken(TTag tag) { + m_tag = tag; + } + + public EndToken() + : this(default(TTag)) { + } + + public TTag Tag { + get { return m_tag; } + } + + } +} diff --git a/Implab/Automaton/RegularExpressions/ITaggedDFABuilder.cs b/Implab/Automaton/RegularExpressions/ITaggedDFABuilder.cs new file mode 100644 --- /dev/null +++ b/Implab/Automaton/RegularExpressions/ITaggedDFABuilder.cs @@ -0,0 +1,7 @@ + +namespace Implab.Automaton.RegularExpressions { + public interface ITaggedDFABuilder : IDFATableBuilder { + void SetStateTag(int s, TTag[] tags); + } +} + diff --git a/Implab/Automaton/RegularExpressions/IVisitor.cs b/Implab/Automaton/RegularExpressions/IVisitor.cs new file mode 100644 --- /dev/null +++ b/Implab/Automaton/RegularExpressions/IVisitor.cs @@ -0,0 +1,13 @@ +namespace Implab.Automaton.RegularExpressions { + /// + /// Интерфейс обходчика синтаксического дерева регулярного выражения + /// + public interface IVisitor { + void Visit(AltToken token); + void Visit(StarToken token); + void Visit(CatToken token); + void Visit(EmptyToken token); + void Visit(EndToken token); + void Visit(SymbolToken token); + } +} diff --git a/Implab/Automaton/RegularExpressions/RegularDFA.cs b/Implab/Automaton/RegularExpressions/RegularDFA.cs new file mode 100644 --- /dev/null +++ b/Implab/Automaton/RegularExpressions/RegularDFA.cs @@ -0,0 +1,91 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Implab.Automaton.RegularExpressions { + public class RegularDFA : DFATable, ITaggedDFABuilder { + + readonly Dictionary m_tags = new Dictionary(); + readonly IAlphabet m_alphabet; + + public RegularDFA(IAlphabet alphabet) { + Safe.ArgumentNotNull(alphabet, "aplhabet"); + + m_alphabet = alphabet; + } + + + public IAlphabet InputAlphabet { + get { + return m_alphabet; + } + } + + public void MarkFinalState(int s, TTag[] tags) { + MarkFinalState(s); + SetStateTag(s, tags); + } + + public void SetStateTag(int s, TTag[] tags) { + Safe.ArgumentNotNull(tags, "tags"); + m_tags[s] = tags; + } + + public TTag[] GetStateTag(int s) { + TTag[] tags; + return m_tags.TryGetValue(s, out tags) ? tags : new TTag[0]; + } + + public TTag[][] CreateTagTable() { + var table = new TTag[StateCount][]; + + foreach (var pair in m_tags) + table[pair.Key] = pair.Value; + + return table; + } + + /// + /// Optimize the specified alphabet. + /// + /// Пустой алфавит, который будет зполнен в процессе оптимизации. + public RegularDFA Optimize(IAlphabetBuilder alphabet) { + Safe.ArgumentNotNull(alphabet, "alphabet"); + + var dfa = new RegularDFA(alphabet); + + var alphaMap = new Dictionary(); + var stateMap = new Dictionary(); + + Optimize(dfa, alphaMap, stateMap); + + // mark tags in the new DFA + foreach (var g in m_tags.Where(x => x.Key < StateCount).GroupBy(x => stateMap[x.Key], x => x.Value )) + dfa.SetStateTag(g.Key, g.SelectMany(x => x).ToArray()); + + // make the alphabet for the new DFA + // skip all unclassified symbols + foreach (var pair in alphaMap.Where(x => x.Value != 0)) + alphabet.DefineClass(m_alphabet.GetSymbols(pair.Key), pair.Value); + return dfa; + } + + protected override IEnumerable> SplitFinalStates(IEnumerable states) { + var arrayComparer = new CustomEqualityComparer( + (x,y) => x.Length == y.Length && x.All(it => y.Contains(it)), + x => x.Sum(it => x.GetHashCode()) + ); + return states.GroupBy(x => m_tags[x] ?? new TTag[0], arrayComparer).Select(g => new HashSet(g)); + } + + public override string ToString() { + var states = new MapAlphabet(false, null); + + for (int i = 0; i < StateCount; i++) + states.DefineSymbol(string.Format("s{0}", i), i); + + return string.Format("//[RegularDFA {1} x {2}]\n{0}", PrintDFA(InputAlphabet, states),StateCount, AlphabetSize); + } + + } +} + diff --git a/Implab/Automaton/RegularExpressions/RegularExpressionVisitor.cs b/Implab/Automaton/RegularExpressions/RegularExpressionVisitor.cs new file mode 100644 --- /dev/null +++ b/Implab/Automaton/RegularExpressions/RegularExpressionVisitor.cs @@ -0,0 +1,212 @@ +using Implab; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace Implab.Automaton.RegularExpressions { + /// + /// Используется для построения ДКА по регулярному выражению, сначала обходит + /// регулярное выражение и вычисляет followpos, затем используется метод + /// для построения автомата. + /// + public class RegularExpressionVisitor : IVisitor { + int m_idx; + Token m_root; + HashSet m_firstpos; + HashSet m_lastpos; + + readonly Dictionary> m_followpos = new Dictionary>(); + readonly Dictionary m_indexes = new Dictionary(); + readonly HashSet m_ends = new HashSet(); + + readonly IDFATableBuilder m_builder; + readonly IAlphabetBuilder> m_states = new MapAlphabet>( + false, + new CustomEqualityComparer>( + (x, y) => x.SetEquals(y), + x => x.Sum(n => n.GetHashCode()) + ) + ); + + public RegularExpressionVisitor(IDFATableBuilder builder) { + Safe.ArgumentNotNull(builder, "builder"); + + m_builder = builder; + } + + HashSet Followpos(int pos) { + HashSet set; + return m_followpos.TryGetValue(pos, out set) ? set : m_followpos[pos] = new HashSet(); + } + + bool Nullable(object n) { + if (n is EmptyToken || n is StarToken) + return true; + var altToken = n as AltToken; + if (altToken != null) + return Nullable(altToken.Left) || Nullable(altToken.Right); + var catToken = n as CatToken; + if (catToken != null) + return Nullable(catToken.Left) && Nullable(catToken.Right); + return false; + } + + protected int Index { + get { return m_idx; } + } + + public void Visit(AltToken token) { + if (m_root == null) + m_root = token; + var firtspos = new HashSet(); + var lastpos = new HashSet(); + + token.Left.Accept(this); + firtspos.UnionWith(m_firstpos); + lastpos.UnionWith(m_lastpos); + + token.Right.Accept(this); + firtspos.UnionWith(m_firstpos); + lastpos.UnionWith(m_lastpos); + + m_firstpos = firtspos; + m_lastpos = lastpos; + } + + public void Visit(StarToken token) { + if (m_root == null) + m_root = token; + token.Token.Accept(this); + + foreach (var i in m_lastpos) + Followpos(i).UnionWith(m_firstpos); + } + + public void Visit(CatToken token) { + if (m_root == null) + m_root = token; + + var firtspos = new HashSet(); + var lastpos = new HashSet(); + token.Left.Accept(this); + firtspos.UnionWith(m_firstpos); + var leftLastpos = m_lastpos; + + token.Right.Accept(this); + lastpos.UnionWith(m_lastpos); + var rightFirstpos = m_firstpos; + + if (Nullable(token.Left)) + firtspos.UnionWith(rightFirstpos); + + if (Nullable(token.Right)) + lastpos.UnionWith(leftLastpos); + + m_firstpos = firtspos; + m_lastpos = lastpos; + + foreach (var i in leftLastpos) + Followpos(i).UnionWith(rightFirstpos); + + } + + public void Visit(EmptyToken token) { + if (m_root == null) + m_root = token; + } + + public void Visit(SymbolToken token) { + if (m_root == null) + m_root = token; + m_idx++; + m_indexes[m_idx] = token.Value; + m_firstpos = new HashSet(new[] { m_idx }); + m_lastpos = new HashSet(new[] { m_idx }); + } + + public virtual void Visit(EndToken token) { + if (m_root == null) + m_root = token; + m_idx++; + m_indexes[m_idx] = AutomatonConst.UNCLASSIFIED_INPUT; + m_firstpos = new HashSet(new[] { m_idx }); + m_lastpos = new HashSet(new[] { m_idx }); + Followpos(m_idx); + m_ends.Add(m_idx); + } + + public void BuildDFA() { + AddState(m_firstpos); + SetInitialState(m_firstpos); + + if(IsFinal(m_firstpos)) + MarkFinalState(m_firstpos); + + var inputMax = m_indexes.Values.Max(); + var queue = new Queue>(); + + queue.Enqueue(m_firstpos); + + while (queue.Count > 0) { + var s1 = queue.Dequeue(); + + for (int a = 0; a <= inputMax; a++) { + var s2 = new HashSet(); + foreach (var p in s1) { + if (m_indexes[p] == a) { + s2.UnionWith(Followpos(p)); + } + } + if (s2.Count > 0) { + if (!HasState(s2)) { + AddState(s2); + if (IsFinal(s2)) + MarkFinalState(s2); + + queue.Enqueue(s2); + } + + DefineTransition(s1, s2, a); + } + + } + } + } + + protected bool HasState(HashSet state) { + return m_states.Contains(state); + } + + protected void AddState(HashSet state) { + Debug.Assert(!HasState(state)); + + m_states.DefineSymbol(state); + } + + protected int Translate(HashSet state) { + Debug.Assert(HasState(state)); + + return m_states.Translate(state); + } + + protected virtual void SetInitialState(HashSet state) { + m_builder.SetInitialState(Translate(state)); + } + + protected virtual void MarkFinalState(HashSet state) { + m_builder.MarkFinalState(Translate(state)); + } + + protected virtual void DefineTransition(HashSet s1, HashSet s2, int ch) { + + m_builder.Add(new AutomatonTransition(Translate(s1), Translate(s2), ch)); + } + + bool IsFinal(IEnumerable state) { + Debug.Assert(state != null); + return state.Any(m_ends.Contains); + } + + } +} diff --git a/Implab/Automaton/RegularExpressions/RegularExpressionVisitorT.cs b/Implab/Automaton/RegularExpressions/RegularExpressionVisitorT.cs new file mode 100644 --- /dev/null +++ b/Implab/Automaton/RegularExpressions/RegularExpressionVisitorT.cs @@ -0,0 +1,37 @@ +using Implab; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace Implab.Automaton.RegularExpressions { + /// + /// + public class RegularExpressionVisitor : RegularExpressionVisitor { + readonly Dictionary m_tags = new Dictionary(); + + readonly ITaggedDFABuilder m_builder; + + public RegularExpressionVisitor(ITaggedDFABuilder builder) : base(builder) { + m_builder = builder; + } + + public override void Visit(EndToken token) { + base.Visit(token); + var tagged = token as EndToken; + if (tagged != null) + m_tags.Add(Index, tagged.Tag); + } + + protected override void MarkFinalState(HashSet state) { + base.MarkFinalState(state); + m_builder.SetStateTag(Translate(state), GetStateTags(state)); + } + + TTag[] GetStateTags(IEnumerable state) { + Debug.Assert(state != null); + return state.Where(m_tags.ContainsKey).Select(pos => m_tags[pos]).ToArray(); + } + + } +} diff --git a/Implab/Automaton/RegularExpressions/StarToken.cs b/Implab/Automaton/RegularExpressions/StarToken.cs new file mode 100644 --- /dev/null +++ b/Implab/Automaton/RegularExpressions/StarToken.cs @@ -0,0 +1,31 @@ +using Implab; +using System; + + +namespace Implab.Automaton.RegularExpressions { + /// + /// Замыкание выражения с 0 и более повторов. + /// + public class StarToken: Token { + + Token m_token; + + public Token Token { + get { return m_token; } + } + + public StarToken(Token token) { + Safe.ArgumentNotNull(token, "token"); + m_token = token; + } + + public override void Accept(IVisitor visitor) { + Safe.ArgumentNotNull(visitor, "visitor"); + visitor.Visit(this); + } + + public override string ToString() { + return String.Format("({0})*", Token); + } + } +} diff --git a/Implab/Automaton/RegularExpressions/SymbolToken.cs b/Implab/Automaton/RegularExpressions/SymbolToken.cs new file mode 100644 --- /dev/null +++ b/Implab/Automaton/RegularExpressions/SymbolToken.cs @@ -0,0 +1,27 @@ +using Implab; + +namespace Implab.Automaton.RegularExpressions { + /// + /// Выражение, соответсвующее одному символу. + /// + public class SymbolToken: Token { + int m_value; + + public int Value { + get { return m_value; } + } + + public SymbolToken(int value) { + m_value = value; + } + public override void Accept(IVisitor visitor) { + Safe.ArgumentNotNull(visitor, "visitor"); + + visitor.Visit(this); + } + + public override string ToString() { + return Value.ToString(); + } + } +} diff --git a/Implab/Automaton/RegularExpressions/Token.cs b/Implab/Automaton/RegularExpressions/Token.cs new file mode 100644 --- /dev/null +++ b/Implab/Automaton/RegularExpressions/Token.cs @@ -0,0 +1,63 @@ +using Implab; +using System; +using System.Linq; + +namespace Implab.Automaton.RegularExpressions { + public abstract class Token { + public abstract void Accept(IVisitor visitor); + + public Token End() { + return Cat(new EndToken()); + } + + public Token Tag(TTag tag) { + return Cat(new EndToken(tag)); + } + + public Token Cat(Token right) { + return new CatToken(this, right); + } + + public Token Or(Token right) { + return new AltToken(this, right); + } + + public Token Optional() { + return Or(new EmptyToken()); + } + + public Token EClosure() { + return new StarToken(this); + } + + public Token Closure() { + return Cat(new StarToken(this)); + } + + public Token Repeat(int count) { + Token token = null; + + for (int i = 0; i < count; i++) + token = token != null ? token.Cat(this) : this; + return token ?? new EmptyToken(); + } + + public Token Repeat(int min, int max) { + if (min > max || min < 1) + throw new ArgumentOutOfRangeException(); + var token = Repeat(min); + + for (int i = min; i < max; i++) + token = token.Cat( Optional() ); + return token; + } + + public static Token New(params int[] set) { + Safe.ArgumentNotNull(set, "set"); + Token token = null; + foreach(var c in set.Distinct()) + token = token == null ? new SymbolToken(c) : token.Or(new SymbolToken(c)); + return token; + } + } +} diff --git a/Implab/Components/ExecutionState.cs b/Implab/Components/ExecutionState.cs --- a/Implab/Components/ExecutionState.cs +++ b/Implab/Components/ExecutionState.cs @@ -1,14 +1,24 @@ namespace Implab.Components { public enum ExecutionState { - Reserved = 0, - Uninitialized, + Undefined = 0, + + Created, + + Initializing, + Ready, + Starting, + Running, + Stopping, - Stopped, + + Failed, + Disposed, - Failed + + Last = Disposed } } \ No newline at end of file diff --git a/Implab/Components/IInitializable.cs b/Implab/Components/IInitializable.cs --- a/Implab/Components/IInitializable.cs +++ b/Implab/Components/IInitializable.cs @@ -11,7 +11,7 @@ namespace Implab.Components { /// Completes initialization. /// /// - /// Normally virtual shouldn't be called from the constructor, due to the incomplete object state, but + /// Normally virtual methods shouldn't be called from the constructor, due to the incomplete object state, but /// they can be called from this method. This method is also usefull when we constructing a complex grpah /// of components where cyclic references may take place. /// diff --git a/Implab/Components/LazyAndWeak.cs b/Implab/Components/LazyAndWeak.cs new file mode 100644 --- /dev/null +++ b/Implab/Components/LazyAndWeak.cs @@ -0,0 +1,64 @@ +using System; +using System.Threading; + +namespace Implab.Components { + /// + /// Creates an instace on-demand and allows it to be garbage collected. + /// + /// + /// Usefull when dealing with memory-intensive objects which are frequently used. + /// This class is similar to except it is a singleton. + /// + public class LazyAndWeak where T : class { + + readonly Func m_factory; + readonly object m_lock; + WeakReference m_reference; + + + public LazyAndWeak(Func factory, bool useLock) { + Safe.ArgumentNotNull(factory, "factory"); + m_factory = factory; + m_lock = useLock ? new object() : null; + } + + public LazyAndWeak(Func factory) : this(factory, false) { + } + + public T Value { + get { + while (true) { + var weak = m_reference; + T value; + if (weak != null) { + value = weak.Target as T; + if (value != null) + return value; + } + + if (m_lock == null) { + value = m_factory(); + + if (Interlocked.CompareExchange(ref m_reference, new WeakReference(value), weak) == weak) + return value; + } else { + lock (m_lock) { + // double check + weak = m_reference; + if (weak != null) { + value = weak.Target as T; + if (value != null) + return value; + } + // we are safe to write + value = m_factory(); + m_reference = new WeakReference(value); + return value; + } + } + } + } + } + } +} + diff --git a/Implab/Components/RunnableComponent.cs b/Implab/Components/RunnableComponent.cs --- a/Implab/Components/RunnableComponent.cs +++ b/Implab/Components/RunnableComponent.cs @@ -1,24 +1,164 @@ using System; -using Implab.Parsing; namespace Implab.Components { - public class RunnableComponent : Disposable, IRunnable, IInitializable { - + public abstract class RunnableComponent : IDisposable, IRunnable, IInitializable { + enum Commands { + Ok = 0, + Fail, + Init, + Start, + Stop, + Dispose, + Last = Dispose + } + + class StateMachine { + static readonly ExecutionState[,] _transitions; + + static StateMachine() { + _transitions = new ExecutionState[(int)ExecutionState.Last + 1, (int)Commands.Last + 1]; + + Edge(ExecutionState.Created, ExecutionState.Initializing, Commands.Init); + Edge(ExecutionState.Created, ExecutionState.Disposed, Commands.Dispose); + + Edge(ExecutionState.Initializing, ExecutionState.Ready, Commands.Ok); + Edge(ExecutionState.Initializing, ExecutionState.Failed, Commands.Fail); + + Edge(ExecutionState.Ready, ExecutionState.Starting, Commands.Start); + Edge(ExecutionState.Ready, ExecutionState.Disposed, Commands.Dispose); + + Edge(ExecutionState.Starting, ExecutionState.Running, Commands.Ok); + Edge(ExecutionState.Starting, ExecutionState.Failed, Commands.Fail); + Edge(ExecutionState.Starting, ExecutionState.Stopping, Commands.Stop); + Edge(ExecutionState.Starting, ExecutionState.Disposed, Commands.Dispose); + Edge(ExecutionState.Running, ExecutionState.Failed, Commands.Fail); + Edge(ExecutionState.Running, ExecutionState.Stopping, Commands.Stop); + Edge(ExecutionState.Running, ExecutionState.Disposed, Commands.Dispose); + Edge(ExecutionState.Stopping, ExecutionState.Failed, Commands.Fail); + Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Ok); + + Edge(ExecutionState.Failed, ExecutionState.Disposed, Commands.Dispose); + } + + static void Edge(ExecutionState s1, ExecutionState s2, Commands cmd) { + _transitions[(int)s1, (int)cmd] = s2; + } - + public ExecutionState State { + get; + private set; + } + + public StateMachine(ExecutionState initial) { + State = initial; + } + + public bool Move(Commands cmd) { + var next = _transitions[(int)State, (int)cmd]; + if (next == ExecutionState.Undefined) + return false; + State = next; + return true; + } + } + IPromise m_pending; Exception m_lastError; + readonly StateMachine m_stateMachine; + protected RunnableComponent(bool initialized) { + m_stateMachine = new StateMachine(initialized ? ExecutionState.Ready : ExecutionState.Created); + } + + protected virtual int DisposeTimeout { + get { + return 10000; + } + } + + void ThrowInvalidCommand(Commands cmd) { + if (m_stateMachine.State == ExecutionState.Disposed) + throw new ObjectDisposedException(ToString()); + + throw new InvalidOperationException(String.Format("Commnd {0} is not allowed in the state {1}", cmd, m_stateMachine.State)); + } + + void Move(Commands cmd) { + if (!m_stateMachine.Move(cmd)) + ThrowInvalidCommand(cmd); + } + + void Invoke(Commands cmd, Action action) { + lock (m_stateMachine) + Move(cmd); + try { + action(); + lock(m_stateMachine) + Move(Commands.Ok); + + } catch (Exception err) { + lock (m_stateMachine) { + Move(Commands.Fail); + m_lastError = err; + } + throw; + } } + IPromise InvokeAsync(Commands cmd, Func action, Action chain) { + IPromise promise = null; + IPromise prev; + + var task = new ActionChainTask(action, null, null, true); + + lock (m_stateMachine) { + Move(cmd); + + prev = m_pending; + + promise = task.Then( + () => { + lock(m_stateMachine) { + if (m_pending == promise) { + Move(Commands.Ok); + m_pending = null; + } + } + }, e => { + lock(m_stateMachine) { + if (m_pending == promise) { + Move(Commands.Fail); + m_pending = null; + m_lastError = e; + } + } + throw new PromiseTransientException(e); + } + ); + + m_pending = promise; + } + + if (prev == null) + task.Resolve(); + else + chain(prev, task); + + return promise; + } + + #region IInitializable implementation public void Init() { - + Invoke(Commands.Init, OnInitialize); + } + + protected virtual void OnInitialize() { } #endregion @@ -26,33 +166,92 @@ namespace Implab.Components { #region IRunnable implementation public IPromise Start() { - throw new NotImplementedException(); + return InvokeAsync(Commands.Start, OnStart, null); } protected virtual IPromise OnStart() { return Promise.SUCCESS; } - protected virtual void Run() { + public IPromise Stop() { + return InvokeAsync(Commands.Stop, OnStop, StopPending).Then(Dispose); + } + + protected virtual IPromise OnStop() { + return Promise.SUCCESS; } - public IPromise Stop() { - throw new NotImplementedException(); + /// + /// Stops the current operation if one exists. + /// + /// Current. + /// Stop. + protected virtual void StopPending(IPromise current, IDeferred stop) { + if (current == null) { + stop.Resolve(); + } else { + // связваем текущую операцию с операцией остановки + current.On( + stop.Resolve, // если текущая операция заверщилась, то можно начинать остановку + stop.Reject, // если текущая операция дала ошибку - то все плохо, нельзя продолжать + e => stop.Resolve() // если текущая отменилась, то можно начинать остановку + ); + // посылаем текущей операции сигнал остановки + current.Cancel(); + } } public ExecutionState State { get { - throw new NotImplementedException(); + return m_stateMachine.State; } } public Exception LastError { get { - throw new NotImplementedException(); + return m_lastError; } } #endregion + + #region IDisposable implementation + + public void Dispose() { + IPromise pending; + lock (m_stateMachine) { + if (m_stateMachine.State == ExecutionState.Disposed) + return; + + Move(Commands.Dispose); + + GC.SuppressFinalize(this); + + pending = m_pending; + m_pending = null; + } + if (pending != null) { + pending.Cancel(); + pending.Timeout(DisposeTimeout).On( + () => Dispose(true, null), + err => Dispose(true, err), + reason => Dispose(true, new OperationCanceledException("The operation is cancelled", reason)) + ); + } else { + Dispose(true, m_lastError); + } + } + + ~RunnableComponent() { + Dispose(false, null); + } + + #endregion + + protected virtual void Dispose(bool disposing, Exception lastError) { + + } + } } diff --git a/Implab/Formats/ByteAlphabet.cs b/Implab/Formats/ByteAlphabet.cs new file mode 100644 --- /dev/null +++ b/Implab/Formats/ByteAlphabet.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using System.Linq; +using Implab.Automaton; + +namespace Implab.Formats { + public class ByteAlphabet : IndexedAlphabetBase { + + #region implemented abstract members of IndexedAlphabetBase + + public override int GetSymbolIndex(byte symbol) { + return (int)symbol; + } + + public IEnumerable InputSymbols { + get { + return Enumerable.Range(byte.MinValue, byte.MaxValue).Cast(); + } + } + + #endregion + } +} + diff --git a/Implab/Formats/CharAlphabet.cs b/Implab/Formats/CharAlphabet.cs new file mode 100644 --- /dev/null +++ b/Implab/Formats/CharAlphabet.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Linq; +using Implab.Automaton; + +namespace Implab.Formats { + public class CharAlphabet: IndexedAlphabetBase { + + public override int GetSymbolIndex(char symbol) { + return symbol; + } + + public IEnumerable InputSymbols { + get { return Enumerable.Range(char.MinValue, char.MaxValue).Cast(); } + } + } +} diff --git a/Implab/Formats/Grammar.cs b/Implab/Formats/Grammar.cs new file mode 100644 --- /dev/null +++ b/Implab/Formats/Grammar.cs @@ -0,0 +1,99 @@ +using Implab; +using System; +using System.Collections.Generic; +using System.Linq; +using Implab.Automaton; +using Implab.Automaton.RegularExpressions; + +namespace Implab.Formats { + /// + /// Базовый абстрактный класс. Грамматика, позволяет формулировать выражения над алфавитом типа char. + /// + public abstract class Grammar { + + protected abstract IAlphabetBuilder AlphabetBuilder { + get; + } + + protected SymbolToken UnclassifiedToken() { + return new SymbolToken(AutomatonConst.UNCLASSIFIED_INPUT); + } + + protected void DefineAlphabet(IEnumerable alphabet) { + Safe.ArgumentNotNull(alphabet, "alphabet"); + + foreach (var ch in alphabet) + AlphabetBuilder.DefineSymbol(ch); + } + + protected Token SymbolToken(TSymbol symbol) { + return Token.New(TranslateOrAdd(symbol)); + } + + protected Token SymbolToken(IEnumerable symbols) { + Safe.ArgumentNotNull(symbols, "symbols"); + + return Token.New(TranslateOrAdd(symbols).ToArray()); + } + + protected Token SymbolSetToken(params TSymbol[] set) { + return SymbolToken(set); + } + + int TranslateOrAdd(TSymbol ch) { + var t = AlphabetBuilder.Translate(ch); + if (t == AutomatonConst.UNCLASSIFIED_INPUT) + t = AlphabetBuilder.DefineSymbol(ch); + return t; + } + + IEnumerable TranslateOrAdd(IEnumerable symbols) { + return symbols.Distinct().Select(TranslateOrAdd); + } + + int TranslateOrDie(TSymbol ch) { + var t = AlphabetBuilder.Translate(ch); + if (t == AutomatonConst.UNCLASSIFIED_INPUT) + throw new ApplicationException(String.Format("Symbol '{0}' is UNCLASSIFIED", ch)); + return t; + } + + IEnumerable TranslateOrDie(IEnumerable symbols) { + return symbols.Distinct().Select(TranslateOrDie); + } + + protected Token SymbolTokenExcept(IEnumerable symbols) { + Safe.ArgumentNotNull(symbols, "symbols"); + + return Token.New( Enumerable.Range(0, AlphabetBuilder.Count).Except(TranslateOrDie(symbols)).ToArray() ); + } + + protected abstract IndexedAlphabetBase CreateAlphabet(); + + protected ScannerContext BuildScannerContext(Token regexp) { + + var dfa = new RegularDFA(AlphabetBuilder); + + var visitor = new RegularExpressionVisitor(dfa); + regexp.Accept(visitor); + visitor.BuildDFA(); + + if (dfa.IsFinalState(dfa.InitialState)) + throw new ApplicationException("The specified language contains empty token"); + + var ab = CreateAlphabet(); + var optimal = dfa.Optimize(ab); + + return new ScannerContext( + optimal.CreateTransitionTable(), + optimal.CreateFinalStateTable(), + optimal.CreateTagTable(), + optimal.InitialState, + ab.GetTranslationMap() + ); + } + + } + + +} diff --git a/Implab/Formats/JSON/JSONElementContext.cs b/Implab/Formats/JSON/JSONElementContext.cs new file mode 100644 --- /dev/null +++ b/Implab/Formats/JSON/JSONElementContext.cs @@ -0,0 +1,11 @@ +namespace Implab.Formats.JSON { + /// + /// internal + /// + enum JSONElementContext { + None, + Object, + Array, + Closed + } +} diff --git a/Implab/Formats/JSON/JSONElementType.cs b/Implab/Formats/JSON/JSONElementType.cs new file mode 100644 --- /dev/null +++ b/Implab/Formats/JSON/JSONElementType.cs @@ -0,0 +1,28 @@ +namespace Implab.Formats.JSON { + /// + /// Тип элемента на котором находится парсер + /// + public enum JSONElementType { + None, + /// + /// Начало объекта + /// + BeginObject, + /// + /// Конец объекта + /// + EndObject, + /// + /// Начало массива + /// + BeginArray, + /// + /// Конец массива + /// + EndArray, + /// + /// Простое значение + /// + Value + } +} diff --git a/Implab/Formats/JSON/JSONGrammar.cs b/Implab/Formats/JSON/JSONGrammar.cs new file mode 100644 --- /dev/null +++ b/Implab/Formats/JSON/JSONGrammar.cs @@ -0,0 +1,121 @@ +using System.Linq; +using Implab.Automaton.RegularExpressions; +using System; +using Implab.Automaton; +using Implab.Components; + +namespace Implab.Formats.JSON { + class JSONGrammar : Grammar { + public enum TokenType { + None, + BeginObject, + EndObject, + BeginArray, + EndArray, + String, + Number, + Literal, + NameSeparator, + ValueSeparator, + Whitespace, + + StringBound, + EscapedChar, + UnescapedChar, + EscapedUnicode + } + + static LazyAndWeak _instance = new LazyAndWeak(() => new JSONGrammar()); + + public static JSONGrammar Instance { + get { return _instance.Value; } + } + + readonly ScannerContext m_jsonExpression; + readonly ScannerContext m_stringExpression; + readonly CharAlphabet m_defaultAlphabet = new CharAlphabet(); + + public JSONGrammar() { + DefineAlphabet(Enumerable.Range(0, 0x20).Select(x => (char)x)); + var hexDigit = SymbolRangeToken('a','f').Or(SymbolRangeToken('A','F')).Or(SymbolRangeToken('0','9')); + var digit9 = SymbolRangeToken('1', '9'); + var zero = SymbolToken('0'); + var digit = zero.Or(digit9); + var dot = SymbolToken('.'); + var minus = SymbolToken('-'); + var sign = SymbolSetToken('-', '+'); + var expSign = SymbolSetToken('e', 'E'); + var letters = SymbolRangeToken('a', 'z'); + var integer = zero.Or(digit9.Cat(digit.EClosure())); + var frac = dot.Cat(digit.Closure()); + var exp = expSign.Cat(sign.Optional()).Cat(digit.Closure()); + var quote = SymbolToken('"'); + var backSlash = SymbolToken('\\'); + var specialEscapeChars = SymbolSetToken('\\', '"', '/', 'b', 'f', 't', 'n', 'r'); + var unicodeEspace = SymbolToken('u').Cat(hexDigit.Repeat(4)); + var whitespace = SymbolSetToken('\n', '\r', '\t', ' ').EClosure(); + var beginObject = whitespace.Cat(SymbolToken('{')).Cat(whitespace); + var endObject = whitespace.Cat(SymbolToken('}')).Cat(whitespace); + var beginArray = whitespace.Cat(SymbolToken('[')).Cat(whitespace); + var endArray = whitespace.Cat(SymbolToken(']')).Cat(whitespace); + var nameSep = whitespace.Cat(SymbolToken(':')).Cat(whitespace); + var valueSep = whitespace.Cat(SymbolToken(',')).Cat(whitespace); + + var number = minus.Optional().Cat(integer).Cat(frac.Optional()).Cat(exp.Optional()); + var literal = letters.Closure(); + var unescaped = SymbolTokenExcept(Enumerable.Range(0, 0x20).Union(new int[] { '\\', '"' }).Select(x => (char)x)); + + var jsonExpression = + number.Tag(TokenType.Number) + .Or(literal.Tag(TokenType.Literal)) + .Or(quote.Tag(TokenType.StringBound)) + .Or(beginObject.Tag(TokenType.BeginObject)) + .Or(endObject.Tag(TokenType.EndObject)) + .Or(beginArray.Tag(TokenType.BeginArray)) + .Or(endArray.Tag(TokenType.EndArray)) + .Or(nameSep.Tag(TokenType.NameSeparator)) + .Or(valueSep.Tag(TokenType.ValueSeparator)) + .Or(SymbolSetToken('\n', '\r', '\t', ' ').Closure().Tag(TokenType.Whitespace)); + + + var jsonStringExpression = + quote.Tag(TokenType.StringBound) + .Or(backSlash.Cat(specialEscapeChars).Tag(TokenType.EscapedChar)) + .Or(backSlash.Cat(unicodeEspace).Tag(TokenType.EscapedUnicode)) + .Or(unescaped.Closure().Tag(TokenType.UnescapedChar)); + + + m_jsonExpression = BuildScannerContext(jsonExpression); + m_stringExpression = BuildScannerContext(jsonStringExpression); + + + } + + protected override IAlphabetBuilder AlphabetBuilder { + get { + return m_defaultAlphabet; + } + } + + public ScannerContext JsonExpression { + get { + return m_jsonExpression; + } + } + + public ScannerContext JsonStringExpression { + get { + return m_stringExpression; + } + } + + Token SymbolRangeToken(char start, char stop) { + return SymbolToken(Enumerable.Range(start, stop - start + 1).Select(x => (char)x)); + } + + protected override IndexedAlphabetBase CreateAlphabet() { + return new CharAlphabet(); + } + + } +} diff --git a/Implab/Formats/JSON/JSONParser.cs b/Implab/Formats/JSON/JSONParser.cs new file mode 100644 --- /dev/null +++ b/Implab/Formats/JSON/JSONParser.cs @@ -0,0 +1,293 @@ +using System; +using System.Diagnostics; +using System.IO; +using Implab.Automaton; +using Implab.Automaton.RegularExpressions; +using System.Linq; +using Implab.Components; +using System.Collections.Generic; + +namespace Implab.Formats.JSON { + /// + /// Pull парсер JSON данных. + /// + /// + /// Следует отметить отдельную интерпретацию свойства , + /// оно означает текущий уровень вложенности объектов, однако закрывающий + /// элемент объекта и массива имеет уровень меньше, чем сам объект. + /// + /// { // Level = 1 + /// "name" : "Peter", // Level = 1 + /// "address" : { // Level = 2 + /// city : "Stern" // Level = 2 + /// } // Level = 1 + /// } // Level = 0 + /// + /// + public class JSONParser : Disposable { + + enum MemberContext { + MemberName, + MemberValue + } + + #region Parser rules + struct ParserContext { + readonly int[,] m_dfa; + int m_state; + + readonly JSONElementContext m_elementContext; + + public ParserContext(int[,] dfa, int state, JSONElementContext context) { + m_dfa = dfa; + m_state = state; + m_elementContext = context; + } + + public bool Move(JsonTokenType token) { + var next = m_dfa[m_state, (int)token]; + if (next == AutomatonConst.UNREACHABLE_STATE) + return false; + m_state = next; + return true; + } + + public JSONElementContext ElementContext { + get { return m_elementContext; } + } + } + + static readonly ParserContext _jsonContext; + static readonly ParserContext _objectContext; + static readonly ParserContext _arrayContext; + + static JSONParser() { + + var valueExpression = MakeToken(JsonTokenType.BeginArray, JsonTokenType.BeginObject, JsonTokenType.Literal, JsonTokenType.Number, JsonTokenType.String); + var memberExpression = MakeToken(JsonTokenType.String).Cat(MakeToken(JsonTokenType.NameSeparator)).Cat(valueExpression); + + var objectExpression = memberExpression + .Cat( + MakeToken(JsonTokenType.ValueSeparator) + .Cat(memberExpression) + .EClosure() + ) + .Optional() + .Cat(MakeToken(JsonTokenType.EndObject)) + .End(); + + var arrayExpression = valueExpression + .Cat( + MakeToken(JsonTokenType.ValueSeparator) + .Cat(valueExpression) + .EClosure() + ) + .Optional() + .Cat(MakeToken(JsonTokenType.EndArray)) + .End(); + + var jsonExpression = valueExpression.End(); + + _jsonContext = CreateParserContext(jsonExpression, JSONElementContext.None); + _objectContext = CreateParserContext(objectExpression, JSONElementContext.Object); + _arrayContext = CreateParserContext(arrayExpression, JSONElementContext.Array); + } + + static Token MakeToken(params JsonTokenType[] input) { + return Token.New( input.Select(t => (int)t).ToArray() ); + } + + static ParserContext CreateParserContext(Token expr, JSONElementContext context) { + + var dfa = new DFATable(); + var builder = new RegularExpressionVisitor(dfa); + expr.Accept(builder); + builder.BuildDFA(); + + return new ParserContext(dfa.CreateTransitionTable(), dfa.InitialState, context); + } + + #endregion + + readonly JSONScanner m_scanner; + MemberContext m_memberContext; + + JSONElementType m_elementType; + object m_elementValue; + string m_memberName = String.Empty; + + Stack m_stack = new Stack(); + ParserContext m_context = _jsonContext; + + /// + /// Создает новый парсер на основе строки, содержащей JSON + /// + /// + public JSONParser(string text) { + Safe.ArgumentNotEmpty(text, "text"); + m_scanner = new JSONScanner(text); + } + + /// + /// Создает новый экземпляр парсера, на основе текстового потока. + /// + /// Текстовый поток. + public JSONParser(TextReader reader) { + Safe.ArgumentNotNull(reader, "reader"); + m_scanner = new JSONScanner(reader); + } + + public int Level { + get { return m_stack.Count; } + } + + /// + /// Тип текущего элемента на котором стоит парсер. + /// + public JSONElementType ElementType { + get { return m_elementType; } + } + + /// + /// Имя элемента - имя свойства родительского контейнера. Для элементов массивов и корневого всегда + /// пустая строка. + /// + public string ElementName { + get { return m_memberName; } + } + + /// + /// Значение элемента. Только для элементов типа , для остальных null + /// + public object ElementValue { + get { return m_elementValue; } + } + + /// + /// Читает слеюудущий объект из потока + /// + /// true - операция чтения прошла успешно, false - конец данных + public bool Read() { + object tokenValue; + JsonTokenType tokenType; + + m_memberName = String.Empty; + + while (m_scanner.ReadToken(out tokenValue, out tokenType)) { + if(!m_context.Move(tokenType)) + UnexpectedToken(tokenValue, tokenType); + + switch (tokenType) { + case JsonTokenType.BeginObject: + m_stack.Push(m_context); + m_context = _objectContext; + + m_elementValue = null; + m_memberContext = MemberContext.MemberName; + m_elementType = JSONElementType.BeginObject; + return true; + case JsonTokenType.EndObject: + if (m_stack.Count == 0) + UnexpectedToken(tokenValue, tokenType); + m_context = m_stack.Pop(); + + m_elementValue = null; + m_elementType = JSONElementType.EndObject; + return true; + case JsonTokenType.BeginArray: + m_stack.Push(m_context); + m_context = _arrayContext; + + m_elementValue = null; + m_memberContext = MemberContext.MemberValue; + m_elementType = JSONElementType.BeginArray; + return true; + case JsonTokenType.EndArray: + if (m_stack.Count == 0) + UnexpectedToken(tokenValue, tokenType); + m_context = m_stack.Pop(); + + m_elementValue = null; + m_elementType = JSONElementType.EndArray; + return true; + case JsonTokenType.String: + if (m_memberContext == MemberContext.MemberName) { + m_memberName = (string)tokenValue; + break; + } + m_elementType = JSONElementType.Value; + m_elementValue = tokenValue; + return true; + case JsonTokenType.Number: + m_elementType = JSONElementType.Value; + m_elementValue = tokenValue; + return true; + case JsonTokenType.Literal: + m_elementType = JSONElementType.Value; + m_elementValue = ParseLiteral((string)tokenValue); + return true; + case JsonTokenType.NameSeparator: + m_memberContext = MemberContext.MemberValue; + break; + case JsonTokenType.ValueSeparator: + m_memberContext = m_context.ElementContext == JSONElementContext.Object ? MemberContext.MemberName : MemberContext.MemberValue; + break; + default: + UnexpectedToken(tokenValue, tokenType); + break; + } + } + if (m_context.ElementContext != JSONElementContext.None) + throw new ParserException("Unexpedted end of data"); + + EOF = true; + + return false; + } + + object ParseLiteral(string literal) { + switch (literal) { + case "null": + return null; + case "false": + return false; + case "true": + return true; + default: + UnexpectedToken(literal, JsonTokenType.Literal); + return null; // avoid compliler error + } + } + + void UnexpectedToken(object value, JsonTokenType tokenType) { + throw new ParserException(String.Format("Unexpected token {0}: '{1}'", tokenType, value)); + } + + + /// + /// Признак конца потока + /// + public bool EOF { + get; + private set; + } + + protected override void Dispose(bool disposing) { + if (disposing) + Safe.Dispose(m_scanner); + } + + /// + /// Переходит в конец текущего объекта. + /// + public void SeekElementEnd() { + var level = Level - 1; + + Debug.Assert(level >= 0); + + while (Level != level) + Read(); + } + } + +} diff --git a/Implab/Formats/JSON/JSONScanner.cs b/Implab/Formats/JSON/JSONScanner.cs new file mode 100644 --- /dev/null +++ b/Implab/Formats/JSON/JSONScanner.cs @@ -0,0 +1,109 @@ +using System; +using System.Globalization; +using Implab.Automaton; +using System.Text; +using Implab.Components; +using System.IO; + +namespace Implab.Formats.JSON { + /// + /// Сканнер (лексер), разбивающий поток символов на токены JSON. + /// + public class JSONScanner : Disposable { + readonly StringBuilder m_builder = new StringBuilder(); + + readonly ScannerContext m_jsonContext = JSONGrammar.Instance.JsonExpression; + readonly ScannerContext m_stringContext = JSONGrammar.Instance.JsonStringExpression; + + + readonly TextScanner m_scanner; + + /// + /// Создает новый экземпляр сканнера + /// + public JSONScanner(string text) { + Safe.ArgumentNotEmpty(text, "text"); + + m_scanner = new StringScanner(text); + } + + public JSONScanner(TextReader reader, int bufferMax, int chunkSize) { + Safe.ArgumentNotNull(reader, "reader"); + + m_scanner = new ReaderScanner(reader, bufferMax, chunkSize); + } + + public JSONScanner(TextReader reader) : this(reader, 1024*1024, 1024){ + } + + /// + /// Читает следующий лексический элемент из входных данных. + /// + /// Возвращает значение прочитанного токена. + /// Возвращает тип прочитанного токена. + /// true - чтение произведено успешно. false - достигнут конец входных данных + /// В случе если токен не распознается, возникает исключение. Значения токенов обрабатываются, т.е. + /// в строках обрабатываются экранированные символы, числа становтся типа double. + public bool ReadToken(out object tokenValue, out JsonTokenType tokenType) { + JSONGrammar.TokenType[] tag; + while (m_jsonContext.Execute(m_scanner, out tag)) { + switch (tag[0]) { + case JSONGrammar.TokenType.StringBound: + tokenValue = ReadString(); + tokenType = JsonTokenType.String; + break; + case JSONGrammar.TokenType.Number: + tokenValue = Double.Parse(m_scanner.GetTokenValue(), CultureInfo.InvariantCulture); + tokenType = JsonTokenType.Number; + break; + case JSONGrammar.TokenType.Whitespace: + continue; + default: + tokenType = (JsonTokenType)tag[0]; + tokenValue = m_scanner.GetTokenValue(); + break; + } + return true; + } + tokenValue = null; + tokenType = JsonTokenType.None; + return false; + } + + string ReadString() { + int pos = 0; + var buf = new char[6]; // the buffer for unescaping chars + + JSONGrammar.TokenType[] tag; + m_builder.Clear(); + + while (m_stringContext.Execute(m_scanner, out tag)) { + switch (tag[0]) { + case JSONGrammar.TokenType.StringBound: + return m_builder.ToString(); + case JSONGrammar.TokenType.UnescapedChar: + m_scanner.CopyTokenTo(m_builder); + break; + case JSONGrammar.TokenType.EscapedUnicode: // \xXXXX - unicode escape sequence + m_scanner.CopyTokenTo(buf, 0); + m_builder.Append(StringTranslator.TranslateHexUnicode(buf, 2)); + pos++; + break; + case JSONGrammar.TokenType.EscapedChar: // \t - escape sequence + m_scanner.CopyTokenTo(buf, 0); + m_builder.Append(StringTranslator.TranslateEscapedChar(buf[1])); + break; + } + + } + + throw new ParserException("Unexpected end of data"); + } + + protected override void Dispose(bool disposing) { + if (disposing) + Safe.Dispose(m_scanner); + base.Dispose(disposing); + } + } +} diff --git a/Implab/Formats/JSON/JSONWriter.cs b/Implab/Formats/JSON/JSONWriter.cs new file mode 100644 --- /dev/null +++ b/Implab/Formats/JSON/JSONWriter.cs @@ -0,0 +1,319 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Globalization; +using System.Diagnostics; + +namespace Implab.Formats.JSON { + public class JSONWriter { + struct Context { + public bool needComma; + public JSONElementContext element; + } + Stack m_contextStack = new Stack(); + Context m_context; + + const int BUFFER_SIZE = 64; + + TextWriter m_writer; + readonly bool m_indent = true; + readonly int m_indentSize = 4; + readonly char[] m_buffer = new char[BUFFER_SIZE]; + int m_bufferPos; + + static readonly char [] _hex = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + static readonly char [] _escapeBKS, + _escapeFWD, + _escapeCR, + _escapeNL, + _escapeTAB, + _escapeBSLASH, + _escapeQ; + + static JSONWriter() { + _escapeBKS = "\\b".ToCharArray(); + _escapeFWD = "\\f".ToCharArray(); + _escapeCR = "\\r".ToCharArray(); + _escapeNL = "\\n".ToCharArray(); + _escapeTAB = "\\t".ToCharArray(); + _escapeBSLASH = "\\\\".ToCharArray(); + _escapeQ = "\\\"".ToCharArray(); + } + + public JSONWriter(TextWriter writer) { + Safe.ArgumentNotNull(writer, "writer"); + m_writer = writer; + } + + public JSONWriter(TextWriter writer, bool indent) { + Safe.ArgumentNotNull(writer, "writer"); + + m_writer = writer; + m_indent = indent; + } + + void WriteIndent() { + if (m_indent) { + var indent = new char[m_contextStack.Count * m_indentSize + 1]; + indent[0] = '\n'; + for (int i = 1; i < indent.Length; i++) + indent[i] = ' '; + m_writer.Write(new String(indent)); + } else { + m_writer.Write(' '); + } + } + + void WriteMemberName(string name) { + Safe.ArgumentNotEmpty(name, "name"); + if (m_context.element != JSONElementContext.Object) + OperationNotApplicable("WriteMember"); + if (m_context.needComma) + m_writer.Write(","); + + WriteIndent(); + m_context.needComma = true; + Write(name); + m_writer.Write(" : "); + } + + public void WriteValue(string name, string value) { + WriteMemberName(name); + Write(value); + } + + public void WriteValue(string name, bool value) { + WriteMemberName(name); + Write(value); + } + + public void WriteValue(string name, double value) { + WriteMemberName(name); + Write(value); + } + + public void WriteValue(string value) { + if (m_context.element == JSONElementContext.Array) { + + if (m_context.needComma) + m_writer.Write(","); + WriteIndent(); + m_context.needComma = true; + + Write(value); + } else if (m_context.element == JSONElementContext.None) { + Write(value); + m_context.element = JSONElementContext.Closed; + } else { + OperationNotApplicable("WriteValue"); + } + } + + public void WriteValue(bool value) { + if (m_context.element == JSONElementContext.Array) { + + if (m_context.needComma) + m_writer.Write(","); + WriteIndent(); + m_context.needComma = true; + + Write(value); + } else if (m_context.element == JSONElementContext.None) { + Write(value); + m_context.element = JSONElementContext.Closed; + } else { + OperationNotApplicable("WriteValue"); + } + } + + public void WriteValue(double value) { + if (m_context.element == JSONElementContext.Array) { + + if (m_context.needComma) + m_writer.Write(","); + WriteIndent(); + m_context.needComma = true; + + Write(value); + } else if (m_context.element == JSONElementContext.None) { + Write(value); + m_context.element = JSONElementContext.Closed; + } else { + OperationNotApplicable("WriteValue"); + } + } + + public void BeginObject() { + if (m_context.element != JSONElementContext.None && m_context.element != JSONElementContext.Array) + OperationNotApplicable("BeginObject"); + if (m_context.needComma) + m_writer.Write(","); + + WriteIndent(); + + m_context.needComma = true; + + m_contextStack.Push(m_context); + + m_context = new Context { element = JSONElementContext.Object, needComma = false }; + m_writer.Write("{"); + } + + public void BeginObject(string name) { + WriteMemberName(name); + + m_contextStack.Push(m_context); + + m_context = new Context { element = JSONElementContext.Object, needComma = false }; + m_writer.Write("{"); + } + + public void EndObject() { + if (m_context.element != JSONElementContext.Object) + OperationNotApplicable("EndObject"); + + m_context = m_contextStack.Pop(); + if (m_contextStack.Count == 0) + m_context.element = JSONElementContext.Closed; + WriteIndent(); + m_writer.Write("}"); + } + + public void BeginArray() { + if (m_context.element != JSONElementContext.None && m_context.element != JSONElementContext.Array) + throw new InvalidOperationException(); + if (m_context.needComma) { + m_writer.Write(","); + + } + m_context.needComma = true; + + WriteIndent(); + m_contextStack.Push(m_context); + m_context = new Context { element = JSONElementContext.Array, needComma = false }; + m_writer.Write("["); + } + + public void BeginArray(string name) { + WriteMemberName(name); + + m_contextStack.Push(m_context); + + m_context = new Context { element = JSONElementContext.Array, needComma = false }; + m_writer.Write("["); + } + + public void EndArray() { + if (m_context.element != JSONElementContext.Array) + OperationNotApplicable("EndArray"); + + m_context = m_contextStack.Pop(); + if (m_contextStack.Count == 0) + m_context.element = JSONElementContext.Closed; + WriteIndent(); + m_writer.Write("]"); + } + + void Write(bool value) { + m_writer.Write(value ? "true" : "false"); + } + + void FlushBuffer() { + if (m_bufferPos > 0) { + m_writer.Write(m_buffer, 0, m_bufferPos); + m_bufferPos = 0; + } + } + + void Write(string value) { + if (value == null) { + m_writer.Write("null"); + return; + } + + Debug.Assert(m_bufferPos == 0); + + var chars = value.ToCharArray(); + m_buffer[m_bufferPos++] = '"'; + + // Analysis disable once ForCanBeConvertedToForeach + for (int i = 0; i < chars.Length; i++) { + var ch = chars[i]; + + char[] escapeSeq; + + switch (ch) { + case '\b': + escapeSeq = _escapeBKS; + break; + case '\f': + escapeSeq = _escapeFWD; + break; + case '\r': + escapeSeq = _escapeCR; + break; + case '\n': + escapeSeq = _escapeNL; + break; + case '\t': + escapeSeq = _escapeTAB; + break; + case '\\': + escapeSeq = _escapeBSLASH; + break; + case '"': + escapeSeq = _escapeQ; + break; + default: + if (ch < 0x20) { + if (m_bufferPos + 6 > BUFFER_SIZE) + FlushBuffer(); + + m_buffer[m_bufferPos++] = '\\'; + m_buffer[m_bufferPos++] = 'u'; + m_buffer[m_bufferPos++] = '0'; + m_buffer[m_bufferPos++] = '0'; + m_buffer[m_bufferPos++] = _hex[ch >> 4 & 0xf]; + m_buffer[m_bufferPos++] = _hex[ch & 0xf]; + + } else { + if (m_bufferPos >= BUFFER_SIZE) + FlushBuffer(); + m_buffer[m_bufferPos++] = ch; + } + continue; + } + + if (m_bufferPos + escapeSeq.Length > BUFFER_SIZE) + FlushBuffer(); + + Array.Copy(escapeSeq, 0, m_buffer, m_bufferPos, escapeSeq.Length); + m_bufferPos += escapeSeq.Length; + + } + + if (m_bufferPos >= BUFFER_SIZE) + FlushBuffer(); + + m_buffer[m_bufferPos++] = '"'; + + FlushBuffer(); + } + + void Write(double value) { + if (double.IsNaN(value)) + Write("NaN"); + else if (double.IsNegativeInfinity(value)) + Write("-Infinity"); + else if (double.IsPositiveInfinity(value)) + Write("Infinity"); + else + m_writer.Write(value.ToString(CultureInfo.InvariantCulture)); + } + + void OperationNotApplicable(string opName) { + throw new InvalidOperationException(String.Format("The operation '{0}' isn't applicable in the context of '{1}'", opName, m_context.element )); + } + + } +} diff --git a/Implab/Formats/JSON/JSONXmlReader.cs b/Implab/Formats/JSON/JSONXmlReader.cs new file mode 100644 --- /dev/null +++ b/Implab/Formats/JSON/JSONXmlReader.cs @@ -0,0 +1,335 @@ +using Implab; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Xml; + +namespace Implab.Formats.JSON { + public class JSONXmlReader : XmlReader { + + enum ValueContext { + Undefined, + ElementStart, + ElementValue, + ElementEnd, + ElementEmpty + } + + struct LocalNameContext { + public string localName; + public bool isArray; + } + + JSONParser m_parser; + ValueContext m_valueContext; + ReadState m_state = ReadState.Initial; + Stack m_localNameStack = new Stack(); + LocalNameContext m_localName; + int m_depthCorrection; + + readonly string m_rootName; + readonly string m_prefix; + readonly string m_namespaceUri; + readonly bool m_flattenArrays; + readonly string m_arrayItemName; + readonly XmlNameTable m_nameTable; + + JSONXmlReader(JSONParser parser, JSONXmlReaderOptions options) { + m_parser = parser; + + if (options != null) { + m_prefix = options.NodesPrefix ?? String.Empty; + m_namespaceUri = options.NamespaceURI ?? String.Empty; + m_rootName = options.RootName ?? "json"; + m_flattenArrays = options.FlattenArrays; + m_arrayItemName = options.ArrayItemName ?? "item"; + m_nameTable = options.NameTable ?? new NameTable(); + } else { + m_prefix = String.Empty; + m_namespaceUri = String.Empty; + m_rootName = "json"; + m_flattenArrays = false; + m_arrayItemName = "item"; + m_nameTable = new NameTable(); + } + } + + /// + /// Always 0, JSON doesn't support attributes + /// + public override int AttributeCount { + get { return 0; } + } + + public override string BaseURI { + get { return String.Empty; } + } + + public override int Depth { + get { + return m_localNameStack.Count + m_depthCorrection; + } + } + + public override bool EOF { + get { return m_parser.EOF; } + } + + /// + /// Always throws an exception + /// + /// + /// + public override string GetAttribute(int i) { + throw new ArgumentOutOfRangeException(); + } + + /// + /// Always returns empty string + /// + /// + /// + /// + public override string GetAttribute(string name, string namespaceURI) { + return String.Empty; + } + + /// + /// Always returns empty string + /// + /// + /// + public override string GetAttribute(string name) { + return String.Empty; + } + + public override bool IsEmptyElement { + get { return m_parser.ElementType == JSONElementType.Value && m_valueContext == ValueContext.ElementEmpty; } + } + + public override string LocalName { + get { return m_localName.localName; } + } + + public override string LookupNamespace(string prefix) { + if (String.IsNullOrEmpty(prefix) || prefix == m_prefix) + return m_namespaceUri; + + return String.Empty; + } + + public override bool MoveToAttribute(string name, string ns) { + return false; + } + + public override bool MoveToAttribute(string name) { + return false; + } + + public override bool MoveToElement() { + return false; + } + + public override bool MoveToFirstAttribute() { + return false; + } + + public override bool MoveToNextAttribute() { + return false; + } + + public override XmlNameTable NameTable { + get { return m_nameTable; } + } + + public override string NamespaceURI { + get { return m_namespaceUri; } + } + + public override XmlNodeType NodeType { + get { + switch (m_parser.ElementType) { + case JSONElementType.BeginObject: + case JSONElementType.BeginArray: + return XmlNodeType.Element; + case JSONElementType.EndObject: + case JSONElementType.EndArray: + return XmlNodeType.EndElement; + case JSONElementType.Value: + switch (m_valueContext) { + case ValueContext.ElementStart: + case ValueContext.ElementEmpty: + return XmlNodeType.Element; + case ValueContext.ElementValue: + return XmlNodeType.Text; + case ValueContext.ElementEnd: + return XmlNodeType.EndElement; + default: + throw new InvalidOperationException(); + } + default: + throw new InvalidOperationException(); + } + } + } + + public override string Prefix { + get { return m_prefix; } + } + + public override bool Read() { + if (m_state != ReadState.Interactive && m_state != ReadState.Initial) + return false; + + if (m_state == ReadState.Initial) + m_state = ReadState.Interactive; + + try { + switch (m_parser.ElementType) { + case JSONElementType.Value: + switch (m_valueContext) { + case ValueContext.ElementStart: + SetLocalName(String.Empty); + m_valueContext = ValueContext.ElementValue; + return true; + case ValueContext.ElementValue: + RestoreLocalName(); + m_valueContext = ValueContext.ElementEnd; + return true; + case ValueContext.ElementEmpty: + case ValueContext.ElementEnd: + RestoreLocalName(); + break; + } + break; + case JSONElementType.EndArray: + case JSONElementType.EndObject: + RestoreLocalName(); + break; + } + string itemName = m_parser.ElementType == JSONElementType.None ? m_rootName : m_flattenArrays ? m_localName.localName : m_arrayItemName; + while (m_parser.Read()) { + if (!String.IsNullOrEmpty(m_parser.ElementName)) + itemName = m_parser.ElementName; + + switch (m_parser.ElementType) { + case JSONElementType.BeginArray: + if (m_flattenArrays && !m_localName.isArray) { + m_depthCorrection--; + SetLocalName(itemName, true); + continue; + } + SetLocalName(itemName, true); + break; + case JSONElementType.BeginObject: + SetLocalName(itemName); + break; + case JSONElementType.EndArray: + if (m_flattenArrays && !m_localNameStack.Peek().isArray) { + RestoreLocalName(); + m_depthCorrection++; + continue; + } + break; + case JSONElementType.EndObject: + break; + case JSONElementType.Value: + SetLocalName(itemName); + m_valueContext = m_parser.ElementValue == null ? ValueContext.ElementEmpty : ValueContext.ElementStart; + break; + } + return true; + } + + m_state = ReadState.EndOfFile; + return false; + } catch { + m_state = ReadState.Error; + throw; + } + } + + public override bool ReadAttributeValue() { + return false; + } + + public override ReadState ReadState { + get { return m_state; } + } + + public override void ResolveEntity() { + // do nothing + } + + public override string Value { + get { + if (m_parser.ElementValue == null) + return String.Empty; + if (Convert.GetTypeCode(m_parser.ElementValue) == TypeCode.Double) + return ((double)m_parser.ElementValue).ToString(CultureInfo.InvariantCulture); + return m_parser.ElementValue.ToString(); + } + } + + void SetLocalName(string name) { + m_localNameStack.Push(m_localName); + m_localName.localName = name; + m_localName.isArray = false; + } + + void SetLocalName(string name, bool isArray) { + m_localNameStack.Push(m_localName); + m_localName.localName = name; + m_localName.isArray = isArray; + } + + void RestoreLocalName() { + m_localName = m_localNameStack.Pop(); + } + + public override void Close() { + + } + + protected override void Dispose(bool disposing) { + #if MONO + disposing = true; + #endif + if (disposing) { + m_parser.Dispose(); + } + base.Dispose(disposing); + } + + public static JSONXmlReader Create(string file, JSONXmlReaderOptions options) { + return Create(File.OpenText(file), options); + } + + /// + /// Creates the XmlReader for the specified text stream with JSON data. + /// + /// Text reader. + /// Options. + /// + /// The reader will be disposed when the XmlReader is disposed. + /// + public static JSONXmlReader Create(TextReader reader, JSONXmlReaderOptions options) { + return new JSONXmlReader(new JSONParser(reader), options); + } + + /// + /// Creates the XmlReader for the specified stream with JSON data. + /// + /// Stream. + /// Options. + /// + /// The stream will be disposed when the XmlReader is disposed. + /// + public static JSONXmlReader Create(Stream stream, JSONXmlReaderOptions options) { + Safe.ArgumentNotNull(stream, "stream"); + // HACK don't dispose StreaReader to keep stream opened + return Create(new StreamReader(stream), options); + } + } +} diff --git a/Implab/Formats/JSON/JSONXmlReaderOptions.cs b/Implab/Formats/JSON/JSONXmlReaderOptions.cs new file mode 100644 --- /dev/null +++ b/Implab/Formats/JSON/JSONXmlReaderOptions.cs @@ -0,0 +1,62 @@ + +using System.Xml; + +namespace Implab.Formats.JSON { + /// + /// Набор необязательных параметров для , позволяющий управлять процессом + /// интерпретации JSON документа. + /// + public class JSONXmlReaderOptions { + /// + /// Пространство имен в котором будут располагаться читаемые элементы документа + /// + public string NamespaceURI { + get; + set; + } + + /// + /// Интерпретировать массивы как множественные элементы (убирает один уровень вложенности), иначе массив + /// представляется в виде узла, дочерними элементами которого являются элементы массива, имена дочерних элементов + /// определяются свойством . По умолчанию false. + /// + public bool FlattenArrays { + get; + set; + } + + /// + /// Префикс, для узлов документа + /// + public string NodesPrefix { + get; + set; + } + + /// + /// Имя корневого элемента в xml документе + /// + public string RootName { + get; + set; + } + + /// + /// Имя элемента для массивов, если не включена опция . + /// По умолчанию item. + /// + public string ArrayItemName { + get; + set; + } + + /// + /// Таблица атомизированных строк для построения документа. + /// + public XmlNameTable NameTable { + get; + set; + } + + } +} diff --git a/Implab/Formats/JSON/JsonTokenType.cs b/Implab/Formats/JSON/JsonTokenType.cs new file mode 100644 --- /dev/null +++ b/Implab/Formats/JSON/JsonTokenType.cs @@ -0,0 +1,44 @@ +namespace Implab.Formats.JSON { + /// + /// Тип токенов, возвращаемых . + /// + public enum JsonTokenType : int { + None = 0, + /// + /// Начало объекта + /// + BeginObject, + /// + /// Конец объекта + /// + EndObject, + /// + /// Начало массива + /// + BeginArray, + /// + /// Конец массива + /// + EndArray, + /// + /// Строка + /// + String, + /// + /// Число + /// + Number, + /// + /// Литерал + /// + Literal, + /// + /// Разделитель имени : + /// + NameSeparator, + /// + /// Разделитель имени , + /// + ValueSeparator + } +} diff --git a/Implab/Formats/JSON/StringTranslator.cs b/Implab/Formats/JSON/StringTranslator.cs new file mode 100644 --- /dev/null +++ b/Implab/Formats/JSON/StringTranslator.cs @@ -0,0 +1,52 @@ +using Implab; +using Implab.Formats; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Implab.Formats.JSON { + /// + /// Класс для преобразования экранированной строки JSON + /// + static class StringTranslator { + static readonly char[] _escMap; + static readonly int[] _hexMap; + + static StringTranslator() { + var chars = new char[] { 'b', 'f', 't', 'r', 'n', '\\', '/' }; + var vals = new char[] { '\b', '\f', '\t', '\r', '\n', '\\', '/' }; + + _escMap = new char[chars.Max() + 1]; + + for (int i = 0; i < chars.Length; i++) + _escMap[chars[i]] = vals[i]; + + var hexs = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F' }; + var ints = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 10, 11, 12, 13, 14, 15 }; + + _hexMap = new int[hexs.Max() + 1]; + + for (int i = 0; i < hexs.Length; i++) + _hexMap[hexs[i]] = ints[i]; + + } + + internal static char TranslateEscapedChar(char symbol) { + return _escMap[symbol]; + } + + internal static char TranslateHexUnicode(char[] symbols, int offset) { + Debug.Assert(symbols != null); + Debug.Assert(symbols.Length - offset >= 4); + + int value = (_hexMap[symbols[offset]] << 12) + | (_hexMap[symbols[offset + 1]] << 8) + | (_hexMap[symbols[offset + 2]] << 4) + | (_hexMap[symbols[offset + 3]]); + return (char)value; + } + } +} diff --git a/Implab/Formats/ReaderScanner.cs b/Implab/Formats/ReaderScanner.cs new file mode 100644 --- /dev/null +++ b/Implab/Formats/ReaderScanner.cs @@ -0,0 +1,30 @@ +using System; +using System.IO; + +namespace Implab.Formats { + public class ReaderScanner: TextScanner { + const int CHUNK_SIZE = 1024*4; + const int BUFFER_MAX = CHUNK_SIZE*1024; + + readonly TextReader m_reader; + + public ReaderScanner(TextReader reader, int limit, int chunk) : base(limit, chunk) { + Safe.ArgumentNotNull(reader, "reader"); + m_reader = reader; + } + + public ReaderScanner(TextReader reader) : this(reader, BUFFER_MAX, CHUNK_SIZE) { + } + + protected override int Read(char[] buffer, int offset, int size) { + return m_reader.Read(buffer, offset, size); + } + + protected override void Dispose(bool disposing) { + if (disposing) + Safe.Dispose(m_reader); + base.Dispose(disposing); + } + } +} + diff --git a/Implab/Formats/ScannerContext.cs b/Implab/Formats/ScannerContext.cs new file mode 100644 --- /dev/null +++ b/Implab/Formats/ScannerContext.cs @@ -0,0 +1,30 @@ +namespace Implab.Formats { + /// + /// Represents a scanner configuration usefull to recongnize token, based on the DFA. + /// + public class ScannerContext { + + public int[,] Dfa { get; private set; } + + public bool[] Final { get; private set; } + + public TTag[][] Tags { get; private set; } + + public int State { get; private set; } + + public int[] Alphabet { get; private set; } + + public ScannerContext(int[,] dfa, bool[] final, TTag[][] tags, int state, int[] alphabet) { + Dfa = dfa; + Final = final; + Tags = tags; + State = state; + Alphabet = alphabet; + } + + public bool Execute(TextScanner scanner, out TTag[] tag) { + return scanner.ReadToken(Dfa, Final, Tags, State, Alphabet, out tag); + } + } +} + diff --git a/Implab/Formats/StringScanner.cs b/Implab/Formats/StringScanner.cs new file mode 100644 --- /dev/null +++ b/Implab/Formats/StringScanner.cs @@ -0,0 +1,18 @@ +using System; + +namespace Implab.Formats { + public class StringScanner: TextScanner { + const int CHUNK_SIZE = 1024; + + public StringScanner(string text) : base(null) { + Safe.ArgumentNotNull(text, "text"); + var data = text.ToCharArray(); + Feed(data, 0, data.Length); + } + + protected override int Read(char[] buffer, int offset, int size) { + return 0; + } + } +} + diff --git a/Implab/Formats/TextScanner.cs b/Implab/Formats/TextScanner.cs new file mode 100644 --- /dev/null +++ b/Implab/Formats/TextScanner.cs @@ -0,0 +1,157 @@ +using System; +using Implab.Components; +using System.Diagnostics; +using Implab.Automaton; +using System.Text; + +namespace Implab.Formats { + public abstract class TextScanner : Disposable { + readonly int m_bufferMax; + readonly int m_chunkSize; + + char[] m_buffer; + int m_bufferOffset; + int m_bufferSize; + int m_tokenOffset; + int m_tokenLength; + + /// + /// Initializes a new instance of the class. + /// + /// Buffer max. + /// Chunk size. + protected TextScanner(int bufferMax, int chunkSize) { + Debug.Assert(m_chunkSize <= m_bufferMax); + + m_bufferMax = bufferMax; + m_chunkSize = chunkSize; + } + + /// + /// Initializes a new instance of the class. + /// + /// Buffer. + protected TextScanner(char[] buffer) { + if (buffer != null) { + m_buffer = buffer; + m_bufferSize = buffer.Length; + } + } + + /// + /// (hungry) Reads the next token. + /// + /// true, if token internal was read, false if there is no more tokens in the stream. + /// The transition map for the automaton + /// Final states of the automaton. + /// Tags. + /// The initial state for the automaton. + /// + /// + internal bool ReadToken(int[,] dfa, bool[] final, TTag[][] tags, int state, int[] alphabet, out TTag[] tag) { + m_tokenLength = 0; + tag = null; + + var maxSymbol = alphabet.Length - 1; + int next; + do { + // after the next chunk is read the offset in the buffer may change + int pos = m_bufferOffset + m_tokenLength; + next = state; + while (pos < m_bufferSize) { + var ch = m_buffer[pos]; + + next = dfa[next, ch > maxSymbol ? AutomatonConst.UNCLASSIFIED_INPUT : alphabet[ch]]; + + if (next == AutomatonConst.UNREACHABLE_STATE) + break; + + state = next; + pos++; + } + m_tokenLength = pos - m_bufferOffset; + } while (next != AutomatonConst.UNREACHABLE_STATE && Feed()); + + m_tokenOffset = m_bufferOffset; + m_bufferOffset += m_tokenLength; + + if (final[state]) { + tag = tags[state]; + return true; + } + + if (m_bufferOffset == m_bufferSize) { + if (m_tokenLength == 0) //EOF + return false; + + throw new ParserException(); + } + + throw new ParserException(String.Format("Unexpected symbol '{0}'", m_buffer[m_bufferOffset])); + + } + + protected void Feed(char[] buffer, int offset, int length) { + m_buffer = buffer; + m_bufferOffset = offset; + m_bufferSize = offset + length; + } + + protected bool Feed() { + if (m_chunkSize <= 0) + return false; + + if (m_buffer != null) { + var free = m_buffer.Length - m_bufferSize; + + if (free < m_chunkSize) { + free += m_chunkSize; + var used = m_bufferSize - m_bufferOffset; + var size = used + free; + + if (size > m_bufferMax) + throw new ParserException(String.Format("The buffer limit ({0} Kb) is reached", m_bufferMax / 1024)); + + var temp = new char[size]; + + var read = Read(temp, used, m_chunkSize); + if (read == 0) + return false; + + Array.Copy(m_buffer, m_bufferOffset, temp, 0, used); + + m_bufferOffset = 0; + m_bufferSize = used + read; + m_buffer = temp; + } else { + var read = Read(m_buffer, m_bufferSize, m_chunkSize); + if (read == 0) + return false; + m_bufferSize += m_chunkSize; + } + return true; + } else { + Debug.Assert(m_bufferOffset == 0); + m_buffer = new char[m_chunkSize]; + m_bufferSize = Read(m_buffer, 0, m_chunkSize); + return (m_bufferSize != 0); + } + } + + protected abstract int Read(char[] buffer, int offset, int size); + + public string GetTokenValue() { + return new String(m_buffer, m_tokenOffset, m_tokenLength); + } + + public void CopyTokenTo(char[] buffer, int offset) { + Array.Copy(m_buffer, m_tokenOffset,buffer, offset, m_tokenLength); + } + + public void CopyTokenTo(StringBuilder sb) { + sb.Append(m_buffer, m_tokenOffset, m_tokenLength); + } + + } +} + diff --git a/Implab/FuncChainTask.cs b/Implab/FuncChainTask.cs --- a/Implab/FuncChainTask.cs +++ b/Implab/FuncChainTask.cs @@ -13,8 +13,10 @@ namespace Implab { if (m_task != null && LockCancelation()) { try { var operation = m_task(); - operation.On(SetResult, HandleErrorInternal, SetCancelled); + operation.On(SetResult, HandleErrorInternal, HandleCancelInternal); CancellationRequested(operation.Cancel); + } catch (OperationCanceledException reason) { + HandleCancelInternal(reason); } catch (Exception err) { HandleErrorInternal(err); } diff --git a/Implab/FuncChainTaskBase.cs b/Implab/FuncChainTaskBase.cs --- a/Implab/FuncChainTaskBase.cs +++ b/Implab/FuncChainTaskBase.cs @@ -1,13 +1,10 @@ using System; -using System.Threading; namespace Implab { - public class FuncChainTaskBase : AbstractPromise { + public class FuncChainTaskBase : AbstractTask { readonly Func> m_error; readonly Func> m_cancel; - int m_cancelationLock; - protected FuncChainTaskBase( Func> error, Func> cancel, bool autoCancellable) { m_error = error; m_cancel = cancel; @@ -21,37 +18,36 @@ namespace Implab { } public override void CancelOperation(Exception reason) { - if (LockCancelation()) { - if (m_cancel != null) { - try { - m_cancel(reason).On(SetResult, HandleErrorInternal, SetCancelled); - } catch (Exception err) { - HandleErrorInternal(err); - } - } else { - SetCancelled(reason); - } - } - + if (LockCancelation()) + HandleCancelInternal(reason); } protected void HandleErrorInternal(Exception error) { if (m_error != null) { try { - var operation = m_error(error); - - operation.On(SetResult, SetError, SetCancelled); - CancellationRequested(operation.Cancel); + var p = m_error(error); + p.On(SetResult, SetErrorInternal, SetCancelledInternal); + CancellationRequested(p.Cancel); } catch(Exception err) { - SetError(err); + SetErrorInternal(err); } } else { - SetError(error); + SetErrorInternal(error); } } - protected bool LockCancelation() { - return 0 == Interlocked.CompareExchange(ref m_cancelationLock, 1, 0); + protected void HandleCancelInternal(Exception reason) { + if (m_cancel != null) { + try { + var p = m_cancel(reason); + p.On(SetResult, HandleErrorInternal, SetCancelledInternal); + CancellationRequested(p.Cancel); + } catch (Exception err) { + HandleErrorInternal(err); + } + } else { + HandleErrorInternal(reason ?? new OperationCanceledException()); + } } } } diff --git a/Implab/FuncChainTaskT.cs b/Implab/FuncChainTaskT.cs --- a/Implab/FuncChainTaskT.cs +++ b/Implab/FuncChainTaskT.cs @@ -14,6 +14,8 @@ namespace Implab { var operation = m_task(value); operation.On(SetResult, HandleErrorInternal, SetCancelled); CancellationRequested(operation.Cancel); + } catch (OperationCanceledException reason) { + HandleCancelInternal(reason); } catch (Exception err) { HandleErrorInternal(err); } diff --git a/Implab/FuncTask.cs b/Implab/FuncTask.cs --- a/Implab/FuncTask.cs +++ b/Implab/FuncTask.cs @@ -13,6 +13,8 @@ namespace Implab { if (m_task != null && LockCancelation()) { try { SetResult(m_task()); + } catch(OperationCanceledException reason) { + HandleCancelInternal(reason); } catch(Exception err) { HandleErrorInternal(err); } diff --git a/Implab/FuncTaskBase.cs b/Implab/FuncTaskBase.cs --- a/Implab/FuncTaskBase.cs +++ b/Implab/FuncTaskBase.cs @@ -1,13 +1,10 @@ using System; -using System.Threading; namespace Implab { - public class FuncTaskBase : AbstractPromise { + public class FuncTaskBase : AbstractTask { readonly Func m_cancel; readonly Func m_error; - int m_cancelationLock; - protected FuncTaskBase( Func error, Func cancel, bool autoCancellable) { m_error = error; m_cancel = cancel; @@ -26,30 +23,30 @@ namespace Implab { try { SetResult(m_error(error)); } catch(Exception err) { - SetError(err); + SetErrorInternal(err); } } else { - SetError(error); + SetErrorInternal(error); } } public override void CancelOperation(Exception reason) { - if (LockCancelation()) { - if (m_cancel != null) { - try { - SetResult(m_cancel(reason)); - } catch (Exception err) { - HandleErrorInternal(err); - } - } else { - SetCancelled(reason); + if (LockCancelation()) + HandleCancelInternal(reason); + } + + protected void HandleCancelInternal(Exception reason) { + if (m_cancel != null) { + try { + SetResult(m_cancel(reason)); + } catch (Exception err) { + HandleErrorInternal(err); } + } else { + HandleErrorInternal(reason ?? new OperationCanceledException()); } } - protected bool LockCancelation() { - return 0 == Interlocked.CompareExchange(ref m_cancelationLock, 1, 0); - } } } diff --git a/Implab/FuncTaskT.cs b/Implab/FuncTaskT.cs --- a/Implab/FuncTaskT.cs +++ b/Implab/FuncTaskT.cs @@ -12,7 +12,9 @@ namespace Implab { if (m_task != null && LockCancelation()) { try { SetResult(m_task(value)); - } catch (Exception err) { + } catch(OperationCanceledException reason) { + HandleCancelInternal(reason); + } catch(Exception err) { HandleErrorInternal(err); } } diff --git a/Implab/Implab.csproj b/Implab/Implab.csproj --- a/Implab/Implab.csproj +++ b/Implab/Implab.csproj @@ -8,6 +8,9 @@ Implab Implab v4.5 + 0.2 + 8.0.30703 + 2.0 true @@ -88,40 +91,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -178,11 +151,50 @@ - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -257,5 +269,8 @@ + + + \ No newline at end of file diff --git a/Implab/JSON/JSONElementContext.cs b/Implab/JSON/JSONElementContext.cs deleted file mode 100644 --- a/Implab/JSON/JSONElementContext.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Implab.JSON { - /// - /// internal - /// - public enum JSONElementContext { - None, - Object, - Array, - Closed - } -} diff --git a/Implab/JSON/JSONElementType.cs b/Implab/JSON/JSONElementType.cs deleted file mode 100644 --- a/Implab/JSON/JSONElementType.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Implab.JSON { - /// - /// Тип элемента на котором находится парсер - /// - public enum JSONElementType { - None, - /// - /// Начало объекта - /// - BeginObject, - /// - /// Конец объекта - /// - EndObject, - /// - /// Начало массива - /// - BeginArray, - /// - /// Конец массива - /// - EndArray, - /// - /// Простое значение - /// - Value - } -} diff --git a/Implab/JSON/JSONGrammar.cs b/Implab/JSON/JSONGrammar.cs deleted file mode 100644 --- a/Implab/JSON/JSONGrammar.cs +++ /dev/null @@ -1,99 +0,0 @@ -using Implab.Parsing; -using System.Linq; - -namespace Implab.JSON { - class JSONGrammar : Grammar { - public enum TokenType { - None, - BeginObject, - EndObject, - BeginArray, - EndArray, - String, - Number, - Literal, - NameSeparator, - ValueSeparator, - - StringBound, - EscapedChar, - UnescapedChar, - EscapedUnicode, - - Minus, - Plus, - Sign, - Integer, - Dot, - Exp - } - - readonly CDFADefinition m_jsonDFA; - readonly CDFADefinition m_stringDFA; - - public JSONGrammar() { - DefineAlphabet(Enumerable.Range(0, 0x20).Select(x => (char)x)); - var hexDigit = SymbolRangeToken('a','f').Or(SymbolRangeToken('A','F')).Or(SymbolRangeToken('0','9')); - var digit9 = SymbolRangeToken('1', '9'); - var zero = SymbolToken('0'); - var digit = zero.Or(digit9); - var dot = SymbolToken('.'); - var minus = SymbolToken('-'); - var sign = SymbolSetToken('-', '+'); - var expSign = SymbolSetToken('e', 'E'); - var letters = SymbolRangeToken('a', 'z'); - var integer = zero.Or(digit9.Cat(digit.EClosure())); - var frac = dot.Cat(digit.Closure()); - var exp = expSign.Cat(sign.Optional()).Cat(digit.Closure()); - var quote = SymbolToken('"'); - var backSlash = SymbolToken('\\'); - var specialEscapeChars = SymbolSetToken('\\', '"', '/', 'b', 'f', 't', 'n', 'r'); - var unicodeEspace = SymbolToken('u').Cat(hexDigit.Repeat(4)); - var whitespace = SymbolSetToken('\n', '\r', '\t', ' ').EClosure(); - var beginObject = whitespace.Cat(SymbolToken('{')).Cat(whitespace); - var endObject = whitespace.Cat(SymbolToken('}')).Cat(whitespace); - var beginArray = whitespace.Cat(SymbolToken('[')).Cat(whitespace); - var endArray = whitespace.Cat(SymbolToken(']')).Cat(whitespace); - var nameSep = whitespace.Cat(SymbolToken(':')).Cat(whitespace); - var valueSep = whitespace.Cat(SymbolToken(',')).Cat(whitespace); - - var number = minus.Optional().Cat(integer).Cat(frac.Optional()).Cat(exp.Optional()); - var literal = letters.Closure(); - var unescaped = SymbolTokenExcept(Enumerable.Range(0, 0x20).Union(new int[] { '\\', '"' }).Select(x => (char)x)); - - var jsonExpression = - number.Tag(TokenType.Number) - .Or(literal.Tag(TokenType.Literal)) - .Or(quote.Tag(TokenType.StringBound)) - .Or(beginObject.Tag(TokenType.BeginObject)) - .Or(endObject.Tag(TokenType.EndObject)) - .Or(beginArray.Tag(TokenType.BeginArray)) - .Or(endArray.Tag(TokenType.EndArray)) - .Or(nameSep.Tag(TokenType.NameSeparator)) - .Or(valueSep.Tag(TokenType.ValueSeparator)); - - - var jsonStringExpression = - quote.Tag(TokenType.StringBound) - .Or(backSlash.Cat(specialEscapeChars).Tag(TokenType.EscapedChar)) - .Or(backSlash.Cat(unicodeEspace).Tag(TokenType.EscapedUnicode)) - .Or(unescaped.Closure().Tag(TokenType.UnescapedChar)); - - - m_jsonDFA = BuildDFA(jsonExpression); - m_stringDFA = BuildDFA(jsonStringExpression); - } - - public CDFADefinition JsonDFA { - get { - return m_jsonDFA; - } - } - - public CDFADefinition JsonStringDFA { - get { - return m_stringDFA; - } - } - } -} diff --git a/Implab/JSON/JSONParser.cs b/Implab/JSON/JSONParser.cs deleted file mode 100644 --- a/Implab/JSON/JSONParser.cs +++ /dev/null @@ -1,277 +0,0 @@ -using Implab.Parsing; -using System; -using System.Diagnostics; -using System.IO; - -namespace Implab.JSON { - /// - /// internal - /// - public struct JSONParserContext { - public string memberName; - public JSONElementContext elementContext; - } - - /// - /// Pull парсер JSON данных. - /// - /// - /// Следует отметить отдельную интерпретацию свойства , - /// оно означает текущий уровень вложенности объектов, однако закрывающий - /// элемент объекта и массива имеет уровень меньше, чем сам объект. - /// - /// { // Level = 1 - /// "name" : "Peter", // Level = 1 - /// "address" : { // Level = 2 - /// city : "Stern" // Level = 2 - /// } // Level = 1 - /// } // Level = 0 - /// - /// - public class JSONParser : DFAutomaton, IDisposable { - - enum MemberContext { - MemberName, - MemberValue - } - - static readonly EnumAlphabet _alphabet = EnumAlphabet.FullAlphabet; - static readonly DFAStateDescriptior[] _jsonDFA; - static readonly DFAStateDescriptior[] _objectDFA; - static readonly DFAStateDescriptior[] _arrayDFA; - - static JSONParser() { - - - var valueExpression = Token.New(JsonTokenType.BeginArray, JsonTokenType.BeginObject, JsonTokenType.Literal, JsonTokenType.Number, JsonTokenType.String); - var memberExpression = Token.New(JsonTokenType.String).Cat(Token.New(JsonTokenType.NameSeparator)).Cat(valueExpression); - - var objectExpression = memberExpression - .Cat( - Token.New(JsonTokenType.ValueSeparator) - .Cat(memberExpression) - .EClosure() - ) - .Optional() - .Cat(Token.New(JsonTokenType.EndObject)) - .Tag(0); - var arrayExpression = valueExpression - .Cat( - Token.New(JsonTokenType.ValueSeparator) - .Cat(valueExpression) - .EClosure() - ) - .Optional() - .Cat(Token.New(JsonTokenType.EndArray)) - .Tag(0); - - var jsonExpression = valueExpression.Tag(0); - - _jsonDFA = BuildDFA(jsonExpression).States; - _objectDFA = BuildDFA(objectExpression).States; - _arrayDFA = BuildDFA(arrayExpression).States; - } - - static EDFADefinition BuildDFA(Token expr) { - var builder = new DFABuilder(); - var dfa = new EDFADefinition(_alphabet); - expr.Accept(builder); - - builder.BuildDFA(dfa); - return dfa; - } - - JSONScanner m_scanner; - MemberContext m_memberContext; - - JSONElementType m_elementType; - object m_elementValue; - - /// - /// Создает новый парсер на основе строки, содержащей JSON - /// - /// - public JSONParser(string text) - : base(_jsonDFA, INITIAL_STATE, new JSONParserContext { elementContext = JSONElementContext.None, memberName = String.Empty }) { - Safe.ArgumentNotEmpty(text, "text"); - m_scanner = new JSONScanner(); - m_scanner.Feed(text.ToCharArray()); - } - - /// - /// Создает новый экземпляр парсера, на основе текстового потока. - /// - /// Текстовый поток. - /// Признак того, что парсер должен конролировать время жизни входного потока. - public JSONParser(TextReader reader, bool dispose) - : base(_jsonDFA, INITIAL_STATE, new JSONParserContext { elementContext = JSONElementContext.None, memberName = String.Empty }) { - Safe.ArgumentNotNull(reader, "reader"); - m_scanner = new JSONScanner(); - m_scanner.Feed(reader, dispose); - } - - /// - /// Тип текущего элемента на котором стоит парсер. - /// - public JSONElementType ElementType { - get { return m_elementType; } - } - - /// - /// Имя элемента - имя свойства родительского контейнера. Для элементов массивов и корневого всегда - /// пустая строка. - /// - public string ElementName { - get { return m_context.info.memberName; } - } - - /// - /// Значение элемента. Только для элементов типа , для остальных null - /// - public object ElementValue { - get { return m_elementValue; } - } - - /// - /// Читает слеюудущий объект из потока - /// - /// true - операция чтения прошла успешно, false - конец данных - public bool Read() { - if (m_context.current == UNREACHEBLE_STATE) - throw new InvalidOperationException("The parser is in invalid state"); - object tokenValue; - JsonTokenType tokenType; - m_context.info.memberName = String.Empty; - while (m_scanner.ReadToken(out tokenValue, out tokenType)) { - Move((int)tokenType); - if (m_context.current == UNREACHEBLE_STATE) - UnexpectedToken(tokenValue, tokenType); - switch (tokenType) { - case JsonTokenType.BeginObject: - Switch( - _objectDFA, - INITIAL_STATE, - new JSONParserContext { - memberName = m_context.info.memberName, - elementContext = JSONElementContext.Object - } - ); - m_elementValue = null; - m_memberContext = MemberContext.MemberName; - m_elementType = JSONElementType.BeginObject; - return true; - case JsonTokenType.EndObject: - Restore(); - m_elementValue = null; - m_elementType = JSONElementType.EndObject; - return true; - case JsonTokenType.BeginArray: - Switch( - _arrayDFA, - INITIAL_STATE, - new JSONParserContext { - memberName = m_context.info.memberName, - elementContext = JSONElementContext.Array - } - ); - m_elementValue = null; - m_memberContext = MemberContext.MemberValue; - m_elementType = JSONElementType.BeginArray; - return true; - case JsonTokenType.EndArray: - Restore(); - m_elementValue = null; - m_elementType = JSONElementType.EndArray; - return true; - case JsonTokenType.String: - if (m_memberContext == MemberContext.MemberName) { - m_context.info.memberName = (string)tokenValue; - break; - } - m_elementType = JSONElementType.Value; - m_elementValue = tokenValue; - return true; - case JsonTokenType.Number: - m_elementType = JSONElementType.Value; - m_elementValue = tokenValue; - return true; - case JsonTokenType.Literal: - m_elementType = JSONElementType.Value; - m_elementValue = ParseLiteral((string)tokenValue); - return true; - case JsonTokenType.NameSeparator: - m_memberContext = MemberContext.MemberValue; - break; - case JsonTokenType.ValueSeparator: - m_memberContext = m_context.info.elementContext == JSONElementContext.Object ? MemberContext.MemberName : MemberContext.MemberValue; - break; - default: - UnexpectedToken(tokenValue, tokenType); - break; - } - } - if (m_context.info.elementContext != JSONElementContext.None) - throw new ParserException("Unexpedted end of data"); - return false; - } - - object ParseLiteral(string literal) { - switch (literal) { - case "null": - return null; - case "false": - return false; - case "true": - return true; - default: - UnexpectedToken(literal, JsonTokenType.Literal); - return null; // avoid compliler error - } - } - - void UnexpectedToken(object value, JsonTokenType tokenType) { - throw new ParserException(String.Format("Unexpected token {0}: '{1}'", tokenType, value)); - } - - - /// - /// Признак конца потока - /// - public bool EOF { - get { - return m_scanner.EOF; - } - } - - protected virtual void Dispose(bool disposing) { - if (disposing) { - m_scanner.Dispose(); - } - } - - /// - /// Освобождает парсер и связанный с ним сканнер. - /// - public void Dispose() { - Dispose(true); - GC.SuppressFinalize(this); - } - - ~JSONParser() { - Dispose(false); - } - - /// - /// Переходит в конец текущего объекта. - /// - public void SeekElementEnd() { - var level = Level - 1; - - Debug.Assert(level >= 0); - - while (Level != level) - Read(); - } - } - -} diff --git a/Implab/JSON/JSONScanner.cs b/Implab/JSON/JSONScanner.cs deleted file mode 100644 --- a/Implab/JSON/JSONScanner.cs +++ /dev/null @@ -1,100 +0,0 @@ -using Implab.Parsing; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Implab.JSON { - /// - /// Сканнер (лексер), разбивающий поток символов на токены JSON. - /// - public class JSONScanner : Scanner { - char[] m_stringBuffer; - DFAStateDescriptior[] m_stringDFA; - int[] m_stringAlphabet; - - /// - /// Создает новый экземпляр сканнера - /// - public JSONScanner() - : base(JSONGrammar.Instance.JsonDFA.States, JSONGrammar.Instance.JsonDFA.Alphabet.GetTranslationMap()) { - m_stringBuffer = new char[1024]; - var dfa = JSONGrammar.Instance.JsonStringDFA; - m_stringAlphabet = dfa.Alphabet.GetTranslationMap(); - m_stringDFA = dfa.States; - } - - /// - /// Читает следующий лексический элемент из входных данных. - /// - /// Возвращает значение прочитанного токена. - /// Возвращает тип прочитанного токена. - /// true - чтение произведено успешно. false - достигнут конец входных данных - /// В случе если токен не распознается, возникает исключение. Значения токенов обрабатываются, т.е. - /// в строках обрабатываются экранированные символы, числа становтся типа double. - public bool ReadToken(out object tokenValue, out JsonTokenType tokenType) { - if (ReadTokenInternal()) { - switch ((JSONGrammar.TokenType)m_currentState.tag[0]) { - case JSONGrammar.TokenType.StringBound: - tokenValue = ReadString(); - tokenType = JsonTokenType.String; - break; - case JSONGrammar.TokenType.Number: - tokenValue = Double.Parse(new String(m_buffer, m_tokenOffset, m_tokenLen), CultureInfo.InvariantCulture); - tokenType = JsonTokenType.Number; - break; - default: - tokenType = (JsonTokenType)m_currentState.tag[0]; - tokenValue = new String(m_buffer, m_tokenOffset, m_tokenLen); - break; - } - return true; - } - tokenValue = null; - tokenType = JsonTokenType.None; - return false; - } - - string ReadString() { - int pos = 0; - Switch(m_stringDFA, m_stringAlphabet); - while (ReadTokenInternal()) { - switch ((JSONGrammar.TokenType)m_currentState.tag[0]) { - case JSONGrammar.TokenType.StringBound: - Restore(); - return new String(m_stringBuffer, 0, pos); - case JSONGrammar.TokenType.UnescapedChar: - EnsureStringBufferSize(pos + m_tokenLen); - Array.Copy(m_buffer, m_tokenOffset, m_stringBuffer, pos, m_tokenLen); - pos += m_tokenLen; - break; - case JSONGrammar.TokenType.EscapedUnicode: - EnsureStringBufferSize(pos + 1); - m_stringBuffer[pos] = StringTranslator.TranslateHexUnicode(m_buffer, m_tokenOffset + 2); - pos++; - break; - case JSONGrammar.TokenType.EscapedChar: - EnsureStringBufferSize(pos + 1); - m_stringBuffer[pos] = StringTranslator.TranslateEscapedChar(m_buffer[m_tokenOffset + 1]); - pos++; - break; - default: - break; - } - - } - - throw new ParserException("Unexpected end of data"); - } - - void EnsureStringBufferSize(int size) { - if (size > m_stringBuffer.Length) { - var newBuffer = new char[size]; - m_stringBuffer.CopyTo(newBuffer, 0); - m_stringBuffer = newBuffer; - } - } - } -} diff --git a/Implab/JSON/JSONWriter.cs b/Implab/JSON/JSONWriter.cs deleted file mode 100644 --- a/Implab/JSON/JSONWriter.cs +++ /dev/null @@ -1,319 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Globalization; -using System.Diagnostics; - -namespace Implab.JSON { - public class JSONWriter { - struct Context { - public bool needComma; - public JSONElementContext element; - } - Stack m_contextStack = new Stack(); - Context m_context; - - const int BUFFER_SIZE = 64; - - TextWriter m_writer; - readonly bool m_indent = true; - readonly int m_indentSize = 4; - readonly char[] m_buffer = new char[BUFFER_SIZE]; - int m_bufferPos; - - static readonly char [] _hex = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; - static readonly char [] _escapeBKS, - _escapeFWD, - _escapeCR, - _escapeNL, - _escapeTAB, - _escapeBSLASH, - _escapeQ; - - static JSONWriter() { - _escapeBKS = "\\b".ToCharArray(); - _escapeFWD = "\\f".ToCharArray(); - _escapeCR = "\\r".ToCharArray(); - _escapeNL = "\\n".ToCharArray(); - _escapeTAB = "\\t".ToCharArray(); - _escapeBSLASH = "\\\\".ToCharArray(); - _escapeQ = "\\\"".ToCharArray(); - } - - public JSONWriter(TextWriter writer) { - Safe.ArgumentNotNull(writer, "writer"); - m_writer = writer; - } - - public JSONWriter(TextWriter writer, bool indent) { - Safe.ArgumentNotNull(writer, "writer"); - - m_writer = writer; - m_indent = indent; - } - - void WriteIndent() { - if (m_indent) { - var indent = new char[m_contextStack.Count * m_indentSize + 1]; - indent[0] = '\n'; - for (int i = 1; i < indent.Length; i++) - indent[i] = ' '; - m_writer.Write(new String(indent)); - } else { - m_writer.Write(' '); - } - } - - void WriteMemberName(string name) { - Safe.ArgumentNotEmpty(name, "name"); - if (m_context.element != JSONElementContext.Object) - OperationNotApplicable("WriteMember"); - if (m_context.needComma) - m_writer.Write(","); - - WriteIndent(); - m_context.needComma = true; - Write(name); - m_writer.Write(" : "); - } - - public void WriteValue(string name, string value) { - WriteMemberName(name); - Write(value); - } - - public void WriteValue(string name, bool value) { - WriteMemberName(name); - Write(value); - } - - public void WriteValue(string name, double value) { - WriteMemberName(name); - Write(value); - } - - public void WriteValue(string value) { - if (m_context.element == JSONElementContext.Array) { - - if (m_context.needComma) - m_writer.Write(","); - WriteIndent(); - m_context.needComma = true; - - Write(value); - } else if (m_context.element == JSONElementContext.None) { - Write(value); - m_context.element = JSONElementContext.Closed; - } else { - OperationNotApplicable("WriteValue"); - } - } - - public void WriteValue(bool value) { - if (m_context.element == JSONElementContext.Array) { - - if (m_context.needComma) - m_writer.Write(","); - WriteIndent(); - m_context.needComma = true; - - Write(value); - } else if (m_context.element == JSONElementContext.None) { - Write(value); - m_context.element = JSONElementContext.Closed; - } else { - OperationNotApplicable("WriteValue"); - } - } - - public void WriteValue(double value) { - if (m_context.element == JSONElementContext.Array) { - - if (m_context.needComma) - m_writer.Write(","); - WriteIndent(); - m_context.needComma = true; - - Write(value); - } else if (m_context.element == JSONElementContext.None) { - Write(value); - m_context.element = JSONElementContext.Closed; - } else { - OperationNotApplicable("WriteValue"); - } - } - - public void BeginObject() { - if (m_context.element != JSONElementContext.None && m_context.element != JSONElementContext.Array) - OperationNotApplicable("BeginObject"); - if (m_context.needComma) - m_writer.Write(","); - - WriteIndent(); - - m_context.needComma = true; - - m_contextStack.Push(m_context); - - m_context = new Context { element = JSONElementContext.Object, needComma = false }; - m_writer.Write("{"); - } - - public void BeginObject(string name) { - WriteMemberName(name); - - m_contextStack.Push(m_context); - - m_context = new Context { element = JSONElementContext.Object, needComma = false }; - m_writer.Write("{"); - } - - public void EndObject() { - if (m_context.element != JSONElementContext.Object) - OperationNotApplicable("EndObject"); - - m_context = m_contextStack.Pop(); - if (m_contextStack.Count == 0) - m_context.element = JSONElementContext.Closed; - WriteIndent(); - m_writer.Write("}"); - } - - public void BeginArray() { - if (m_context.element != JSONElementContext.None && m_context.element != JSONElementContext.Array) - throw new InvalidOperationException(); - if (m_context.needComma) { - m_writer.Write(","); - - } - m_context.needComma = true; - - WriteIndent(); - m_contextStack.Push(m_context); - m_context = new Context { element = JSONElementContext.Array, needComma = false }; - m_writer.Write("["); - } - - public void BeginArray(string name) { - WriteMemberName(name); - - m_contextStack.Push(m_context); - - m_context = new Context { element = JSONElementContext.Array, needComma = false }; - m_writer.Write("["); - } - - public void EndArray() { - if (m_context.element != JSONElementContext.Array) - OperationNotApplicable("EndArray"); - - m_context = m_contextStack.Pop(); - if (m_contextStack.Count == 0) - m_context.element = JSONElementContext.Closed; - WriteIndent(); - m_writer.Write("]"); - } - - void Write(bool value) { - m_writer.Write(value ? "true" : "false"); - } - - void FlushBuffer() { - if (m_bufferPos > 0) { - m_writer.Write(m_buffer, 0, m_bufferPos); - m_bufferPos = 0; - } - } - - void Write(string value) { - if (value == null) { - m_writer.Write("null"); - return; - } - - Debug.Assert(m_bufferPos == 0); - - var chars = value.ToCharArray(); - m_buffer[m_bufferPos++] = '"'; - - // Analysis disable once ForCanBeConvertedToForeach - for (int i = 0; i < chars.Length; i++) { - var ch = chars[i]; - - char[] escapeSeq; - - switch (ch) { - case '\b': - escapeSeq = _escapeBKS; - break; - case '\f': - escapeSeq = _escapeFWD; - break; - case '\r': - escapeSeq = _escapeCR; - break; - case '\n': - escapeSeq = _escapeNL; - break; - case '\t': - escapeSeq = _escapeTAB; - break; - case '\\': - escapeSeq = _escapeBSLASH; - break; - case '"': - escapeSeq = _escapeQ; - break; - default: - if (ch < 0x20) { - if (m_bufferPos + 6 > BUFFER_SIZE) - FlushBuffer(); - - m_buffer[m_bufferPos++] = '\\'; - m_buffer[m_bufferPos++] = 'u'; - m_buffer[m_bufferPos++] = '0'; - m_buffer[m_bufferPos++] = '0'; - m_buffer[m_bufferPos++] = _hex[ch >> 4 & 0xf]; - m_buffer[m_bufferPos++] = _hex[ch & 0xf]; - - } else { - if (m_bufferPos >= BUFFER_SIZE) - FlushBuffer(); - m_buffer[m_bufferPos++] = ch; - } - continue; - } - - if (m_bufferPos + escapeSeq.Length > BUFFER_SIZE) - FlushBuffer(); - - Array.Copy(escapeSeq, 0, m_buffer, m_bufferPos, escapeSeq.Length); - m_bufferPos += escapeSeq.Length; - - } - - if (m_bufferPos >= BUFFER_SIZE) - FlushBuffer(); - - m_buffer[m_bufferPos++] = '"'; - - FlushBuffer(); - } - - void Write(double value) { - if (double.IsNaN(value)) - Write("NaN"); - else if (double.IsNegativeInfinity(value)) - Write("-Infinity"); - else if (double.IsPositiveInfinity(value)) - Write("Infinity"); - else - m_writer.Write(value.ToString(CultureInfo.InvariantCulture)); - } - - void OperationNotApplicable(string opName) { - throw new InvalidOperationException(String.Format("The operation '{0}' isn't applicable in the context of '{1}'", opName, m_context.element )); - } - - } -} diff --git a/Implab/JSON/JSONXmlReader.cs b/Implab/JSON/JSONXmlReader.cs deleted file mode 100644 --- a/Implab/JSON/JSONXmlReader.cs +++ /dev/null @@ -1,343 +0,0 @@ -using Implab; -using Implab.Parsing; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml; - -namespace Implab.JSON { - public class JSONXmlReader : XmlReader { - - enum ValueContext { - Undefined, - ElementStart, - ElementValue, - ElementEnd, - ElementEmpty - } - - struct LocalNameContext { - public string localName; - public bool isArray; - } - - JSONParser m_parser; - ValueContext m_valueContext; - ReadState m_state = ReadState.Initial; - Stack m_localNameStack = new Stack(); - LocalNameContext m_localName; - int m_depthCorrection = 0; - - readonly string m_rootName; - readonly string m_prefix; - readonly string m_namespaceUri; - readonly bool m_flattenArrays; - readonly string m_arrayItemName; - readonly XmlNameTable m_nameTable; - - JSONXmlReader(JSONParser parser, JSONXmlReaderOptions options) { - m_parser = parser; - - if (options != null) { - m_prefix = options.NodesPrefix ?? String.Empty; - m_namespaceUri = options.NamespaceURI ?? String.Empty; - m_rootName = options.RootName ?? "json"; - m_flattenArrays = options.FlattenArrays; - m_arrayItemName = options.ArrayItemName ?? "item"; - m_nameTable = options.NameTable ?? new NameTable(); - } else { - m_prefix = String.Empty; - m_namespaceUri = String.Empty; - m_rootName = "json"; - m_flattenArrays = false; - m_arrayItemName = "item"; - m_nameTable = new NameTable(); - } - } - - /// - /// Always 0, JSON doesn't support attributes - /// - public override int AttributeCount { - get { return 0; } - } - - public override string BaseURI { - get { return String.Empty; } - } - - public override int Depth { - get { - return m_localNameStack.Count + m_depthCorrection; - } - } - - public override bool EOF { - get { return m_parser.EOF; } - } - - /// - /// Always throws an exception - /// - /// - /// - public override string GetAttribute(int i) { - throw new ArgumentOutOfRangeException(); - } - - /// - /// Always returns empty string - /// - /// - /// - /// - public override string GetAttribute(string name, string namespaceURI) { - return String.Empty; - } - - /// - /// Always returns empty string - /// - /// - /// - public override string GetAttribute(string name) { - return String.Empty; - } - - public override bool IsEmptyElement { - get { return m_parser.ElementType == JSONElementType.Value && m_valueContext == ValueContext.ElementEmpty; } - } - - public override string LocalName { - get { return m_localName.localName; } - } - - public override string LookupNamespace(string prefix) { - if (String.IsNullOrEmpty(prefix) || prefix == m_prefix) - return m_namespaceUri; - else - return String.Empty; - } - - public override bool MoveToAttribute(string name, string ns) { - return false; - } - - public override bool MoveToAttribute(string name) { - return false; - } - - public override bool MoveToElement() { - return false; - } - - public override bool MoveToFirstAttribute() { - return false; - } - - public override bool MoveToNextAttribute() { - return false; - } - - public override XmlNameTable NameTable { - get { return m_nameTable; } - } - - public override string NamespaceURI { - get { return m_namespaceUri; } - } - - public override XmlNodeType NodeType { - get { - switch (m_parser.ElementType) { - case JSONElementType.BeginObject: - case JSONElementType.BeginArray: - return XmlNodeType.Element; - case JSONElementType.EndObject: - case JSONElementType.EndArray: - return XmlNodeType.EndElement; - case JSONElementType.Value: - switch (m_valueContext) { - case ValueContext.ElementStart: - case ValueContext.ElementEmpty: - return XmlNodeType.Element; - case ValueContext.ElementValue: - return XmlNodeType.Text; - case ValueContext.ElementEnd: - return XmlNodeType.EndElement; - default: - throw new InvalidOperationException(); - } - default: - throw new InvalidOperationException(); - } - } - } - - public override string Prefix { - get { return m_prefix; } - } - - public override bool Read() { - if (m_state != System.Xml.ReadState.Interactive && m_state != System.Xml.ReadState.Initial) - return false; - - if (m_state == ReadState.Initial) - m_state = System.Xml.ReadState.Interactive; - - try { - switch (m_parser.ElementType) { - case JSONElementType.Value: - switch (m_valueContext) { - case ValueContext.ElementStart: - SetLocalName(String.Empty); - m_valueContext = ValueContext.ElementValue; - return true; - case ValueContext.ElementValue: - RestoreLocalName(); - m_valueContext = ValueContext.ElementEnd; - return true; - case ValueContext.ElementEmpty: - case ValueContext.ElementEnd: - RestoreLocalName(); - break; - } - break; - case JSONElementType.EndArray: - case JSONElementType.EndObject: - RestoreLocalName(); - break; - } - string itemName = m_parser.ElementType == JSONElementType.None ? m_rootName : m_flattenArrays ? m_localName.localName : m_arrayItemName; - while (m_parser.Read()) { - if (!String.IsNullOrEmpty(m_parser.ElementName)) - itemName = m_parser.ElementName; - - switch (m_parser.ElementType) { - case JSONElementType.BeginArray: - if (m_flattenArrays && !m_localName.isArray) { - m_depthCorrection--; - SetLocalName(itemName, true); - continue; - } else { - SetLocalName(itemName, true); - } - break; - case JSONElementType.BeginObject: - SetLocalName(itemName); - break; - case JSONElementType.EndArray: - if (m_flattenArrays && !m_localNameStack.Peek().isArray) { - RestoreLocalName(); - m_depthCorrection++; - continue; - } - break; - case JSONElementType.EndObject: - break; - case JSONElementType.Value: - SetLocalName(itemName); - m_valueContext = m_parser.ElementValue == null ? ValueContext.ElementEmpty : ValueContext.ElementStart; - break; - default: - break; - } - return true; - } - - m_state = System.Xml.ReadState.EndOfFile; - return false; - } catch { - m_state = System.Xml.ReadState.Error; - throw; - } - } - - public override bool ReadAttributeValue() { - return false; - } - - public override ReadState ReadState { - get { return m_state; } - } - - public override void ResolveEntity() { - // do nothing - } - - public override string Value { - get { - if (m_parser.ElementValue == null) - return String.Empty; - if (Convert.GetTypeCode(m_parser.ElementValue) == TypeCode.Double) - return ((double)m_parser.ElementValue).ToString(CultureInfo.InvariantCulture); - else - return m_parser.ElementValue.ToString(); - } - } - - void SetLocalName(string name) { - m_localNameStack.Push(m_localName); - m_localName.localName = name; - m_localName.isArray = false; - } - - void SetLocalName(string name, bool isArray) { - m_localNameStack.Push(m_localName); - m_localName.localName = name; - m_localName.isArray = isArray; - } - - void RestoreLocalName() { - m_localName = m_localNameStack.Pop(); - } - - public override void Close() { - - } - - protected override void Dispose(bool disposing) { - #if MONO - disposing = true; - #endif - if (disposing) { - m_parser.Dispose(); - } - base.Dispose(disposing); - } - - public static JSONXmlReader Create(string file, JSONXmlReaderOptions options) { - return Create(File.OpenText(file), options); - } - - /// - /// Creates the XmlReader for the specified text stream with JSON data. - /// - /// Text reader. - /// Options. - /// - /// The reader will be disposed when the XmlReader is disposed. - /// - public static JSONXmlReader Create(TextReader reader, JSONXmlReaderOptions options) { - return new JSONXmlReader(new JSONParser(reader, true), options); - } - - /// - /// Creates the XmlReader for the specified stream with JSON data. - /// - /// Stream. - /// Options. - /// - /// The stream will be disposed when the XmlReader is disposed. - /// - public static JSONXmlReader Create(Stream stream, JSONXmlReaderOptions options) { - Safe.ArgumentNotNull(stream, "stream"); - // HACK don't dispose StreaReader to keep stream opened - return Create(new StreamReader(stream), options); - } - } -} diff --git a/Implab/JSON/JSONXmlReaderOptions.cs b/Implab/JSON/JSONXmlReaderOptions.cs deleted file mode 100644 --- a/Implab/JSON/JSONXmlReaderOptions.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Xml; - -namespace Implab.JSON { - /// - /// Набор необязательных параметров для , позволяющий управлять процессом - /// интерпретации JSON документа. - /// - public class JSONXmlReaderOptions { - /// - /// Пространство имен в котором будут располагаться читаемые элементы документа - /// - public string NamespaceURI { - get; - set; - } - - /// - /// Интерпретировать массивы как множественные элементы (убирает один уровень вложенности), иначе массив - /// представляется в виде узла, дочерними элементами которого являются элементы массива, имена дочерних элементов - /// определяются свойством . По умолчанию false. - /// - public bool FlattenArrays { - get; - set; - } - - /// - /// Префикс, для узлов документа - /// - public string NodesPrefix { - get; - set; - } - - /// - /// Имя корневого элемента в xml документе - /// - public string RootName { - get; - set; - } - - /// - /// Имя элемента для массивов, если не включена опция . - /// По умолчанию item. - /// - public string ArrayItemName { - get; - set; - } - - /// - /// Таблица атомизированных строк для построения документа. - /// - public XmlNameTable NameTable { - get; - set; - } - - } -} diff --git a/Implab/JSON/JsonTokenType.cs b/Implab/JSON/JsonTokenType.cs deleted file mode 100644 --- a/Implab/JSON/JsonTokenType.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Implab.JSON { - /// - /// Тип токенов, возвращаемых . - /// - public enum JsonTokenType : int { - None = 0, - /// - /// Начало объекта - /// - BeginObject, - /// - /// Конец объекта - /// - EndObject, - /// - /// Начало массива - /// - BeginArray, - /// - /// Конец массива - /// - EndArray, - /// - /// Строка - /// - String, - /// - /// Число - /// - Number, - /// - /// Литерал - /// - Literal, - /// - /// Разделитель имени : - /// - NameSeparator, - /// - /// Разделитель имени , - /// - ValueSeparator - } -} diff --git a/Implab/JSON/StringTranslator.cs b/Implab/JSON/StringTranslator.cs deleted file mode 100644 --- a/Implab/JSON/StringTranslator.cs +++ /dev/null @@ -1,96 +0,0 @@ -using Implab; -using Implab.Parsing; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Implab.JSON { - /// - /// Класс для преобразования экранированной строки JSON - /// - public class StringTranslator : Scanner { - static readonly char[] _escMap; - static readonly int[] _hexMap; - - static StringTranslator() { - var chars = new char[] { 'b', 'f', 't', 'r', 'n', '\\', '/' }; - var vals = new char[] { '\b', '\f', '\t', '\r', '\n', '\\', '/' }; - - _escMap = new char[chars.Max() + 1]; - - for (int i = 0; i < chars.Length; i++) - _escMap[chars[i]] = vals[i]; - - var hexs = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F' }; - var ints = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 10, 11, 12, 13, 14, 15 }; - - _hexMap = new int[hexs.Max() + 1]; - - for (int i = 0; i < hexs.Length; i++) - _hexMap[hexs[i]] = ints[i]; - - } - - public StringTranslator() - : base(JSONGrammar.Instance.JsonStringDFA.States, JSONGrammar.Instance.JsonStringDFA.Alphabet.GetTranslationMap()) { - } - - public string Translate(string data) { - Safe.ArgumentNotNull(data, "data"); - return Translate(data.ToCharArray()); - } - - public string Translate(char[] data) { - Safe.ArgumentNotNull(data, "data"); - return Translate(data, data.Length); - } - - public string Translate(char[] data, int length) { - Safe.ArgumentNotNull(data, "data"); - Safe.ArgumentInRange(length, 0, data.Length, "length"); - - var translated = new char[length]; - - Feed(data,length); - - int pos = 0; - - while (ReadTokenInternal()) { - switch ((JSONGrammar.TokenType)TokenTags[0]) { - case JSONGrammar.TokenType.UnescapedChar: - Array.Copy(m_buffer,m_tokenOffset,translated,pos,m_tokenLen); - pos += m_tokenLen; - break; - case JSONGrammar.TokenType.EscapedChar: - translated[pos] = _escMap[m_buffer[m_tokenOffset + 1]]; - pos++; - break; - case JSONGrammar.TokenType.EscapedUnicode: - translated[pos] = TranslateHexUnicode(m_buffer,m_tokenOffset + 2); - pos++; - break; - } - } - - return new String(translated, 0, pos); - } - - internal static char TranslateEscapedChar(char symbol) { - return _escMap[symbol]; - } - - internal static char TranslateHexUnicode(char[] symbols, int offset) { - Debug.Assert(symbols != null); - Debug.Assert(symbols.Length - offset >= 4); - - int value = (_hexMap[symbols[offset]] << 12) - | (_hexMap[symbols[offset + 1]] << 8) - | (_hexMap[symbols[offset + 2]] << 4) - | (_hexMap[symbols[offset + 3]]); - return (char)value; - } - } -} diff --git a/Implab/Parsing/AltToken.cs b/Implab/Parsing/AltToken.cs deleted file mode 100644 --- a/Implab/Parsing/AltToken.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; - -namespace Implab.Parsing { - public class AltToken: BinaryToken { - public AltToken(Token left, Token right) - : base(left, right) { - } - - public override void Accept(IVisitor visitor) { - Safe.ArgumentNotNull(visitor, "visitor"); - visitor.Visit(this); - } - public override string ToString() { - return String.Format(Right is BinaryToken ? "{0}|({1})" : "{0}|{1}", Left, Right); - } - } -} diff --git a/Implab/Parsing/BinaryToken.cs b/Implab/Parsing/BinaryToken.cs deleted file mode 100644 --- a/Implab/Parsing/BinaryToken.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Implab; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Implab.Parsing { - public abstract class BinaryToken : Token { - Token m_left; - Token m_right; - - public Token Left { - get { return m_left; } - } - - public Token Right { - get { return m_right; } - } - - protected BinaryToken(Token left, Token right) { - Safe.ArgumentNotNull(m_left = left, "left"); - Safe.ArgumentNotNull(m_right = right, "right"); - } - } -} diff --git a/Implab/Parsing/CDFADefinition.cs b/Implab/Parsing/CDFADefinition.cs deleted file mode 100644 --- a/Implab/Parsing/CDFADefinition.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Implab.Parsing { - public class CDFADefinition : DFADefinition { - readonly CharAlphabet m_alphabet; - - public CharAlphabet Alphabet { - get { return m_alphabet; } - } - - public CDFADefinition(CharAlphabet alphabet): base(alphabet.Count) { - m_alphabet = alphabet; - } - - public CDFADefinition Optimize() { - - return (CDFADefinition)Optimize(alphabet => new CDFADefinition((CharAlphabet)alphabet), m_alphabet, new CharAlphabet()); - } - - public void PrintDFA() { - PrintDFA(m_alphabet); - } - } -} diff --git a/Implab/Parsing/CatToken.cs b/Implab/Parsing/CatToken.cs deleted file mode 100644 --- a/Implab/Parsing/CatToken.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; - -namespace Implab.Parsing { - public class CatToken : BinaryToken { - public CatToken(Token left, Token right) - : base(left, right) { - } - - public override void Accept(IVisitor visitor) { - Safe.ArgumentNotNull(visitor, "visitor"); - visitor.Visit(this); - } - - public override string ToString() { - return String.Format("{0}{1}", FormatToken(Left), FormatToken(Right)); - } - - string FormatToken(Token token) { - return String.Format(token is AltToken ? "({0})" : "{0}", token); - } - } -} diff --git a/Implab/Parsing/CharAlphabet.cs b/Implab/Parsing/CharAlphabet.cs deleted file mode 100644 --- a/Implab/Parsing/CharAlphabet.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Implab; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Implab.Parsing { - public class CharAlphabet: IndexedAlphabetBase { - - public CharAlphabet() - : base(char.MaxValue + 1) { - } - - public override int GetSymbolIndex(char symbol) { - return symbol; - } - - public override IEnumerable InputSymbols { - get { return Enumerable.Range(char.MinValue, char.MaxValue).Select(x => (char)x); } - } - } -} diff --git a/Implab/Parsing/DFABuilder.cs b/Implab/Parsing/DFABuilder.cs deleted file mode 100644 --- a/Implab/Parsing/DFABuilder.cs +++ /dev/null @@ -1,180 +0,0 @@ -using Implab; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; - -namespace Implab.Parsing { - /// - /// Используется для построения ДКА по регулярному выражению, сначала обходит - /// регулярное выражение и вычисляет followpos, затем используется метод - /// для построения автомата. - /// - public class DFABuilder : IVisitor { - int m_idx = 0; - Token m_root; - HashSet m_firstpos; - HashSet m_lastpos; - - Dictionary> m_followpos = new Dictionary>(); - Dictionary m_indexes = new Dictionary(); - Dictionary m_ends = new Dictionary(); - - public Dictionary> FollowposMap { - get { return m_followpos; } - } - - public HashSet Followpos(int pos) { - HashSet set; - if (m_followpos.TryGetValue(pos, out set)) - return set; - return m_followpos[pos] = new HashSet(); - } - - bool Nullable(object n) { - if (n is EmptyToken || n is StarToken) - return true; - if (n is AltToken) - return Nullable(((AltToken)n).Left) || Nullable(((AltToken)n).Right); - if (n is CatToken) - return Nullable(((CatToken)n).Left) && Nullable(((CatToken)n).Right); - return false; - } - - - public void Visit(AltToken token) { - if (m_root == null) - m_root = token; - var firtspos = new HashSet(); - var lastpos = new HashSet(); - - token.Left.Accept(this); - firtspos.UnionWith(m_firstpos); - lastpos.UnionWith(m_lastpos); - - token.Right.Accept(this); - firtspos.UnionWith(m_firstpos); - lastpos.UnionWith(m_lastpos); - - m_firstpos = firtspos; - m_lastpos = lastpos; - } - - public void Visit(StarToken token) { - if (m_root == null) - m_root = token; - token.Token.Accept(this); - - foreach (var i in m_lastpos) - Followpos(i).UnionWith(m_firstpos); - } - - public void Visit(CatToken token) { - if (m_root == null) - m_root = token; - - var firtspos = new HashSet(); - var lastpos = new HashSet(); - token.Left.Accept(this); - firtspos.UnionWith(m_firstpos); - var leftLastpos = m_lastpos; - - token.Right.Accept(this); - lastpos.UnionWith(m_lastpos); - var rightFirstpos = m_firstpos; - - if (Nullable(token.Left)) - firtspos.UnionWith(rightFirstpos); - - if (Nullable(token.Right)) - lastpos.UnionWith(leftLastpos); - - m_firstpos = firtspos; - m_lastpos = lastpos; - - foreach (var i in leftLastpos) - Followpos(i).UnionWith(rightFirstpos); - - } - - public void Visit(EmptyToken token) { - if (m_root == null) - m_root = token; - ; - } - - public void Visit(SymbolToken token) { - if (m_root == null) - m_root = token; - m_idx++; - m_indexes[m_idx] = token.Value; - m_firstpos = new HashSet(new[] { m_idx }); - m_lastpos = new HashSet(new[] { m_idx }); - } - - public void Visit(EndToken token) { - if (m_root == null) - m_root = token; - m_idx++; - m_indexes[m_idx] = IndexedAlphabetBase.UNCLASSIFIED; - m_firstpos = new HashSet(new[] { m_idx }); - m_lastpos = new HashSet(new[] { m_idx }); - Followpos(m_idx); - m_ends.Add(m_idx, token.Tag); - } - - public void BuildDFA(IDFADefinition dfa) { - Safe.ArgumentNotNull(dfa,"dfa"); - - var stateMap = new Dictionary, int>(new CustomEqualityComparer>( - (x, y) => x.SetEquals(y), - (x) => x.Sum(n => n.GetHashCode()) - )); - - stateMap[m_firstpos] = DefineState( dfa, m_firstpos); - Debug.Assert(stateMap[m_firstpos] == DFADefinition.INITIAL_STATE); - - var queue = new Queue>(); - - queue.Enqueue(m_firstpos); - - while (queue.Count > 0) { - var state = queue.Dequeue(); - var s1 = stateMap[state]; - - for (int a = 0; a < dfa.AlphabetSize; a++) { - var next = new HashSet(); - foreach (var p in state) { - if (m_indexes[p] == a) { - next.UnionWith(Followpos(p)); - } - } - if (next.Count > 0) { - int s2; - if (!stateMap.TryGetValue(next, out s2)) { - stateMap[next] = s2 = DefineState(dfa, next); - queue.Enqueue(next); - } - dfa.DefineTransition(s1, s2, a); - } - } - - } - } - - int[] GetStateTags(HashSet state) { - Debug.Assert(state != null); - return state.Where(m_ends.ContainsKey).Select(pos => m_ends[pos]).ToArray(); - } - - int DefineState(IDFADefinition automa, HashSet state) { - Debug.Assert(automa != null); - Debug.Assert(state != null); - - var tags = GetStateTags(state); - - return tags.Length > 0 ? automa.AddState(tags) : automa.AddState(); - } - - } -} diff --git a/Implab/Parsing/DFADefinition.cs b/Implab/Parsing/DFADefinition.cs deleted file mode 100644 --- a/Implab/Parsing/DFADefinition.cs +++ /dev/null @@ -1,285 +0,0 @@ -using Implab; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; - -namespace Implab.Parsing { - public class DFADefinition : IDFADefinition { - readonly List> m_states; - - public const int INITIAL_STATE = 1; - public const int UNREACHEBLE_STATE = 0; - - DFAStateDescriptior[] m_statesArray; - readonly int m_alpabetSize; - - public DFADefinition(int alphabetSize) { - m_states = new List>(); - m_alpabetSize = alphabetSize; - - m_states.Add(new DFAStateDescriptior()); - } - - public bool InitialStateIsFinal { - get { - return m_states[INITIAL_STATE].final; - } - } - - public int AddState() { - var index = m_states.Count; - m_states.Add(new DFAStateDescriptior { - final = false, - transitions = new int[AlphabetSize] - }); - m_statesArray = null; - - return index; - } - - public int AddState(TTag[] tag) { - var index = m_states.Count; - bool final = tag != null && tag.Length != 0; - m_states.Add(new DFAStateDescriptior { - final = final, - transitions = new int[AlphabetSize], - tag = final ? tag : null - }); - m_statesArray = null; - return index; - } - - public void DefineTransition(TState s1, TState s2, TInput symbol) { - int is1 = StateAlphabet.Translate(s1); - int is2 = StateAlphabet.Translate(s2); - int isym = InputAlphabet.Translate(symbol); - - Safe.ArgumentAssert(is1 != 0, "s1"); - Safe.ArgumentAssert(is2 != 0, "s2"); - Safe.ArgumentAssert(isym != 0, "symbol"); - - m_states[is1].transitions[isym] = is2; - } - - #region IDFADefinition implementation - - public DFAStateDescriptior[] GetTransitionTable() { - if (m_statesArray == null) - m_statesArray = m_states.ToArray(); - return m_statesArray; - } - - public IAlphabet InputAlphabet { - get { - throw new NotImplementedException(); - } - } - - public IAlphabet StateAlphabet { - get { - throw new NotImplementedException(); - } - } - - #endregion - - protected IDFADefinition<> Optimize(Func, IDFADefinition> dfaFactory,IAlphabet sourceAlphabet, IAlphabet minimalAlphabet) { - Safe.ArgumentNotNull(dfaFactory, "dfaFactory"); - Safe.ArgumentNotNull(minimalAlphabet, "minimalAlphabet"); - - var setComparer = new CustomEqualityComparer>( - (x, y) => x.SetEquals(y), - (s) => s.Sum(x => x.GetHashCode()) - ); - - var arrayComparer = new CustomEqualityComparer( - (x,y) => (new HashSet(x)).SetEquals(new HashSet(y)), - (a) => a.Sum(x => x.GetHashCode()) - ); - - var optimalStates = new HashSet>(setComparer); - var queue = new HashSet>(setComparer); - - foreach (var g in Enumerable - .Range(INITIAL_STATE, m_states.Count-1) - .Select(i => new { - index = i, - descriptor = m_states[i] - }) - .Where(x => x.descriptor.final) - .GroupBy(x => x.descriptor.tag, arrayComparer) - ) { - optimalStates.Add(new HashSet(g.Select(x => x.index))); - } - - var state = new HashSet( - Enumerable - .Range(INITIAL_STATE, m_states.Count - 1) - .Where(i => !m_states[i].final) - ); - optimalStates.Add(state); - queue.Add(state); - - while (queue.Count > 0) { - var stateA = queue.First(); - queue.Remove(stateA); - - for (int c = 0; c < AlphabetSize; c++) { - var stateX = new HashSet(); - - for(int s = 1; s < m_states.Count; s++) { - if (stateA.Contains(m_states[s].transitions[c])) - stateX.Add(s); - } - - foreach (var stateY in optimalStates.ToArray()) { - if (stateX.Overlaps(stateY) && !stateY.IsSubsetOf(stateX)) { - var stateR1 = new HashSet(stateY); - var stateR2 = new HashSet(stateY); - - stateR1.IntersectWith(stateX); - stateR2.ExceptWith(stateX); - - optimalStates.Remove(stateY); - optimalStates.Add(stateR1); - optimalStates.Add(stateR2); - - if (queue.Contains(stateY)) { - queue.Remove(stateY); - queue.Add(stateR1); - queue.Add(stateR2); - } else { - queue.Add(stateR1.Count <= stateR2.Count ? stateR1 : stateR2); - } - } - } - } - } - - // строим карты соотвествия оптимальных состояний с оригинальными - - var initialState = optimalStates.Single(x => x.Contains(INITIAL_STATE)); - - // карта получения оптимального состояния по соотвествующему ему простому состоянию - int[] reveseOptimalMap = new int[m_states.Count]; - // карта с индексами оптимальных состояний - HashSet[] optimalMap = new HashSet[optimalStates.Count + 1]; - { - optimalMap[0] = new HashSet(); // unreachable state - optimalMap[1] = initialState; // initial state - foreach (var ss in initialState) - reveseOptimalMap[ss] = 1; - - int i = 2; - foreach (var s in optimalStates) { - if (s.SetEquals(initialState)) - continue; - optimalMap[i] = s; - foreach (var ss in s) - reveseOptimalMap[ss] = i; - i++; - } - } - - // получаем минимальный алфавит - - var minClasses = new HashSet>(setComparer); - var alphaQueue = new Queue>(); - alphaQueue.Enqueue(new HashSet(Enumerable.Range(0,AlphabetSize))); - - for (int s = 1 ; s < optimalMap.Length; s++) { - var newQueue = new Queue>(); - - foreach (var A in alphaQueue) { - if (A.Count == 1) { - minClasses.Add(A); - continue; - } - - // различаем классы символов, которые переводят в различные оптимальные состояния - // optimalState -> alphaClass - var classes = new Dictionary>(); - - foreach (var term in A) { - // ищем все переходы класса по символу term - var s2 = reveseOptimalMap[ - optimalMap[s].Select(x => m_states[x].transitions[term]).FirstOrDefault(x => x != 0) // первое допустимое элементарное состояние, если есть - ]; - - HashSet A2; - if (!classes.TryGetValue(s2, out A2)) { - A2 = new HashSet(); - newQueue.Enqueue(A2); - classes[s2] = A2; - } - A2.Add(term); - } - } - - if (newQueue.Count == 0) - break; - alphaQueue = newQueue; - } - - foreach (var A in alphaQueue) - minClasses.Add(A); - - var alphabetMap = sourceAlphabet.Reclassify(minimalAlphabet, minClasses); - - // построение автомата - - var minimalDFA = dfaFactory(minimalAlphabet); - - var states = new int[ optimalMap.Length ]; - states[0] = UNREACHEBLE_STATE; - - for(var s = INITIAL_STATE; s < states.Length; s++) { - var tags = optimalMap[s].SelectMany(x => m_states[x].tag ?? Enumerable.Empty()).Distinct().ToArray(); - if (tags.Length > 0) - states[s] = minimalDFA.AddState(tags); - else - states[s] = minimalDFA.AddState(); - } - - Debug.Assert(states[INITIAL_STATE] == INITIAL_STATE); - - for (int s1 = 1; s1 < m_states.Count; s1++) { - for (int c = 0; c < AlphabetSize; c++) { - var s2 = m_states[s1].transitions[c]; - if (s2 != UNREACHEBLE_STATE) { - minimalDFA.DefineTransition( - reveseOptimalMap[s1], - reveseOptimalMap[s2], - alphabetMap[c] - ); - } - } - } - - return minimalDFA; - } - - public void PrintDFA(IAlphabet alphabet) { - - var reverseMap = alphabet.CreateReverseMap(); - - for (int i = 1; i < reverseMap.Length; i++) { - Console.WriteLine("C{0}: {1}", i, String.Join(",", reverseMap[i])); - } - - for (int i = 1; i < m_states.Count; i++) { - var s = m_states[i]; - for (int c = 0; c < AlphabetSize; c++) - if (s.transitions[c] != UNREACHEBLE_STATE) - Console.WriteLine("S{0} -{1}-> S{2}{3}", i, String.Join(",", reverseMap[c]), s.transitions[c], m_states[s.transitions[c]].final ? "$" : ""); - } - } - - public int AlphabetSize { - get { - return m_alpabetSize; - } - } - } -} diff --git a/Implab/Parsing/DFAStateDescriptor.cs b/Implab/Parsing/DFAStateDescriptor.cs deleted file mode 100644 --- a/Implab/Parsing/DFAStateDescriptor.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Implab.Parsing { - public struct DFAStateDescriptior { - public bool final; - public TTag[] tag; - public int[] transitions; - } -} diff --git a/Implab/Parsing/DFAutomaton.cs b/Implab/Parsing/DFAutomaton.cs deleted file mode 100644 --- a/Implab/Parsing/DFAutomaton.cs +++ /dev/null @@ -1,61 +0,0 @@ -using Implab; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Implab.Parsing { - public abstract class DFAutomaton { - protected struct ContextFrame { - public DFAStateDescriptior[] states; - public int current; - public T info; - } - - public const int INITIAL_STATE = DFADefinition.INITIAL_STATE; - public const int UNREACHEBLE_STATE = DFADefinition.UNREACHEBLE_STATE; - - protected ContextFrame m_context; - Stack m_contextStack = new Stack(); - - protected int Level { - get { return m_contextStack.Count; } - } - - protected DFAutomaton(DFAStateDescriptior[] states, int startState, T info) { - Safe.ArgumentNotNull(states, "states"); - Safe.ArgumentInRange(startState, 0, states.Length - 1, "startState"); - - m_context.states = states; - m_context.current = startState; - m_context.info = info; - } - - protected void Switch(DFAStateDescriptior[] states, int current, T info) { - Debug.Assert(states != null); - Debug.Assert(current >= 0 && current < states.Length); - m_contextStack.Push(m_context); - m_context.states = states; - m_context.current = current; - m_context.info = info; - } - - protected void Restore() { - Debug.Assert(m_contextStack.Count > 0); - - m_context = m_contextStack.Pop(); - } - - protected void Move(int input) { - Debug.Assert(input > 0 && input < m_context.states[m_context.current].transitions.Length); - m_context.current = m_context.states[m_context.current].transitions[input]; - } - - protected bool CanMove(int input) { - Debug.Assert(input > 0 && input < m_context.states[m_context.current].transitions.Length); - return m_context.states[m_context.current].transitions[input] != UNREACHEBLE_STATE; - } - } -} diff --git a/Implab/Parsing/EDFADefinition.cs b/Implab/Parsing/EDFADefinition.cs deleted file mode 100644 --- a/Implab/Parsing/EDFADefinition.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Implab; -using System; - -namespace Implab.Parsing { - public class EDFADefinition : DFADefinition where T : struct, IConvertible { - readonly EnumAlphabet m_alphabet; - - public EnumAlphabet Alphabet { - get { return m_alphabet; } - } - - public EDFADefinition(EnumAlphabet alphabet) : base(alphabet.Count) { - m_alphabet = alphabet; - } - - public void DefineTransition(int s1, int s2, T input) { - DefineTransition(s1, s2, m_alphabet.Translate(input)); - } - - public EDFADefinition Optimize() { - - return (EDFADefinition)Optimize(alphabet => new EDFADefinition((EnumAlphabet)alphabet), m_alphabet, new EnumAlphabet()); - } - - public void PrintDFA() { - PrintDFA(m_alphabet); - } - } -} diff --git a/Implab/Parsing/EmptyToken.cs b/Implab/Parsing/EmptyToken.cs deleted file mode 100644 --- a/Implab/Parsing/EmptyToken.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Implab; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Implab.Parsing { - public class EmptyToken : Token { - public override void Accept(IVisitor visitor) { - Safe.ArgumentNotNull(visitor, "visitor"); - visitor.Visit(this); - } - public override string ToString() { - return "$"; - } - } -} diff --git a/Implab/Parsing/EndToken.cs b/Implab/Parsing/EndToken.cs deleted file mode 100644 --- a/Implab/Parsing/EndToken.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Implab; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Implab.Parsing { - /// - /// Конечный символ расширенного регулярного выражения, при построении ДКА - /// используется для определения конечных состояний. - /// - public class EndToken: Token { - - int m_tag; - - public EndToken(int tag) { - m_tag = tag; - } - - public EndToken() - : this(0) { - } - - public int Tag { - get { return m_tag; } - } - - public override void Accept(IVisitor visitor) { - Safe.ArgumentNotNull(visitor, "visitor"); - visitor.Visit(this); - } - public override string ToString() { - return "#"; - } - } -} diff --git a/Implab/Parsing/EnumAlphabet.cs b/Implab/Parsing/EnumAlphabet.cs deleted file mode 100644 --- a/Implab/Parsing/EnumAlphabet.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.Linq; -using System.Diagnostics.CodeAnalysis; - -namespace Implab.Parsing { - /// - /// Алфавит символами которого являются элементы перечислений. - /// - /// Тип перечислений - public class EnumAlphabet : IndexedAlphabetBase where T : struct, IConvertible { - [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")] - static readonly T[] _symbols; - static readonly EnumAlphabet _fullAlphabet; - - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - static EnumAlphabet() { - if (!typeof(T).IsEnum) - throw new InvalidOperationException("Invalid generic parameter, enumeration is required"); - - if (Enum.GetUnderlyingType(typeof(T)) != typeof(Int32)) - throw new InvalidOperationException("Only enums based on Int32 are supported"); - - _symbols = ((T[])Enum.GetValues(typeof(T))) - .OrderBy(x => x.ToInt32(CultureInfo.InvariantCulture)) - .ToArray(); - - if ( - _symbols[_symbols.Length - 1].ToInt32(CultureInfo.InvariantCulture) >= _symbols.Length - || _symbols[0].ToInt32(CultureInfo.InvariantCulture) != 0 - ) - throw new InvalidOperationException("The specified enumeration must be zero-based and continuously numbered"); - - _fullAlphabet = new EnumAlphabet(_symbols.Select(x => x.ToInt32(CultureInfo.InvariantCulture)).ToArray()); - } - - - - public static EnumAlphabet FullAlphabet { - get { - return _fullAlphabet; - } - } - - - public EnumAlphabet() - : base(_symbols.Length) { - } - - public EnumAlphabet(int[] map) - : base(map) { - Debug.Assert(map.Length == _symbols.Length); - } - - - public override int GetSymbolIndex(T symbol) { - return symbol.ToInt32(CultureInfo.InvariantCulture); - } - - public override IEnumerable InputSymbols { - get { return _symbols; } - } - - } -} diff --git a/Implab/Parsing/Grammar.cs b/Implab/Parsing/Grammar.cs deleted file mode 100644 --- a/Implab/Parsing/Grammar.cs +++ /dev/null @@ -1,108 +0,0 @@ -using Implab; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Implab.Parsing { - /// - /// Базовый абстрактный класс. Грамматика, позволяет формулировать выражения над алфавитом типа char. - /// - /// - public abstract class Grammar where TGrammar: Grammar, new() { - static TGrammar _instance; - - public static TGrammar Instance{ - get { - if (_instance == null) - _instance = new TGrammar(); - return _instance; - } - } - - readonly CharAlphabet m_alphabet = new CharAlphabet(); - - public CharAlphabet Alphabet { - get { return m_alphabet; } - } - - public SymbolToken UnclassifiedToken() { - return new SymbolToken(CharAlphabet.UNCLASSIFIED); - } - - public void DefineAlphabet(IEnumerable alphabet) { - Safe.ArgumentNotNull(alphabet, "alphabet"); - - foreach (var ch in alphabet) - m_alphabet.DefineSymbol(ch); - } - public Token SymbolRangeToken(char start, char end) { - return SymbolToken(Enumerable.Range(start, end - start + 1).Select(x => (char)x)); - } - - public Token SymbolToken(char symbol) { - return Token.New(TranslateOrAdd(symbol)); - } - - public Token SymbolToken(IEnumerable symbols) { - Safe.ArgumentNotNull(symbols, "symbols"); - - return Token.New(TranslateOrAdd(symbols).ToArray()); - } - - public Token SymbolSetToken(params char[] set) { - return SymbolToken(set); - } - - int TranslateOrAdd(char ch) { - var t = m_alphabet.Translate(ch); - if (t == CharAlphabet.UNCLASSIFIED) - t = m_alphabet.DefineSymbol(ch); - return t; - } - - IEnumerable TranslateOrAdd(IEnumerable symbols) { - return symbols.Distinct().Select(TranslateOrAdd); - } - - int TranslateOrDie(char ch) { - var t = m_alphabet.Translate(ch); - if (t == CharAlphabet.UNCLASSIFIED) - throw new ApplicationException(String.Format("Symbol '{0}' is UNCLASSIFIED", ch)); - return t; - } - - IEnumerable TranslateOrDie(IEnumerable symbols) { - return symbols.Distinct().Select(TranslateOrDie); - } - - public Token SymbolTokenExcept(IEnumerable symbols) { - Safe.ArgumentNotNull(symbols, "symbols"); - - return Token.New( Enumerable.Range(0, m_alphabet.Count).Except(TranslateOrDie(symbols)).ToArray()); - } - - protected CDFADefinition BuildDFA(Token lang) { - Safe.ArgumentNotNull(lang, "lang"); - - var dfa = new CDFADefinition(m_alphabet); - - var builder = new DFABuilder(); - - lang.Accept( builder ); - - builder.BuildDFA(dfa); - if (dfa.InitialStateIsFinal) - throw new ApplicationException("The specified language contains empty token"); - - return dfa.Optimize(); - } - - - - //protected abstract TGrammar CreateInstance(); - } - - -} diff --git a/Implab/Parsing/IAlphabet.cs b/Implab/Parsing/IAlphabet.cs deleted file mode 100644 --- a/Implab/Parsing/IAlphabet.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Implab.Parsing { - /// - /// Алфавит. Множество символов, которые разбиты на классы, при этом классы имеют непрерывную нумерацию, - /// что позволяет использовать их в качестве индексов массивов. - /// - /// - /// Алфавит является сюрьективным отображением множества символов в множество индексов, это позволяет сократить размер таблицы переходов автомата - /// для входных символов, которые для него не различимы. - /// - /// Тип символов. - public interface IAlphabet { - /// - /// Количество классов символов в алфавите. - /// - int Count { get; } - - /// - /// Создает карту обратного сопоставления класса символов алфавита и сопоставленным - /// ему исходным символам. - /// - /// - List[] CreateReverseMap(); - - /// - /// Создает новый алфавит на основе текущего, горппируя его сиволы в более - /// крупные непересекающиеся классы символов. - /// - /// Новый, пустой алфавит, в котором быдут определены классы. - /// Множество классов символов текущего алфавита. - /// Карта для перехода классов текущего - /// алфавита к классам нового. - /// Ползволяет укрупнить алфавит, объединив классы в текущем алфавите. Используется при оптимизации автомата. - int[] Reclassify(IAlphabetBuilder newAlphabet, IEnumerable> classes); - - /// - /// Преобразует входной символ в индекс символа из алфавита. - /// - /// Исходный символ - /// Индекс в алфавите - int Translate(TSymbol symobl); - } -} diff --git a/Implab/Parsing/IAlphabetBuilder.cs b/Implab/Parsing/IAlphabetBuilder.cs deleted file mode 100644 --- a/Implab/Parsing/IAlphabetBuilder.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Implab.Parsing { - public interface IAlphabetBuilder : IAlphabet { - /// - /// Добавляет новый символ в алфавит, если символ уже был добавлен, то - /// возвращается ранее сопоставленный с символом класс. - /// - /// Символ для добавления. - /// Индекс класса, который попоставлен с символом. - int DefineSymbol(TSymbol symbol); - /// - /// Доабвляем класс символов. Множеству указанных исходных символов - /// будет сопоставлен символ в алфавите. - /// - /// Множестов исходных символов - /// Идентификатор символа алфавита. - int DefineClass(IEnumerable symbols); - - - - } -} - diff --git a/Implab/Parsing/IDFADefinition.cs b/Implab/Parsing/IDFADefinition.cs deleted file mode 100644 --- a/Implab/Parsing/IDFADefinition.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Implab.Parsing { - /// - /// Полностью описывает DFA автомат, его поведение, состояние и входные символы. - /// - /// - /// class MyAutomaton { - /// int m_current; - /// readonly DFAStateDescriptor[] m_automaton; - /// readonly IAlphabet m_commands; - /// - /// public MyAutomaton(IDFADefinition<MyCommands,MyStates,string> definition) { - /// m_current = definition.StateAlphabet.Translate(MyStates.Initial); - /// m_automaton = definition.GetTransitionTable(); - /// m_commands = definition.InputAlphabet; - /// } - /// - /// // defined a method which will move the automaton to the next state - /// public void Move(MyCommands cmd) { - /// // use transition map to determine the next state - /// var next = m_automaton[m_current].transitions[m_commands.Translate(cmd)]; - /// - /// // validate that we aren't in the unreachable state - /// if (next == DFAConst.UNREACHABLE_STATE) - /// throw new InvalidOperationException("The specified command is invalid"); - /// - /// // if everything is ok - /// m_current = next; - /// } - /// } - /// - public interface IDFADefinition { - /// - /// Алфавит входных символов - /// - /// The input alphabet. - IAlphabet InputAlphabet { - get; - } - - /// - /// Алфавит состояний автомата - /// - /// The state alphabet. - IAlphabet StateAlphabet { - get; - } - - /// - /// Таблица переходов состояний автомата - /// - /// The transition table. - DFAStateDescriptior[] GetTransitionTable(); - - } -} diff --git a/Implab/Parsing/IDFADefinitionBuilder.cs b/Implab/Parsing/IDFADefinitionBuilder.cs deleted file mode 100644 --- a/Implab/Parsing/IDFADefinitionBuilder.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace Implab.Parsing { - public interface IDFADefinitionBuilder : IDFADefinition { - - - } -} - diff --git a/Implab/Parsing/IVisitor.cs b/Implab/Parsing/IVisitor.cs deleted file mode 100644 --- a/Implab/Parsing/IVisitor.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Implab.Parsing { - /// - /// Интерфейс обходчика синтаксического дерева регулярного выражения - /// - public interface IVisitor { - void Visit(AltToken token); - void Visit(StarToken token); - void Visit(CatToken token); - void Visit(EmptyToken token); - void Visit(EndToken token); - void Visit(SymbolToken token); - } -} diff --git a/Implab/Parsing/IndexedAlphabetBase.cs b/Implab/Parsing/IndexedAlphabetBase.cs deleted file mode 100644 --- a/Implab/Parsing/IndexedAlphabetBase.cs +++ /dev/null @@ -1,107 +0,0 @@ -using Implab; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Implab.Parsing { - /// - /// Indexed alphabet is the finite set of symbols where each symbol has a zero-based unique index. - /// - public abstract class IndexedAlphabetBase : IAlphabet { - public const int UNCLASSIFIED = 0; - - int m_nextId = 1; - readonly int[] m_map; - - public int Count { - get { return m_nextId; } - } - - protected IndexedAlphabetBase(int mapSize) { - m_map = new int[mapSize]; - } - - protected IndexedAlphabetBase(int[] map) { - Debug.Assert(map != null); - - m_map = map; - m_nextId = map.Max() + 1; - } - - public int DefineSymbol(T symbol) { - var index = GetSymbolIndex(symbol); - if (m_map[index] == UNCLASSIFIED) - m_map[index] = m_nextId++; - return m_map[index]; - } - - public int DefineClass(IEnumerable symbols) { - Safe.ArgumentNotNull(symbols, "symbols"); - symbols = symbols.Distinct(); - - foreach (var symbol in symbols) { - var index = GetSymbolIndex(symbol); - if (m_map[index] == UNCLASSIFIED) - m_map[GetSymbolIndex(symbol)] = m_nextId; - else - throw new InvalidOperationException(String.Format("Symbol '{0}' already in use", symbol)); - } - return m_nextId++; - } - - public List[] CreateReverseMap() { - return - Enumerable.Range(UNCLASSIFIED, Count) - .Select( - i => InputSymbols - .Where(x => i != UNCLASSIFIED && m_map[GetSymbolIndex(x)] == i) - .ToList() - ) - .ToArray(); - } - - public int[] Reclassify(IAlphabet newAlphabet, IEnumerable> classes) { - Safe.ArgumentNotNull(newAlphabet, "newAlphabet"); - Safe.ArgumentNotNull(classes, "classes"); - var reverseMap = CreateReverseMap(); - - int[] translationMap = new int[Count]; - - foreach (var scl in classes) { - // skip if the supper class contains the unclassified element - if (scl.Contains(UNCLASSIFIED)) - continue; - var range = new List(); - foreach (var cl in scl) { - if (cl < 0 || cl >= reverseMap.Length) - throw new ArgumentOutOfRangeException(String.Format("Class {0} is not valid for the current alphabet", cl)); - range.AddRange(reverseMap[cl]); - } - var newClass = newAlphabet.DefineClass(range); - foreach (var cl in scl) - translationMap[cl] = newClass; - } - - return translationMap; - } - - public virtual int Translate(T symbol) { - return m_map[GetSymbolIndex(symbol)]; - } - - public abstract int GetSymbolIndex(T symbol); - - public abstract IEnumerable InputSymbols { get; } - - /// - /// Gets the translation map from the index of the symbol to it's class this is usefull for the optimized input symbols transtaion. - /// - /// The translation map. - public int[] GetTranslationMap() { - return m_map; - } - } -} diff --git a/Implab/Parsing/ParserException.cs b/Implab/Parsing/ParserException.cs deleted file mode 100644 --- a/Implab/Parsing/ParserException.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Implab.Parsing { - [Serializable] - public class ParserException : Exception { - public ParserException() { } - public ParserException(string message) : base(message) { } - public ParserException(string message, Exception inner) : base(message, inner) { } - protected ParserException( - System.Runtime.Serialization.SerializationInfo info, - System.Runtime.Serialization.StreamingContext context) - : base(info, context) { } - } -} diff --git a/Implab/Parsing/Scanner.cs b/Implab/Parsing/Scanner.cs deleted file mode 100644 --- a/Implab/Parsing/Scanner.cs +++ /dev/null @@ -1,259 +0,0 @@ -using Implab; -using System; -using System.Collections.Generic; -using System.IO; -using Implab.Components; - -namespace Implab.Parsing { - /// - /// Базовый класс для разбора потока входных символов на токены. - /// - /// - /// Сканнер имеет внутри буффер с симолами входного текста, по которому перемещаются два - /// указателя, начала и конца токена, при перемещении искользуется ДКА для определения - /// конца токена и допустимости текущего символа. - /// - public abstract class Scanner : Disposable { - struct ScannerConfig { - public DFAStateDescriptior[] states; - public int[] alphabetMap; - } - - Stack m_defs = new Stack(); - - DFAStateDescriptior[] m_states; - int[] m_alphabetMap; - - protected DFAStateDescriptior m_currentState; - int m_previewCode; - - protected int m_tokenLen = 0; - protected int m_tokenOffset; - - protected char[] m_buffer; - protected int m_bufferSize; - protected int m_pointer; - - TextReader m_reader; - bool m_disposeReader; - int m_chunkSize = 1024; // 1k - int m_limit = 10 * 1024 * 1024; // 10Mb - - protected Scanner(DFAStateDescriptior[] states, int[] alphabet) { - Safe.ArgumentNotEmpty(states, "states"); - Safe.ArgumentNotNull(alphabet, "alphabet"); - - m_states = states; - m_alphabetMap = alphabet; - - Feed(new char[0]); - } - - /// - /// Заполняет входными данными буффер. - /// - /// Данные для обработки. - /// Копирование данных не происходит, переданный массив используется в - /// качестве входного буффера. - public void Feed(char[] data) { - Safe.ArgumentNotNull(data, "data"); - - Feed(data, data.Length); - } - - /// - /// Заполняет буффур чтения входными данными. - /// - /// Данные для обработки. - /// Длина данных для обработки. - /// Копирование данных не происходит, переданный массив используется в - /// качестве входного буффера. - public void Feed(char[] data, int length) { - Safe.ArgumentNotNull(data, "data"); - Safe.ArgumentInRange(length, 0, data.Length, "length"); - AssertNotDisposed(); - - m_pointer = -1; - m_buffer = data; - m_bufferSize = length; - Shift(); - } - - public void Feed(TextReader reader, bool dispose) { - Safe.ArgumentNotNull(reader, "reader"); - AssertNotDisposed(); - - if (m_reader != null && m_disposeReader) - m_reader.Dispose(); - - m_reader = reader; - m_disposeReader = dispose; - m_pointer = -1; - m_buffer = new char[m_chunkSize]; - m_bufferSize = 0; - Shift(); - } - - /// - /// Получает текущий токен в виде строки. - /// - /// - protected string GetTokenValue() { - return new String(m_buffer, m_tokenOffset, m_tokenLen); - } - - /// - /// Метки текущего токена, которые были назначены в регулярном выражении. - /// - protected int[] TokenTags { - get { - return m_currentState.tag; - } - } - - /// - /// Признак конца данных - /// - public bool EOF { - get { - return m_pointer >= m_bufferSize; - } - } - - /// - /// Читает следующий токен, при этом указывает на начало токена, - /// на длину токена, - массив символов, в - /// котором находится токен. - /// - /// false - достигнут конец данных, токен не прочитан. - protected bool ReadTokenInternal() { - if (m_pointer >= m_bufferSize) - return false; - - m_currentState = m_states[DFADefinition.INITIAL_STATE]; - m_tokenLen = 0; - m_tokenOffset = m_pointer; - int nextState; - do { - nextState = m_currentState.transitions[m_previewCode]; - if (nextState == DFADefinition.UNREACHEBLE_STATE) { - if (m_currentState.final) - return true; - else - throw new ParserException( - String.Format( - "Unexpected symbol '{0}', at pos {1}", - m_buffer[m_pointer], - Position - ) - ); - } else { - m_currentState = m_states[nextState]; - m_tokenLen++; - } - - } while (Shift()); - - // END OF DATA - if (!m_currentState.final) - throw new ParserException("Unexpected end of data"); - - return true; - } - - - bool Shift() { - m_pointer++; - - if (m_pointer >= m_bufferSize) { - if (!ReadNextChunk()) - return false; - } - - m_previewCode = m_alphabetMap[m_buffer[m_pointer]]; - - return true; - } - - bool ReadNextChunk() { - if (m_reader == null) - return false; - - // extend buffer if nesessary - if (m_pointer + m_chunkSize > m_buffer.Length) { - // trim unused buffer head - var size = m_tokenLen + m_chunkSize; - if (size >= m_limit) - throw new ParserException(String.Format("Input buffer {0} bytes limit exceeded", m_limit)); - var temp = new char[size]; - Array.Copy(m_buffer, m_tokenOffset, temp, 0, m_tokenLen); - m_pointer -= m_tokenOffset; - m_bufferSize -= m_tokenOffset; - m_tokenOffset = 0; - m_buffer = temp; - } - - var read = m_reader.Read(m_buffer, m_tokenLen, m_chunkSize); - if (read == 0) - return false; - - m_bufferSize += read; - - return true; - } - - /// - /// Позиция сканнера во входном буфере - /// - public int Position { - get { - return m_pointer + 1; - } - } - - /// - /// Преключает внутренний ДКА на указанный, позволяет реализовать подобие захватывающей - /// группировки. - /// - /// Таблица состояний нового ДКА - /// Таблица входных символов для нового ДКА - protected void Switch(DFAStateDescriptior[] states, int[] alphabet) { - Safe.ArgumentNotNull(states, "dfa"); - - m_defs.Push(new ScannerConfig { - states = m_states, - alphabetMap = m_alphabetMap - }); - - m_states = states; - m_alphabetMap = alphabet; - - m_previewCode = m_alphabetMap[m_buffer[m_pointer]]; - } - - /// - /// Восстанавливает предыдущей ДКА сканнера. - /// - protected void Restore() { - if (m_defs.Count == 0) - throw new InvalidOperationException(); - var prev = m_defs.Pop(); - m_states = prev.states; - m_alphabetMap = prev.alphabetMap; - m_previewCode = m_alphabetMap[m_buffer[m_pointer]]; - } - - protected override void Dispose(bool disposing) { - if (disposing) { - if (m_reader != null && m_disposeReader) - m_reader.Dispose(); - m_buffer = null; - m_bufferSize = 0; - m_pointer = 0; - m_tokenLen = 0; - m_tokenOffset = 0; - } - base.Dispose(disposing); - } - } -} diff --git a/Implab/Parsing/StarToken.cs b/Implab/Parsing/StarToken.cs deleted file mode 100644 --- a/Implab/Parsing/StarToken.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Implab; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Implab.Parsing { - /// - /// Замыкание выражения с 0 и более повторов. - /// - public class StarToken: Token { - - Token m_token; - - public Token Token { - get { return m_token; } - } - - public StarToken(Token token) { - Safe.ArgumentNotNull(token, "token"); - m_token = token; - } - - public override void Accept(IVisitor visitor) { - Safe.ArgumentNotNull(visitor, "visitor"); - visitor.Visit(this); - } - - public override string ToString() { - return String.Format("({0})*", Token.ToString()); - } - } -} diff --git a/Implab/Parsing/SymbolToken.cs b/Implab/Parsing/SymbolToken.cs deleted file mode 100644 --- a/Implab/Parsing/SymbolToken.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Implab; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Implab.Parsing { - /// - /// Выражение, соответсвующее одному символу. - /// - public class SymbolToken : Token { - int m_value; - - public int Value { - get { return m_value; } - } - - public SymbolToken(int value) { - m_value = value; - } - public override void Accept(IVisitor visitor) { - Safe.ArgumentNotNull(visitor, "visitor"); - - visitor.Visit(this); - - } - - public override string ToString() { - return Value.ToString(); - } - } -} diff --git a/Implab/Parsing/Token.cs b/Implab/Parsing/Token.cs deleted file mode 100644 --- a/Implab/Parsing/Token.cs +++ /dev/null @@ -1,67 +0,0 @@ -using Implab; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Implab.Parsing { - public abstract class Token { - public abstract void Accept(IVisitor visitor); - - public Token Extend() { - return new CatToken(this, new EndToken()); - } - - public Token Tag(T tag) where T : IConvertible { - return new CatToken(this, new EndToken(tag.ToInt32(CultureInfo.InvariantCulture))); - } - - public Token Cat(Token right) { - return new CatToken(this, right); - } - - public Token Or(Token right) { - return new AltToken(this, right); - } - - public Token Optional() { - return Or(new EmptyToken()); - } - - public Token EClosure() { - return new StarToken(this); - } - - public Token Closure() { - return new CatToken(this, new StarToken(this)); - } - - public Token Repeat(int count) { - Token token = null; - - for (int i = 0; i < count; i++) - token = token != null ? token.Cat(this) : this; - return token ?? new EmptyToken(); - } - - public Token Repeat(int min, int max) { - if (min > max || min < 1) - throw new ArgumentOutOfRangeException(); - var token = Repeat(min); - - for (int i = min; i < max; i++) - token = token.Cat( this.Optional() ); - return token; - } - - public static Token New(params T[] set) where T : struct, IConvertible { - Safe.ArgumentNotNull(set, "set"); - Token token = null; - foreach(var c in set.Distinct()) - token = token == null ? new SymbolToken(c.ToInt32(CultureInfo.InvariantCulture)) : token.Or(new SymbolToken(c.ToInt32(CultureInfo.InvariantCulture))); - return token; - } - } -} diff --git a/Implab/PromiseExtensions.cs b/Implab/PromiseExtensions.cs --- a/Implab/PromiseExtensions.cs +++ b/Implab/PromiseExtensions.cs @@ -3,11 +3,6 @@ using System; using Implab.Diagnostics; using System.Collections.Generic; - -#if NET_4_5 -using System.Threading.Tasks; -#endif - namespace Implab { public static class PromiseExtensions { public static IPromise DispatchToCurrentContext(this IPromise that) { @@ -17,12 +12,12 @@ namespace Implab { return that; var p = new SyncContextPromise(context); - p.On(that.Cancel, PromiseEventType.Cancelled); + p.CancellationRequested(that.Cancel); that.On( p.Resolve, p.Reject, - p.Cancel + p.CancelOperation ); return p; } @@ -32,13 +27,12 @@ namespace Implab { Safe.ArgumentNotNull(context, "context"); var p = new SyncContextPromise(context); - p.On(that.Cancel, PromiseEventType.Cancelled); - + p.CancellationRequested(that.Cancel); that.On( p.Resolve, p.Reject, - p.Cancel + p.CancelOperation ); return p; } @@ -77,8 +71,8 @@ namespace Implab { }; } - static void CancelCallback(object cookie) { - ((ICancellable)cookie).Cancel(); + static void CancelByTimeoutCallback(object cookie) { + ((ICancellable)cookie).Cancel(new TimeoutException()); } /// @@ -89,7 +83,7 @@ namespace Implab { /// The 1st type parameter. public static TPromise Timeout(this TPromise that, int milliseconds) where TPromise : IPromise { Safe.ArgumentNotNull(that, "that"); - var timer = new Timer(CancelCallback, that, milliseconds, -1); + var timer = new Timer(CancelByTimeoutCallback, that, milliseconds, -1); that.On(timer.Dispose, PromiseEventType.All); return that; } @@ -180,8 +174,7 @@ namespace Implab { var d = new ActionTask(success, error, cancel, false); that.On(d.Resolve, d.Reject, d.CancelOperation); - if (success != null) - d.CancellationRequested(that.Cancel); + d.CancellationRequested(that.Cancel); return d; } @@ -198,8 +191,7 @@ namespace Implab { var d = new FuncTask(success, error, cancel, false); that.On(d.Resolve, d.Reject, d.CancelOperation); - if (success != null) - d.CancellationRequested(that.Cancel); + d.CancellationRequested(that.Cancel); return d; } @@ -215,8 +207,7 @@ namespace Implab { Safe.ArgumentNotNull(that, "that"); var d = new FuncTask(success, error, cancel, false); that.On(d.Resolve, d.Reject, d.CancelOperation); - if (success != null) - d.CancellationRequested(that.Cancel); + d.CancellationRequested(that.Cancel); return d; } @@ -234,8 +225,7 @@ namespace Implab { var d = new ActionChainTask(success, error, cancel, false); that.On(d.Resolve, d.Reject, d.CancelOperation); - if (success != null) - d.CancellationRequested(that.Cancel); + d.CancellationRequested(that.Cancel); return d; } diff --git a/Implab/Safe.cs b/Implab/Safe.cs --- a/Implab/Safe.cs +++ b/Implab/Safe.cs @@ -41,6 +41,11 @@ namespace Implab throw new ArgumentOutOfRangeException(paramName); } + public static void ArgumentOfType(object value, Type type, string paramName) { + if (!type.IsInstanceOfType(value)) + throw new ArgumentException(String.Format("The parameter must be of type {0}", type), paramName); + } + public static void Dispose(params IDisposable[] objects) { foreach (var d in objects) if (d != null) diff --git a/MonoPlay/MonoPlay.csproj b/MonoPlay/MonoPlay.csproj --- a/MonoPlay/MonoPlay.csproj +++ b/MonoPlay/MonoPlay.csproj @@ -32,6 +32,9 @@ + + ..\packages\System.Text.Json.2.0.0.11\lib\net40\System.Text.Json.dll + @@ -44,4 +47,7 @@ Implab + + + \ No newline at end of file diff --git a/MonoPlay/Program.cs b/MonoPlay/Program.cs --- a/MonoPlay/Program.cs +++ b/MonoPlay/Program.cs @@ -1,6 +1,9 @@ using System; using Implab; using System.Threading.Tasks; +using Implab.Formats.JSON; +using System.IO; +using System.Text.Json; namespace MonoPlay { class MainClass { @@ -9,28 +12,33 @@ namespace MonoPlay { public static void Main(string[] args) { if (args == null) throw new ArgumentNullException("args"); - - var t1 = Environment.TickCount; - - DoWork().GetAwaiter().GetResult(); + int t1, t2; - var t2 = Environment.TickCount; - Console.WriteLine("done: {0} ms, {1:.00} Mb, {2} GC", t2 - t1, GC.GetTotalMemory(false) / (1024*1024), GC.CollectionCount(0) ); + for (int i = 0; i < 2; i++) { + t1 = Environment.TickCount; + int elements =0; + using (var reader = new JSONParser(File.OpenText("/home/sergey/temp/citylots.json"))) { + while (reader.Read()) + elements++; + } - } + t2 = Environment.TickCount; + Console.WriteLine("attempt {0} done: {1} ms, {2:.00} Mb, {3} GC, Elements: {4}",i+1, t2 - t1, GC.GetTotalMemory(false) / (1024*1024), GC.CollectionCount(0), elements ); + } - static IPromise DoItem(int x) { - //return Promise.FromResult(x + 1); - var p = new Promise(); - p.Resolve(x+1); - return p; - } + Console.WriteLine("Syste.Text.Json"); + var paraser = new JsonParser(); + for (int i = 0; i < 2; i++) { + t1 = Environment.TickCount; + using (var reader = File.OpenText("/home/sergey/temp/citylots.json")) { + paraser.Parse(reader); + } - static async Task DoWork() { - var c = 0; - for (int i = 0; i < 10000000; i++) - c = await DoItem(c); - return c; + t2 = Environment.TickCount; + Console.WriteLine("attempt {0} done: {1} ms, {2:.00} Mb, {3} GC, ",i+1, t2 - t1, GC.GetTotalMemory(false) / (1024*1024), GC.CollectionCount(0)); + } + + } } diff --git a/MonoPlay/packages.config b/MonoPlay/packages.config new file mode 100644 --- /dev/null +++ b/MonoPlay/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/packages/NUnit.2.6.4/NUnit.2.6.4.nupkg b/packages/NUnit.2.6.4/NUnit.2.6.4.nupkg new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..379b15bf5cd076569cd68476cd21a17795c0587c GIT binary patch literal 99004 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∋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 CL1Ux< diff --git a/packages/NUnit.2.6.4/lib/nunit.framework.xml b/packages/NUnit.2.6.4/lib/nunit.framework.xml new file mode 100644 --- /dev/null +++ b/packages/NUnit.2.6.4/lib/nunit.framework.xml @@ -0,0 +1,10984 @@ + + + + nunit.framework + + + + + The different targets a test action attribute can be applied to + + + + + Default target, which is determined by where the action attribute is attached + + + + + Target a individual test case + + + + + Target a suite of test cases + + + + + Delegate used by tests that execute code and + capture any thrown exception. + + + + + The Assert class contains a collection of static methods that + implement the most common assertions used in NUnit. + + + + + We don't actually want any instances of this object, but some people + like to inherit from it to add other static methods. Hence, the + protected constructor disallows any instances of this object. + + + + + The Equals method throws an AssertionException. This is done + to make sure there is no mistake by calling this function. + + + + + + + override the default ReferenceEquals to throw an AssertionException. This + implementation makes sure there is no mistake in calling this function + as part of Assert. + + + + + + + Throws a with the message and arguments + that are passed in. This allows a test to be cut short, with a result + of success returned to NUnit. + + The message to initialize the with. + Arguments to be used in formatting the message + + + + Throws a with the message and arguments + that are passed in. This allows a test to be cut short, with a result + of success returned to NUnit. + + The message to initialize the with. + + + + Throws a with the message and arguments + that are passed in. This allows a test to be cut short, with a result + of success returned to NUnit. + + + + + Throws an with the message and arguments + that are passed in. This is used by the other Assert functions. + + The message to initialize the with. + Arguments to be used in formatting the message + + + + Throws an with the message that is + passed in. This is used by the other Assert functions. + + The message to initialize the with. + + + + Throws an . + This is used by the other Assert functions. + + + + + Throws an with the message and arguments + that are passed in. This causes the test to be reported as ignored. + + The message to initialize the with. + Arguments to be used in formatting the message + + + + Throws an with the message that is + passed in. This causes the test to be reported as ignored. + + The message to initialize the with. + + + + Throws an . + This causes the test to be reported as ignored. + + + + + Throws an with the message and arguments + that are passed in. This causes the test to be reported as inconclusive. + + The message to initialize the with. + Arguments to be used in formatting the message + + + + Throws an with the message that is + passed in. This causes the test to be reported as inconclusive. + + The message to initialize the with. + + + + Throws an . + This causes the test to be reported as Inconclusive. + + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + The actual value to test + A Constraint to be applied + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + The actual value to test + A Constraint to be applied + The message that will be displayed on failure + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + The actual value to test + A Constraint expression to be applied + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that a condition is true. If the condition is false the method throws + an . + + The evaluated condition + The message to display if the condition is false + Arguments to be used in formatting the message + + + + Asserts that a condition is true. If the condition is false the method throws + an . + + The evaluated condition + The message to display if the condition is false + + + + Asserts that a condition is true. If the condition is false the method throws + an . + + The evaluated condition + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + An ActualValueDelegate returning the value to be tested + A Constraint expression to be applied + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + An ActualValueDelegate returning the value to be tested + A Constraint expression to be applied + The message that will be displayed on failure + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + An ActualValueDelegate returning the value to be tested + A Constraint expression to be applied + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Apply a constraint to a referenced value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + The actual value to test + A Constraint to be applied + + + + Apply a constraint to a referenced value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + The actual value to test + A Constraint to be applied + The message that will be displayed on failure + + + + Apply a constraint to a referenced value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + The actual value to test + A Constraint to be applied + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that the code represented by a delegate throws an exception + that satisfies the constraint provided. + + A TestDelegate to be executed + A ThrowsConstraint used in the test + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + Used as a synonym for That in rare cases where a private setter + causes a Visual Basic compilation error. + + The actual value to test + A Constraint to be applied + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + Used as a synonym for That in rare cases where a private setter + causes a Visual Basic compilation error. + + The actual value to test + A Constraint to be applied + The message that will be displayed on failure + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + Used as a synonym for That in rare cases where a private setter + causes a Visual Basic compilation error. + + + This method is provided for use by VB developers needing to test + the value of properties with private setters. + + The actual value to test + A Constraint expression to be applied + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that a delegate throws a particular exception when called. + + A constraint to be satisfied by the exception + A TestDelegate + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that a delegate throws a particular exception when called. + + A constraint to be satisfied by the exception + A TestDelegate + The message that will be displayed on failure + + + + Verifies that a delegate throws a particular exception when called. + + A constraint to be satisfied by the exception + A TestDelegate + + + + Verifies that a delegate throws a particular exception when called. + + The exception Type expected + A TestDelegate + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that a delegate throws a particular exception when called. + + The exception Type expected + A TestDelegate + The message that will be displayed on failure + + + + Verifies that a delegate throws a particular exception when called. + + The exception Type expected + A TestDelegate + + + + Verifies that a delegate throws a particular exception when called. + + Type of the expected exception + A TestDelegate + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that a delegate throws a particular exception when called. + + Type of the expected exception + A TestDelegate + The message that will be displayed on failure + + + + Verifies that a delegate throws a particular exception when called. + + Type of the expected exception + A TestDelegate + + + + Verifies that a delegate throws an exception when called + and returns it. + + A TestDelegate + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that a delegate throws an exception when called + and returns it. + + A TestDelegate + The message that will be displayed on failure + + + + Verifies that a delegate throws an exception when called + and returns it. + + A TestDelegate + + + + Verifies that a delegate throws an exception of a certain Type + or one derived from it when called and returns it. + + The expected Exception Type + A TestDelegate + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that a delegate throws an exception of a certain Type + or one derived from it when called and returns it. + + The expected Exception Type + A TestDelegate + The message that will be displayed on failure + + + + Verifies that a delegate throws an exception of a certain Type + or one derived from it when called and returns it. + + The expected Exception Type + A TestDelegate + + + + Verifies that a delegate throws an exception of a certain Type + or one derived from it when called and returns it. + + The expected Exception Type + A TestDelegate + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that a delegate throws an exception of a certain Type + or one derived from it when called and returns it. + + The expected Exception Type + A TestDelegate + The message that will be displayed on failure + + + + Verifies that a delegate throws an exception of a certain Type + or one derived from it when called and returns it. + + The expected Exception Type + A TestDelegate + + + + Verifies that a delegate does not throw an exception + + A TestDelegate + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that a delegate does not throw an exception. + + A TestDelegate + The message that will be displayed on failure + + + + Verifies that a delegate does not throw an exception. + + A TestDelegate + + + + Asserts that a condition is true. If the condition is false the method throws + an . + + The evaluated condition + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that a condition is true. If the condition is false the method throws + an . + + The evaluated condition + The message to display in case of failure + + + + Asserts that a condition is true. If the condition is false the method throws + an . + + The evaluated condition + + + + Asserts that a condition is true. If the condition is false the method throws + an . + + The evaluated condition + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that a condition is true. If the condition is false the method throws + an . + + The evaluated condition + The message to display in case of failure + + + + Asserts that a condition is true. If the condition is false the method throws + an . + + The evaluated condition + + + + Asserts that a condition is false. If the condition is true the method throws + an . + + The evaluated condition + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that a condition is false. If the condition is true the method throws + an . + + The evaluated condition + The message to display in case of failure + + + + Asserts that a condition is false. If the condition is true the method throws + an . + + The evaluated condition + + + + Asserts that a condition is false. If the condition is true the method throws + an . + + The evaluated condition + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that a condition is false. If the condition is true the method throws + an . + + The evaluated condition + The message to display in case of failure + + + + Asserts that a condition is false. If the condition is true the method throws + an . + + The evaluated condition + + + + Verifies that the object that is passed in is not equal to null + If the object is null then an + is thrown. + + The object that is to be tested + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the object that is passed in is not equal to null + If the object is null then an + is thrown. + + The object that is to be tested + The message to display in case of failure + + + + Verifies that the object that is passed in is not equal to null + If the object is null then an + is thrown. + + The object that is to be tested + + + + Verifies that the object that is passed in is not equal to null + If the object is null then an + is thrown. + + The object that is to be tested + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the object that is passed in is not equal to null + If the object is null then an + is thrown. + + The object that is to be tested + The message to display in case of failure + + + + Verifies that the object that is passed in is not equal to null + If the object is null then an + is thrown. + + The object that is to be tested + + + + Verifies that the object that is passed in is equal to null + If the object is not null then an + is thrown. + + The object that is to be tested + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the object that is passed in is equal to null + If the object is not null then an + is thrown. + + The object that is to be tested + The message to display in case of failure + + + + Verifies that the object that is passed in is equal to null + If the object is not null then an + is thrown. + + The object that is to be tested + + + + Verifies that the object that is passed in is equal to null + If the object is not null then an + is thrown. + + The object that is to be tested + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the object that is passed in is equal to null + If the object is not null then an + is thrown. + + The object that is to be tested + The message to display in case of failure + + + + Verifies that the object that is passed in is equal to null + If the object is not null then an + is thrown. + + The object that is to be tested + + + + Verifies that two ints are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two ints are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + + + + Verifies that two ints are equal. If they are not, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two longs are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two longs are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + + + + Verifies that two longs are equal. If they are not, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two unsigned ints are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two unsigned ints are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + + + + Verifies that two unsigned ints are equal. If they are not, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two unsigned longs are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two unsigned longs are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + + + + Verifies that two unsigned longs are equal. If they are not, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two decimals are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two decimals are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + + + + Verifies that two decimals are equal. If they are not, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two doubles are equal considering a delta. If the + expected value is infinity then the delta value is ignored. If + they are not equal then an is + thrown. + + The expected value + The actual value + The maximum acceptable difference between the + the expected and the actual + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two doubles are equal considering a delta. If the + expected value is infinity then the delta value is ignored. If + they are not equal then an is + thrown. + + The expected value + The actual value + The maximum acceptable difference between the + the expected and the actual + The message to display in case of failure + + + + Verifies that two doubles are equal considering a delta. If the + expected value is infinity then the delta value is ignored. If + they are not equal then an is + thrown. + + The expected value + The actual value + The maximum acceptable difference between the + the expected and the actual + + + + Verifies that two doubles are equal considering a delta. If the + expected value is infinity then the delta value is ignored. If + they are not equal then an is + thrown. + + The expected value + The actual value + The maximum acceptable difference between the + the expected and the actual + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two doubles are equal considering a delta. If the + expected value is infinity then the delta value is ignored. If + they are not equal then an is + thrown. + + The expected value + The actual value + The maximum acceptable difference between the + the expected and the actual + The message to display in case of failure + + + + Verifies that two doubles are equal considering a delta. If the + expected value is infinity then the delta value is ignored. If + they are not equal then an is + thrown. + + The expected value + The actual value + The maximum acceptable difference between the + the expected and the actual + + + + Verifies that two objects are equal. Two objects are considered + equal if both are null, or if both have the same value. NUnit + has special semantics for some object types. + If they are not equal an is thrown. + + The value that is expected + The actual value + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two objects are equal. Two objects are considered + equal if both are null, or if both have the same value. NUnit + has special semantics for some object types. + If they are not equal an is thrown. + + The value that is expected + The actual value + The message to display in case of failure + + + + Verifies that two objects are equal. Two objects are considered + equal if both are null, or if both have the same value. NUnit + has special semantics for some object types. + If they are not equal an is thrown. + + The value that is expected + The actual value + + + + Verifies that two ints are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two ints are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + + + + Verifies that two ints are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two longs are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two longs are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + + + + Verifies that two longs are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two unsigned ints are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two unsigned ints are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + + + + Verifies that two unsigned ints are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two unsigned longs are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two unsigned longs are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + + + + Verifies that two unsigned longs are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two decimals are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two decimals are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + + + + Verifies that two decimals are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two floats are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two floats are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + + + + Verifies that two floats are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two doubles are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two doubles are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + + + + Verifies that two doubles are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two objects are not equal. Two objects are considered + equal if both are null, or if both have the same value. NUnit + has special semantics for some object types. + If they are equal an is thrown. + + The value that is expected + The actual value + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two objects are not equal. Two objects are considered + equal if both are null, or if both have the same value. NUnit + has special semantics for some object types. + If they are equal an is thrown. + + The value that is expected + The actual value + The message to display in case of failure + + + + Verifies that two objects are not equal. Two objects are considered + equal if both are null, or if both have the same value. NUnit + has special semantics for some object types. + If they are equal an is thrown. + + The value that is expected + The actual value + + + + Asserts that two objects refer to the same object. If they + are not the same an is thrown. + + The expected object + The actual object + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that two objects refer to the same object. If they + are not the same an is thrown. + + The expected object + The actual object + The message to display in case of failure + + + + Asserts that two objects refer to the same object. If they + are not the same an is thrown. + + The expected object + The actual object + + + + Asserts that two objects do not refer to the same object. If they + are the same an is thrown. + + The expected object + The actual object + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that two objects do not refer to the same object. If they + are the same an is thrown. + + The expected object + The actual object + The message to display in case of failure + + + + Asserts that two objects do not refer to the same object. If they + are the same an is thrown. + + The expected object + The actual object + + + + Verifies that the double that is passed in is an NaN value. + If the object is not NaN then an + is thrown. + + The value that is to be tested + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the double that is passed in is an NaN value. + If the object is not NaN then an + is thrown. + + The value that is to be tested + The message to display in case of failure + + + + Verifies that the double that is passed in is an NaN value. + If the object is not NaN then an + is thrown. + + The value that is to be tested + + + + Verifies that the double that is passed in is an NaN value. + If the object is not NaN then an + is thrown. + + The value that is to be tested + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the double that is passed in is an NaN value. + If the object is not NaN then an + is thrown. + + The value that is to be tested + The message to display in case of failure + + + + Verifies that the double that is passed in is an NaN value. + If the object is not NaN then an + is thrown. + + The value that is to be tested + + + + Assert that a string is empty - that is equal to string.Empty + + The string to be tested + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Assert that a string is empty - that is equal to string.Empty + + The string to be tested + The message to display in case of failure + + + + Assert that a string is empty - that is equal to string.Empty + + The string to be tested + + + + Assert that an array, list or other collection is empty + + An array, list or other collection implementing ICollection + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Assert that an array, list or other collection is empty + + An array, list or other collection implementing ICollection + The message to display in case of failure + + + + Assert that an array, list or other collection is empty + + An array, list or other collection implementing ICollection + + + + Assert that a string is not empty - that is not equal to string.Empty + + The string to be tested + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Assert that a string is not empty - that is not equal to string.Empty + + The string to be tested + The message to display in case of failure + + + + Assert that a string is not empty - that is not equal to string.Empty + + The string to be tested + + + + Assert that an array, list or other collection is not empty + + An array, list or other collection implementing ICollection + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Assert that an array, list or other collection is not empty + + An array, list or other collection implementing ICollection + The message to display in case of failure + + + + Assert that an array, list or other collection is not empty + + An array, list or other collection implementing ICollection + + + + Assert that a string is either null or equal to string.Empty + + The string to be tested + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Assert that a string is either null or equal to string.Empty + + The string to be tested + The message to display in case of failure + + + + Assert that a string is either null or equal to string.Empty + + The string to be tested + + + + Assert that a string is not null or empty + + The string to be tested + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Assert that a string is not null or empty + + The string to be tested + The message to display in case of failure + + + + Assert that a string is not null or empty + + The string to be tested + + + + Asserts that an object may be assigned a value of a given Type. + + The expected Type. + The object under examination + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that an object may be assigned a value of a given Type. + + The expected Type. + The object under examination + The message to display in case of failure + + + + Asserts that an object may be assigned a value of a given Type. + + The expected Type. + The object under examination + + + + Asserts that an object may be assigned a value of a given Type. + + The expected Type. + The object under examination + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that an object may be assigned a value of a given Type. + + The expected Type. + The object under examination + The message to display in case of failure + + + + Asserts that an object may be assigned a value of a given Type. + + The expected Type. + The object under examination + + + + Asserts that an object may not be assigned a value of a given Type. + + The expected Type. + The object under examination + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that an object may not be assigned a value of a given Type. + + The expected Type. + The object under examination + The message to display in case of failure + + + + Asserts that an object may not be assigned a value of a given Type. + + The expected Type. + The object under examination + + + + Asserts that an object may not be assigned a value of a given Type. + + The expected Type. + The object under examination + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that an object may not be assigned a value of a given Type. + + The expected Type. + The object under examination + The message to display in case of failure + + + + Asserts that an object may not be assigned a value of a given Type. + + The expected Type. + The object under examination + + + + Asserts that an object is an instance of a given type. + + The expected Type + The object being examined + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that an object is an instance of a given type. + + The expected Type + The object being examined + The message to display in case of failure + + + + Asserts that an object is an instance of a given type. + + The expected Type + The object being examined + + + + Asserts that an object is an instance of a given type. + + The expected Type + The object being examined + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that an object is an instance of a given type. + + The expected Type + The object being examined + The message to display in case of failure + + + + Asserts that an object is an instance of a given type. + + The expected Type + The object being examined + + + + Asserts that an object is an instance of a given type. + + The expected Type + The object being examined + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that an object is an instance of a given type. + + The expected Type + The object being examined + The message to display in case of failure + + + + Asserts that an object is an instance of a given type. + + The expected Type + The object being examined + + + + Asserts that an object is not an instance of a given type. + + The expected Type + The object being examined + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that an object is not an instance of a given type. + + The expected Type + The object being examined + The message to display in case of failure + + + + Asserts that an object is not an instance of a given type. + + The expected Type + The object being examined + + + + Asserts that an object is not an instance of a given type. + + The expected Type + The object being examined + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that an object is not an instance of a given type. + + The expected Type + The object being examined + The message to display in case of failure + + + + Asserts that an object is not an instance of a given type. + + The expected Type + The object being examined + + + + Asserts that an object is not an instance of a given type. + + The expected Type + The object being examined + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that an object is not an instance of a given type. + + The expected Type + The object being examined + The message to display in case of failure + + + + Asserts that an object is not an instance of a given type. + + The expected Type + The object being examined + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Asserts that an object is contained in a list. + + The expected object + The list to be examined + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that an object is contained in a list. + + The expected object + The list to be examined + The message to display in case of failure + + + + Asserts that an object is contained in a list. + + The expected object + The list to be examined + + + + Helper for Assert.AreEqual(double expected, double actual, ...) + allowing code generation to work consistently. + + The expected value + The actual value + The maximum acceptable difference between the + the expected and the actual + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Gets the number of assertions executed so far and + resets the counter to zero. + + + + + AssertionHelper is an optional base class for user tests, + allowing the use of shorter names for constraints and + asserts and avoiding conflict with the definition of + , from which it inherits much of its + behavior, in certain mock object frameworks. + + + + + Helper class with properties and methods that supply + a number of constraints used in Asserts. + + + + + Returns a ConstraintExpression, which will apply + the following constraint to all members of a collection, + succeeding only if a specified number of them succeed. + + + + + Returns a new PropertyConstraintExpression, which will either + test for the existence of the named property on the object + being tested or apply any following constraint to that property. + + + + + Returns a new AttributeConstraint checking for the + presence of a particular attribute on an object. + + + + + Returns a new AttributeConstraint checking for the + presence of a particular attribute on an object. + + + + + Returns a constraint that tests two items for equality + + + + + Returns a constraint that tests that two references are the same object + + + + + Returns a constraint that tests whether the + actual value is greater than the suppled argument + + + + + Returns a constraint that tests whether the + actual value is greater than or equal to the suppled argument + + + + + Returns a constraint that tests whether the + actual value is greater than or equal to the suppled argument + + + + + Returns a constraint that tests whether the + actual value is less than the suppled argument + + + + + Returns a constraint that tests whether the + actual value is less than or equal to the suppled argument + + + + + Returns a constraint that tests whether the + actual value is less than or equal to the suppled argument + + + + + Returns a constraint that tests whether the actual + value is of the exact type supplied as an argument. + + + + + Returns a constraint that tests whether the actual + value is of the exact type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is of the type supplied as an argument or a derived type. + + + + + Returns a constraint that tests whether the actual value + is of the type supplied as an argument or a derived type. + + + + + Returns a constraint that tests whether the actual value + is of the type supplied as an argument or a derived type. + + + + + Returns a constraint that tests whether the actual value + is of the type supplied as an argument or a derived type. + + + + + Returns a constraint that tests whether the actual value + is assignable from the type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is assignable from the type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is assignable from the type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is assignable from the type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is a collection containing the same elements as the + collection supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is a subset of the collection supplied as an argument. + + + + + Returns a new CollectionContainsConstraint checking for the + presence of a particular object in the collection. + + + + + Returns a new CollectionContainsConstraint checking for the + presence of a particular object in the collection. + + + + + Returns a new ContainsConstraint. This constraint + will, in turn, make use of the appropriate second-level + constraint, depending on the type of the actual argument. + This overload is only used if the item sought is a string, + since any other type implies that we are looking for a + collection member. + + + + + Returns a constraint that succeeds if the actual + value contains the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value contains the substring supplied as an argument. + + + + + Returns a constraint that fails if the actual + value contains the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value starts with the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value starts with the substring supplied as an argument. + + + + + Returns a constraint that fails if the actual + value starts with the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value ends with the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value ends with the substring supplied as an argument. + + + + + Returns a constraint that fails if the actual + value ends with the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value matches the regular expression supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value matches the regular expression supplied as an argument. + + + + + Returns a constraint that fails if the actual + value matches the pattern supplied as an argument. + + + + + Returns a constraint that tests whether the path provided + is the same as an expected path after canonicalization. + + + + + Returns a constraint that tests whether the path provided + is the same path or under an expected path after canonicalization. + + + + + Returns a constraint that tests whether the path provided + is the same path or under an expected path after canonicalization. + + + + + Returns a constraint that tests whether the actual value falls + within a specified range. + + + + + Returns a ConstraintExpression that negates any + following constraint. + + + + + Returns a ConstraintExpression that negates any + following constraint. + + + + + Returns a ConstraintExpression, which will apply + the following constraint to all members of a collection, + succeeding if all of them succeed. + + + + + Returns a ConstraintExpression, which will apply + the following constraint to all members of a collection, + succeeding if at least one of them succeeds. + + + + + Returns a ConstraintExpression, which will apply + the following constraint to all members of a collection, + succeeding if all of them fail. + + + + + Returns a new ConstraintExpression, which will apply the following + constraint to the Length property of the object being tested. + + + + + Returns a new ConstraintExpression, which will apply the following + constraint to the Count property of the object being tested. + + + + + Returns a new ConstraintExpression, which will apply the following + constraint to the Message property of the object being tested. + + + + + Returns a new ConstraintExpression, which will apply the following + constraint to the InnerException property of the object being tested. + + + + + Returns a constraint that tests for null + + + + + Returns a constraint that tests for True + + + + + Returns a constraint that tests for False + + + + + Returns a constraint that tests for a positive value + + + + + Returns a constraint that tests for a negative value + + + + + Returns a constraint that tests for NaN + + + + + Returns a constraint that tests for empty + + + + + Returns a constraint that tests whether a collection + contains all unique items. + + + + + Returns a constraint that tests whether an object graph is serializable in binary format. + + + + + Returns a constraint that tests whether an object graph is serializable in xml format. + + + + + Returns a constraint that tests whether a collection is ordered + + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. Works + identically to Assert.That. + + The actual value to test + A Constraint to be applied + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. Works + identically to Assert.That. + + The actual value to test + A Constraint to be applied + The message to be displayed in case of failure + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. Works + identically to Assert.That. + + The actual value to test + A Constraint to be applied + The message to be displayed in case of failure + Arguments to use in formatting the message + + + + Asserts that a condition is true. If the condition is false the method throws + an . Works Identically to + . + + The evaluated condition + The message to display if the condition is false + Arguments to be used in formatting the message + + + + Asserts that a condition is true. If the condition is false the method throws + an . Works Identically to + . + + The evaluated condition + The message to display if the condition is false + + + + Asserts that a condition is true. If the condition is false the method throws + an . Works Identically to . + + The evaluated condition + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + A Constraint expression to be applied + An ActualValueDelegate returning the value to be tested + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + A Constraint expression to be applied + An ActualValueDelegate returning the value to be tested + The message that will be displayed on failure + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + An ActualValueDelegate returning the value to be tested + A Constraint expression to be applied + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Apply a constraint to a referenced value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + The actual value to test + A Constraint to be applied + + + + Apply a constraint to a referenced value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + The actual value to test + A Constraint to be applied + The message that will be displayed on failure + + + + Apply a constraint to a referenced value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + The actual value to test + A Constraint to be applied + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that the code represented by a delegate throws an exception + that satisfies the constraint provided. + + A TestDelegate to be executed + A ThrowsConstraint used in the test + + + + Returns a ListMapper based on a collection. + + The original collection + + + + + Provides static methods to express the assumptions + that must be met for a test to give a meaningful + result. If an assumption is not met, the test + should produce an inconclusive result. + + + + + The Equals method throws an AssertionException. This is done + to make sure there is no mistake by calling this function. + + + + + + + override the default ReferenceEquals to throw an AssertionException. This + implementation makes sure there is no mistake in calling this function + as part of Assert. + + + + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an InconclusiveException on failure. + + A Constraint expression to be applied + The actual value to test + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an InconclusiveException on failure. + + A Constraint expression to be applied + The actual value to test + The message that will be displayed on failure + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an InconclusiveException on failure. + + A Constraint expression to be applied + The actual value to test + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that a condition is true. If the condition is false the method throws + an . + + The evaluated condition + The message to display if the condition is false + Arguments to be used in formatting the message + + + + Asserts that a condition is true. If the condition is false the method throws + an . + + The evaluated condition + The message to display if the condition is false + + + + Asserts that a condition is true. If the condition is false the + method throws an . + + The evaluated condition + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an InconclusiveException on failure. + + A Constraint expression to be applied + An ActualValueDelegate returning the value to be tested + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an InconclusiveException on failure. + + A Constraint expression to be applied + An ActualValueDelegate returning the value to be tested + The message that will be displayed on failure + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an InconclusiveException on failure. + + An ActualValueDelegate returning the value to be tested + A Constraint expression to be applied + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Apply a constraint to a referenced value, succeeding if the constraint + is satisfied and throwing an InconclusiveException on failure. + + A Constraint expression to be applied + The actual value to test + + + + Apply a constraint to a referenced value, succeeding if the constraint + is satisfied and throwing an InconclusiveException on failure. + + A Constraint expression to be applied + The actual value to test + The message that will be displayed on failure + + + + Apply a constraint to a referenced value, succeeding if the constraint + is satisfied and throwing an InconclusiveException on failure. + + A Constraint expression to be applied + The actual value to test + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that the code represented by a delegate throws an exception + that satisfies the constraint provided. + + A TestDelegate to be executed + A ThrowsConstraint used in the test + + + + Waits for pending asynchronous operations to complete, if appropriate, + and returns a proper result of the invocation by unwrapping task results + + The raw result of the method invocation + The unwrapped result, if necessary + + + + A set of Assert methods operationg on one or more collections + + + + + The Equals method throws an AssertionException. This is done + to make sure there is no mistake by calling this function. + + + + + + + override the default ReferenceEquals to throw an AssertionException. This + implementation makes sure there is no mistake in calling this function + as part of Assert. + + + + + + + Asserts that all items contained in collection are of the type specified by expectedType. + + IEnumerable containing objects to be considered + System.Type that all objects in collection must be instances of + + + + Asserts that all items contained in collection are of the type specified by expectedType. + + IEnumerable containing objects to be considered + System.Type that all objects in collection must be instances of + The message that will be displayed on failure + + + + Asserts that all items contained in collection are of the type specified by expectedType. + + IEnumerable containing objects to be considered + System.Type that all objects in collection must be instances of + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that all items contained in collection are not equal to null. + + IEnumerable containing objects to be considered + + + + Asserts that all items contained in collection are not equal to null. + + IEnumerable containing objects to be considered + The message that will be displayed on failure + + + + Asserts that all items contained in collection are not equal to null. + + IEnumerable of objects to be considered + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Ensures that every object contained in collection exists within the collection + once and only once. + + IEnumerable of objects to be considered + + + + Ensures that every object contained in collection exists within the collection + once and only once. + + IEnumerable of objects to be considered + The message that will be displayed on failure + + + + Ensures that every object contained in collection exists within the collection + once and only once. + + IEnumerable of objects to be considered + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that expected and actual are exactly equal. The collections must have the same count, + and contain the exact same objects in the same order. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + + + + Asserts that expected and actual are exactly equal. The collections must have the same count, + and contain the exact same objects in the same order. + If comparer is not null then it will be used to compare the objects. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + The IComparer to use in comparing objects from each IEnumerable + + + + Asserts that expected and actual are exactly equal. The collections must have the same count, + and contain the exact same objects in the same order. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + The message that will be displayed on failure + + + + Asserts that expected and actual are exactly equal. The collections must have the same count, + and contain the exact same objects in the same order. + If comparer is not null then it will be used to compare the objects. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + The IComparer to use in comparing objects from each IEnumerable + The message that will be displayed on failure + + + + Asserts that expected and actual are exactly equal. The collections must have the same count, + and contain the exact same objects in the same order. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that expected and actual are exactly equal. The collections must have the same count, + and contain the exact same objects in the same order. + If comparer is not null then it will be used to compare the objects. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + The IComparer to use in comparing objects from each IEnumerable + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that expected and actual are equivalent, containing the same objects but the match may be in any order. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + + + + Asserts that expected and actual are equivalent, containing the same objects but the match may be in any order. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + The message that will be displayed on failure + + + + Asserts that expected and actual are equivalent, containing the same objects but the match may be in any order. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that expected and actual are not exactly equal. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + + + + Asserts that expected and actual are not exactly equal. + If comparer is not null then it will be used to compare the objects. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + The IComparer to use in comparing objects from each IEnumerable + + + + Asserts that expected and actual are not exactly equal. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + The message that will be displayed on failure + + + + Asserts that expected and actual are not exactly equal. + If comparer is not null then it will be used to compare the objects. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + The IComparer to use in comparing objects from each IEnumerable + The message that will be displayed on failure + + + + Asserts that expected and actual are not exactly equal. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that expected and actual are not exactly equal. + If comparer is not null then it will be used to compare the objects. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + The IComparer to use in comparing objects from each IEnumerable + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that expected and actual are not equivalent. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + + + + Asserts that expected and actual are not equivalent. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + The message that will be displayed on failure + + + + Asserts that expected and actual are not equivalent. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that collection contains actual as an item. + + IEnumerable of objects to be considered + Object to be found within collection + + + + Asserts that collection contains actual as an item. + + IEnumerable of objects to be considered + Object to be found within collection + The message that will be displayed on failure + + + + Asserts that collection contains actual as an item. + + IEnumerable of objects to be considered + Object to be found within collection + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that collection does not contain actual as an item. + + IEnumerable of objects to be considered + Object that cannot exist within collection + + + + Asserts that collection does not contain actual as an item. + + IEnumerable of objects to be considered + Object that cannot exist within collection + The message that will be displayed on failure + + + + Asserts that collection does not contain actual as an item. + + IEnumerable of objects to be considered + Object that cannot exist within collection + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that the superset does not contain the subset + + The IEnumerable subset to be considered + The IEnumerable superset to be considered + + + + Asserts that the superset does not contain the subset + + The IEnumerable subset to be considered + The IEnumerable superset to be considered + The message that will be displayed on failure + + + + Asserts that the superset does not contain the subset + + The IEnumerable subset to be considered + The IEnumerable superset to be considered + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that the superset contains the subset. + + The IEnumerable subset to be considered + The IEnumerable superset to be considered + + + + Asserts that the superset contains the subset. + + The IEnumerable subset to be considered + The IEnumerable superset to be considered + The message that will be displayed on failure + + + + Asserts that the superset contains the subset. + + The IEnumerable subset to be considered + The IEnumerable superset to be considered + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Assert that an array, list or other collection is empty + + An array, list or other collection implementing IEnumerable + The message to be displayed on failure + Arguments to be used in formatting the message + + + + Assert that an array, list or other collection is empty + + An array, list or other collection implementing IEnumerable + The message to be displayed on failure + + + + Assert that an array,list or other collection is empty + + An array, list or other collection implementing IEnumerable + + + + Assert that an array, list or other collection is empty + + An array, list or other collection implementing IEnumerable + The message to be displayed on failure + Arguments to be used in formatting the message + + + + Assert that an array, list or other collection is empty + + An array, list or other collection implementing IEnumerable + The message to be displayed on failure + + + + Assert that an array,list or other collection is empty + + An array, list or other collection implementing IEnumerable + + + + Assert that an array, list or other collection is ordered + + An array, list or other collection implementing IEnumerable + The message to be displayed on failure + Arguments to be used in formatting the message + + + + Assert that an array, list or other collection is ordered + + An array, list or other collection implementing IEnumerable + The message to be displayed on failure + + + + Assert that an array, list or other collection is ordered + + An array, list or other collection implementing IEnumerable + + + + Assert that an array, list or other collection is ordered + + An array, list or other collection implementing IEnumerable + A custom comparer to perform the comparisons + The message to be displayed on failure + Arguments to be used in formatting the message + + + + Assert that an array, list or other collection is ordered + + An array, list or other collection implementing IEnumerable + A custom comparer to perform the comparisons + The message to be displayed on failure + + + + Assert that an array, list or other collection is ordered + + An array, list or other collection implementing IEnumerable + A custom comparer to perform the comparisons + + + + Helper class with properties and methods that supply + a number of constraints used in Asserts. + + + + + Returns a new CollectionContainsConstraint checking for the + presence of a particular object in the collection. + + + + + Returns a constraint that succeeds if the actual + value contains the substring supplied as an argument. + + + + + Summary description for DirectoryAssert + + + + + The Equals method throws an AssertionException. This is done + to make sure there is no mistake by calling this function. + + + + + + + override the default ReferenceEquals to throw an AssertionException. This + implementation makes sure there is no mistake in calling this function + as part of Assert. + + + + + + + We don't actually want any instances of this object, but some people + like to inherit from it to add other static methods. Hence, the + protected constructor disallows any instances of this object. + + + + + Verifies that two directories are equal. Two directories are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + A directory containing the value that is expected + A directory containing the actual value + The message to display if directories are not equal + Arguments to be used in formatting the message + + + + Verifies that two directories are equal. Two directories are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + A directory containing the value that is expected + A directory containing the actual value + The message to display if directories are not equal + + + + Verifies that two directories are equal. Two directories are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + A directory containing the value that is expected + A directory containing the actual value + + + + Verifies that two directories are equal. Two directories are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + A directory path string containing the value that is expected + A directory path string containing the actual value + The message to display if directories are not equal + Arguments to be used in formatting the message + + + + Verifies that two directories are equal. Two directories are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + A directory path string containing the value that is expected + A directory path string containing the actual value + The message to display if directories are not equal + + + + Verifies that two directories are equal. Two directories are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + A directory path string containing the value that is expected + A directory path string containing the actual value + + + + Asserts that two directories are not equal. If they are equal + an is thrown. + + A directory containing the value that is expected + A directory containing the actual value + The message to display if directories are not equal + Arguments to be used in formatting the message + + + + Asserts that two directories are not equal. If they are equal + an is thrown. + + A directory containing the value that is expected + A directory containing the actual value + The message to display if directories are not equal + + + + Asserts that two directories are not equal. If they are equal + an is thrown. + + A directory containing the value that is expected + A directory containing the actual value + + + + Asserts that two directories are not equal. If they are equal + an is thrown. + + A directory path string containing the value that is expected + A directory path string containing the actual value + The message to display if directories are equal + Arguments to be used in formatting the message + + + + Asserts that two directories are not equal. If they are equal + an is thrown. + + A directory path string containing the value that is expected + A directory path string containing the actual value + The message to display if directories are equal + + + + Asserts that two directories are not equal. If they are equal + an is thrown. + + A directory path string containing the value that is expected + A directory path string containing the actual value + + + + Asserts that the directory is empty. If it is not empty + an is thrown. + + A directory to search + The message to display if directories are not equal + Arguments to be used in formatting the message + + + + Asserts that the directory is empty. If it is not empty + an is thrown. + + A directory to search + The message to display if directories are not equal + + + + Asserts that the directory is empty. If it is not empty + an is thrown. + + A directory to search + + + + Asserts that the directory is empty. If it is not empty + an is thrown. + + A directory to search + The message to display if directories are not equal + Arguments to be used in formatting the message + + + + Asserts that the directory is empty. If it is not empty + an is thrown. + + A directory to search + The message to display if directories are not equal + + + + Asserts that the directory is empty. If it is not empty + an is thrown. + + A directory to search + + + + Asserts that the directory is not empty. If it is empty + an is thrown. + + A directory to search + The message to display if directories are not equal + Arguments to be used in formatting the message + + + + Asserts that the directory is not empty. If it is empty + an is thrown. + + A directory to search + The message to display if directories are not equal + + + + Asserts that the directory is not empty. If it is empty + an is thrown. + + A directory to search + + + + Asserts that the directory is not empty. If it is empty + an is thrown. + + A directory to search + The message to display if directories are not equal + Arguments to be used in formatting the message + + + + Asserts that the directory is not empty. If it is empty + an is thrown. + + A directory to search + The message to display if directories are not equal + + + + Asserts that the directory is not empty. If it is empty + an is thrown. + + A directory to search + + + + Asserts that path contains actual as a subdirectory or + an is thrown. + + A directory to search + sub-directory asserted to exist under directory + The message to display if directory is not within the path + Arguments to be used in formatting the message + + + + Asserts that path contains actual as a subdirectory or + an is thrown. + + A directory to search + sub-directory asserted to exist under directory + The message to display if directory is not within the path + + + + Asserts that path contains actual as a subdirectory or + an is thrown. + + A directory to search + sub-directory asserted to exist under directory + + + + Asserts that path contains actual as a subdirectory or + an is thrown. + + A directory to search + sub-directory asserted to exist under directory + The message to display if directory is not within the path + Arguments to be used in formatting the message + + + + Asserts that path contains actual as a subdirectory or + an is thrown. + + A directory to search + sub-directory asserted to exist under directory + The message to display if directory is not within the path + + + + Asserts that path contains actual as a subdirectory or + an is thrown. + + A directory to search + sub-directory asserted to exist under directory + + + + Asserts that path does not contain actual as a subdirectory or + an is thrown. + + A directory to search + sub-directory asserted to exist under directory + The message to display if directory is not within the path + Arguments to be used in formatting the message + + + + Asserts that path does not contain actual as a subdirectory or + an is thrown. + + A directory to search + sub-directory asserted to exist under directory + The message to display if directory is not within the path + + + + Asserts that path does not contain actual as a subdirectory or + an is thrown. + + A directory to search + sub-directory asserted to exist under directory + + + + Asserts that path does not contain actual as a subdirectory or + an is thrown. + + A directory to search + sub-directory asserted to exist under directory + The message to display if directory is not within the path + Arguments to be used in formatting the message + + + + Asserts that path does not contain actual as a subdirectory or + an is thrown. + + A directory to search + sub-directory asserted to exist under directory + The message to display if directory is not within the path + + + + Asserts that path does not contain actual as a subdirectory or + an is thrown. + + A directory to search + sub-directory asserted to exist under directory + + + + Summary description for FileAssert. + + + + + The Equals method throws an AssertionException. This is done + to make sure there is no mistake by calling this function. + + + + + + + override the default ReferenceEquals to throw an AssertionException. This + implementation makes sure there is no mistake in calling this function + as part of Assert. + + + + + + + We don't actually want any instances of this object, but some people + like to inherit from it to add other static methods. Hence, the + protected constructor disallows any instances of this object. + + + + + Verifies that two Streams are equal. Two Streams are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + The expected Stream + The actual Stream + The message to display if Streams are not equal + Arguments to be used in formatting the message + + + + Verifies that two Streams are equal. Two Streams are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + The expected Stream + The actual Stream + The message to display if objects are not equal + + + + Verifies that two Streams are equal. Two Streams are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + The expected Stream + The actual Stream + + + + Verifies that two files are equal. Two files are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + A file containing the value that is expected + A file containing the actual value + The message to display if Streams are not equal + Arguments to be used in formatting the message + + + + Verifies that two files are equal. Two files are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + A file containing the value that is expected + A file containing the actual value + The message to display if objects are not equal + + + + Verifies that two files are equal. Two files are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + A file containing the value that is expected + A file containing the actual value + + + + Verifies that two files are equal. Two files are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + The path to a file containing the value that is expected + The path to a file containing the actual value + The message to display if Streams are not equal + Arguments to be used in formatting the message + + + + Verifies that two files are equal. Two files are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + The path to a file containing the value that is expected + The path to a file containing the actual value + The message to display if objects are not equal + + + + Verifies that two files are equal. Two files are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + The path to a file containing the value that is expected + The path to a file containing the actual value + + + + Asserts that two Streams are not equal. If they are equal + an is thrown. + + The expected Stream + The actual Stream + The message to be displayed when the two Stream are the same. + Arguments to be used in formatting the message + + + + Asserts that two Streams are not equal. If they are equal + an is thrown. + + The expected Stream + The actual Stream + The message to be displayed when the Streams are the same. + + + + Asserts that two Streams are not equal. If they are equal + an is thrown. + + The expected Stream + The actual Stream + + + + Asserts that two files are not equal. If they are equal + an is thrown. + + A file containing the value that is expected + A file containing the actual value + The message to display if Streams are not equal + Arguments to be used in formatting the message + + + + Asserts that two files are not equal. If they are equal + an is thrown. + + A file containing the value that is expected + A file containing the actual value + The message to display if objects are not equal + + + + Asserts that two files are not equal. If they are equal + an is thrown. + + A file containing the value that is expected + A file containing the actual value + + + + Asserts that two files are not equal. If they are equal + an is thrown. + + The path to a file containing the value that is expected + The path to a file containing the actual value + The message to display if Streams are not equal + Arguments to be used in formatting the message + + + + Asserts that two files are not equal. If they are equal + an is thrown. + + The path to a file containing the value that is expected + The path to a file containing the actual value + The message to display if objects are not equal + + + + Asserts that two files are not equal. If they are equal + an is thrown. + + The path to a file containing the value that is expected + The path to a file containing the actual value + + + + GlobalSettings is a place for setting default values used + by the framework in performing asserts. + + + + + Default tolerance for floating point equality + + + + + Class used to guard against unexpected argument values + by throwing an appropriate exception. + + + + + Throws an exception if an argument is null + + The value to be tested + The name of the argument + + + + Throws an exception if a string argument is null or empty + + The value to be tested + The name of the argument + + + + Helper class with properties and methods that supply + a number of constraints used in Asserts. + + + + + Returns a ConstraintExpression, which will apply + the following constraint to all members of a collection, + succeeding only if a specified number of them succeed. + + + + + Returns a new PropertyConstraintExpression, which will either + test for the existence of the named property on the object + being tested or apply any following constraint to that property. + + + + + Returns a new AttributeConstraint checking for the + presence of a particular attribute on an object. + + + + + Returns a new AttributeConstraint checking for the + presence of a particular attribute on an object. + + + + + Returns a new CollectionContainsConstraint checking for the + presence of a particular object in the collection. + + + + + Returns a ConstraintExpression that negates any + following constraint. + + + + + Returns a ConstraintExpression, which will apply + the following constraint to all members of a collection, + succeeding if all of them succeed. + + + + + Returns a ConstraintExpression, which will apply + the following constraint to all members of a collection, + succeeding if at least one of them succeeds. + + + + + Returns a ConstraintExpression, which will apply + the following constraint to all members of a collection, + succeeding if all of them fail. + + + + + Returns a new ConstraintExpression, which will apply the following + constraint to the Length property of the object being tested. + + + + + Returns a new ConstraintExpression, which will apply the following + constraint to the Count property of the object being tested. + + + + + Returns a new ConstraintExpression, which will apply the following + constraint to the Message property of the object being tested. + + + + + Returns a new ConstraintExpression, which will apply the following + constraint to the InnerException property of the object being tested. + + + + + Interface implemented by a user fixture in order to + validate any expected exceptions. It is only called + for test methods marked with the ExpectedException + attribute. + + + + + Method to handle an expected exception + + The exception to be handled + + + + Helper class with properties and methods that supply + a number of constraints used in Asserts. + + + + + Returns a constraint that tests two items for equality + + + + + Returns a constraint that tests that two references are the same object + + + + + Returns a constraint that tests whether the + actual value is greater than the suppled argument + + + + + Returns a constraint that tests whether the + actual value is greater than or equal to the suppled argument + + + + + Returns a constraint that tests whether the + actual value is greater than or equal to the suppled argument + + + + + Returns a constraint that tests whether the + actual value is less than the suppled argument + + + + + Returns a constraint that tests whether the + actual value is less than or equal to the suppled argument + + + + + Returns a constraint that tests whether the + actual value is less than or equal to the suppled argument + + + + + Returns a constraint that tests whether the actual + value is of the exact type supplied as an argument. + + + + + Returns a constraint that tests whether the actual + value is of the exact type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is of the type supplied as an argument or a derived type. + + + + + Returns a constraint that tests whether the actual value + is of the type supplied as an argument or a derived type. + + + + + Returns a constraint that tests whether the actual value + is of the type supplied as an argument or a derived type. + + + + + Returns a constraint that tests whether the actual value + is of the type supplied as an argument or a derived type. + + + + + Returns a constraint that tests whether the actual value + is assignable from the type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is assignable from the type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is assignable from the type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is assignable from the type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is a collection containing the same elements as the + collection supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is a subset of the collection supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value contains the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value starts with the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value ends with the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value matches the regular expression supplied as an argument. + + + + + Returns a constraint that tests whether the path provided + is the same as an expected path after canonicalization. + + + + + Returns a constraint that tests whether the path provided + is under an expected path after canonicalization. + + + + + Returns a constraint that tests whether the path provided + is the same path or under an expected path after canonicalization. + + + + + Returns a constraint that tests whether the actual value falls + within a specified range. + + + + + Returns a ConstraintExpression that negates any + following constraint. + + + + + Returns a ConstraintExpression, which will apply + the following constraint to all members of a collection, + succeeding if all of them succeed. + + + + + Returns a constraint that tests for null + + + + + Returns a constraint that tests for True + + + + + Returns a constraint that tests for False + + + + + Returns a constraint that tests for a positive value + + + + + Returns a constraint that tests for a negative value + + + + + Returns a constraint that tests for NaN + + + + + Returns a constraint that tests for empty + + + + + Returns a constraint that tests whether a collection + contains all unique items. + + + + + Returns a constraint that tests whether an object graph is serializable in binary format. + + + + + Returns a constraint that tests whether an object graph is serializable in xml format. + + + + + Returns a constraint that tests whether a collection is ordered + + + + + The ITestCaseData interface is implemented by a class + that is able to return complete testcases for use by + a parameterized test method. + + NOTE: This interface is used in both the framework + and the core, even though that results in two different + types. However, sharing the source code guarantees that + the various implementations will be compatible and that + the core is able to reflect successfully over the + framework implementations of ITestCaseData. + + + + + Gets the argument list to be provided to the test + + + + + Gets the expected result + + + + + Indicates whether a result has been specified. + This is necessary because the result may be + null, so it's value cannot be checked. + + + + + Gets the expected exception Type + + + + + Gets the FullName of the expected exception + + + + + Gets the name to be used for the test + + + + + Gets the description of the test + + + + + Gets a value indicating whether this is ignored. + + true if ignored; otherwise, false. + + + + Gets a value indicating whether this is explicit. + + true if explicit; otherwise, false. + + + + Gets the ignore reason. + + The ignore reason. + + + + The Iz class is a synonym for Is intended for use in VB, + which regards Is as a keyword. + + + + + The List class is a helper class with properties and methods + that supply a number of constraints used with lists and collections. + + + + + List.Map returns a ListMapper, which can be used to map + the original collection to another collection. + + + + + + + ListMapper is used to transform a collection used as an actual argument + producing another collection to be used in the assertion. + + + + + Construct a ListMapper based on a collection + + The collection to be transformed + + + + Produces a collection containing all the values of a property + + The collection of property values + + + + + Randomizer returns a set of random values in a repeatable + way, to allow re-running of tests if necessary. + + + + + Get a randomizer for a particular member, returning + one that has already been created if it exists. + This ensures that the same values are generated + each time the tests are reloaded. + + + + + Get a randomizer for a particular parameter, returning + one that has already been created if it exists. + This ensures that the same values are generated + each time the tests are reloaded. + + + + + Construct a randomizer using a random seed + + + + + Construct a randomizer using a specified seed + + + + + Return an array of random doubles between 0.0 and 1.0. + + + + + + + Return an array of random doubles with values in a specified range. + + + + + Return an array of random ints with values in a specified range. + + + + + Get a random seed for use in creating a randomizer. + + + + + The SpecialValue enum is used to represent TestCase arguments + that cannot be used as arguments to an Attribute. + + + + + Null represents a null value, which cannot be used as an + argument to an attribute under .NET 1.x + + + + + Basic Asserts on strings. + + + + + The Equals method throws an AssertionException. This is done + to make sure there is no mistake by calling this function. + + + + + + + override the default ReferenceEquals to throw an AssertionException. This + implementation makes sure there is no mistake in calling this function + as part of Assert. + + + + + + + Asserts that a string is found within another string. + + The expected string + The string to be examined + The message to display in case of failure + Arguments used in formatting the message + + + + Asserts that a string is found within another string. + + The expected string + The string to be examined + The message to display in case of failure + + + + Asserts that a string is found within another string. + + The expected string + The string to be examined + + + + Asserts that a string is not found within another string. + + The expected string + The string to be examined + The message to display in case of failure + Arguments used in formatting the message + + + + Asserts that a string is found within another string. + + The expected string + The string to be examined + The message to display in case of failure + + + + Asserts that a string is found within another string. + + The expected string + The string to be examined + + + + Asserts that a string starts with another string. + + The expected string + The string to be examined + The message to display in case of failure + Arguments used in formatting the message + + + + Asserts that a string starts with another string. + + The expected string + The string to be examined + The message to display in case of failure + + + + Asserts that a string starts with another string. + + The expected string + The string to be examined + + + + Asserts that a string does not start with another string. + + The expected string + The string to be examined + The message to display in case of failure + Arguments used in formatting the message + + + + Asserts that a string does not start with another string. + + The expected string + The string to be examined + The message to display in case of failure + + + + Asserts that a string does not start with another string. + + The expected string + The string to be examined + + + + Asserts that a string ends with another string. + + The expected string + The string to be examined + The message to display in case of failure + Arguments used in formatting the message + + + + Asserts that a string ends with another string. + + The expected string + The string to be examined + The message to display in case of failure + + + + Asserts that a string ends with another string. + + The expected string + The string to be examined + + + + Asserts that a string does not end with another string. + + The expected string + The string to be examined + The message to display in case of failure + Arguments used in formatting the message + + + + Asserts that a string does not end with another string. + + The expected string + The string to be examined + The message to display in case of failure + + + + Asserts that a string does not end with another string. + + The expected string + The string to be examined + + + + Asserts that two strings are equal, without regard to case. + + The expected string + The actual string + The message to display in case of failure + Arguments used in formatting the message + + + + Asserts that two strings are equal, without regard to case. + + The expected string + The actual string + The message to display in case of failure + + + + Asserts that two strings are equal, without regard to case. + + The expected string + The actual string + + + + Asserts that two strings are not equal, without regard to case. + + The expected string + The actual string + The message to display in case of failure + Arguments used in formatting the message + + + + Asserts that two strings are Notequal, without regard to case. + + The expected string + The actual string + The message to display in case of failure + + + + Asserts that two strings are not equal, without regard to case. + + The expected string + The actual string + + + + Asserts that a string matches an expected regular expression pattern. + + The regex pattern to be matched + The actual string + The message to display in case of failure + Arguments used in formatting the message + + + + Asserts that a string matches an expected regular expression pattern. + + The regex pattern to be matched + The actual string + The message to display in case of failure + + + + Asserts that a string matches an expected regular expression pattern. + + The regex pattern to be matched + The actual string + + + + Asserts that a string does not match an expected regular expression pattern. + + The regex pattern to be used + The actual string + The message to display in case of failure + Arguments used in formatting the message + + + + Asserts that a string does not match an expected regular expression pattern. + + The regex pattern to be used + The actual string + The message to display in case of failure + + + + Asserts that a string does not match an expected regular expression pattern. + + The regex pattern to be used + The actual string + + + + The TestCaseData class represents a set of arguments + and other parameter info to be used for a parameterized + test case. It provides a number of instance modifiers + for use in initializing the test case. + + Note: Instance modifiers are getters that return + the same instance after modifying it's state. + + + + + The argument list to be provided to the test + + + + + The expected result to be returned + + + + + Set to true if this has an expected result + + + + + The expected exception Type + + + + + The FullName of the expected exception + + + + + The name to be used for the test + + + + + The description of the test + + + + + A dictionary of properties, used to add information + to tests without requiring the class to change. + + + + + If true, indicates that the test case is to be ignored + + + + + If true, indicates that the test case is marked explicit + + + + + The reason for ignoring a test case + + + + + Initializes a new instance of the class. + + The arguments. + + + + Initializes a new instance of the class. + + The argument. + + + + Initializes a new instance of the class. + + The first argument. + The second argument. + + + + Initializes a new instance of the class. + + The first argument. + The second argument. + The third argument. + + + + Sets the expected result for the test + + The expected result + A modified TestCaseData + + + + Sets the expected exception type for the test + + Type of the expected exception. + The modified TestCaseData instance + + + + Sets the expected exception type for the test + + FullName of the expected exception. + The modified TestCaseData instance + + + + Sets the name of the test case + + The modified TestCaseData instance + + + + Sets the description for the test case + being constructed. + + The description. + The modified TestCaseData instance. + + + + Applies a category to the test + + + + + + + Applies a named property to the test + + + + + + + + Applies a named property to the test + + + + + + + + Applies a named property to the test + + + + + + + + Ignores this TestCase. + + + + + + Ignores this TestCase, specifying the reason. + + The reason. + + + + + Marks this TestCase as Explicit + + + + + + Marks this TestCase as Explicit, specifying the reason. + + The reason. + + + + + Gets the argument list to be provided to the test + + + + + Gets the expected result + + + + + Returns true if the result has been set + + + + + Gets the expected exception Type + + + + + Gets the FullName of the expected exception + + + + + Gets the name to be used for the test + + + + + Gets the description of the test + + + + + Gets a value indicating whether this is ignored. + + true if ignored; otherwise, false. + + + + Gets a value indicating whether this is explicit. + + true if explicit; otherwise, false. + + + + Gets the ignore reason. + + The ignore reason. + + + + Gets a list of categories associated with this test. + + + + + Gets the property dictionary for this test + + + + + Provide the context information of the current test + + + + + Constructs a TestContext using the provided context dictionary + + A context dictionary + + + + Get the current test context. This is created + as needed. The user may save the context for + use within a test, but it should not be used + outside the test for which it is created. + + + + + Gets a TestAdapter representing the currently executing test in this context. + + + + + Gets a ResultAdapter representing the current result for the test + executing in this context. + + + + + Gets the directory containing the current test assembly. + + + + + Gets the directory to be used for outputing files created + by this test run. + + + + + TestAdapter adapts a Test for consumption by + the user test code. + + + + + Constructs a TestAdapter for this context + + The context dictionary + + + + The name of the test. + + + + + The FullName of the test + + + + + The properties of the test. + + + + + ResultAdapter adapts a TestResult for consumption by + the user test code. + + + + + Construct a ResultAdapter for a context + + The context holding the result + + + + The TestState of current test. This maps to the ResultState + used in nunit.core and is subject to change in the future. + + + + + The TestStatus of current test. This enum will be used + in future versions of NUnit and so is to be preferred + to the TestState value. + + + + + Provides details about a test + + + + + Creates an instance of TestDetails + + The fixture that the test is a member of, if available. + The method that implements the test, if available. + The full name of the test. + A string representing the type of test, e.g. "Test Case". + Indicates if the test represents a suite of tests. + + + + The fixture that the test is a member of, if available. + + + + + The method that implements the test, if available. + + + + + The full name of the test. + + + + + A string representing the type of test, e.g. "Test Case". + + + + + Indicates if the test represents a suite of tests. + + + + + The ResultState enum indicates the result of running a test + + + + + The result is inconclusive + + + + + The test was not runnable. + + + + + The test has been skipped. + + + + + The test has been ignored. + + + + + The test succeeded + + + + + The test failed + + + + + The test encountered an unexpected exception + + + + + The test was cancelled by the user + + + + + The TestStatus enum indicates the result of running a test + + + + + The test was inconclusive + + + + + The test has skipped + + + + + The test succeeded + + + + + The test failed + + + + + Helper class with static methods used to supply constraints + that operate on strings. + + + + + Returns a constraint that succeeds if the actual + value contains the substring supplied as an argument. + + + + + Returns a constraint that fails if the actual + value contains the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value starts with the substring supplied as an argument. + + + + + Returns a constraint that fails if the actual + value starts with the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value ends with the substring supplied as an argument. + + + + + Returns a constraint that fails if the actual + value ends with the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value matches the Regex pattern supplied as an argument. + + + + + Returns a constraint that fails if the actual + value matches the pattern supplied as an argument. + + + + + Returns a ConstraintExpression, which will apply + the following constraint to all members of a collection, + succeeding if all of them succeed. + + + + + TextMessageWriter writes constraint descriptions and messages + in displayable form as a text stream. It tailors the display + of individual message components to form the standard message + format of NUnit assertion failure messages. + + + + + MessageWriter is the abstract base for classes that write + constraint descriptions and messages in some form. The + class has separate methods for writing various components + of a message, allowing implementations to tailor the + presentation as needed. + + + + + Construct a MessageWriter given a culture + + + + + Method to write single line message with optional args, usually + written to precede the general failure message. + + The message to be written + Any arguments used in formatting the message + + + + Method to write single line message with optional args, usually + written to precede the general failure message, at a givel + indentation level. + + The indentation level of the message + The message to be written + Any arguments used in formatting the message + + + + Display Expected and Actual lines for a constraint. This + is called by MessageWriter's default implementation of + WriteMessageTo and provides the generic two-line display. + + The constraint that failed + + + + Display Expected and Actual lines for given values. This + method may be called by constraints that need more control over + the display of actual and expected values than is provided + by the default implementation. + + The expected value + The actual value causing the failure + + + + Display Expected and Actual lines for given values, including + a tolerance value on the Expected line. + + The expected value + The actual value causing the failure + The tolerance within which the test was made + + + + Display the expected and actual string values on separate lines. + If the mismatch parameter is >=0, an additional line is displayed + line containing a caret that points to the mismatch point. + + The expected string value + The actual string value + The point at which the strings don't match or -1 + If true, case is ignored in locating the point where the strings differ + If true, the strings should be clipped to fit the line + + + + Writes the text for a connector. + + The connector. + + + + Writes the text for a predicate. + + The predicate. + + + + Writes the text for an expected value. + + The expected value. + + + + Writes the text for a modifier + + The modifier. + + + + Writes the text for an actual value. + + The actual value. + + + + Writes the text for a generalized value. + + The value. + + + + Writes the text for a collection value, + starting at a particular point, to a max length + + The collection containing elements to write. + The starting point of the elements to write + The maximum number of elements to write + + + + Abstract method to get the max line length + + + + + Prefix used for the expected value line of a message + + + + + Prefix used for the actual value line of a message + + + + + Length of a message prefix + + + + + Construct a TextMessageWriter + + + + + Construct a TextMessageWriter, specifying a user message + and optional formatting arguments. + + + + + + + Method to write single line message with optional args, usually + written to precede the general failure message, at a givel + indentation level. + + The indentation level of the message + The message to be written + Any arguments used in formatting the message + + + + Display Expected and Actual lines for a constraint. This + is called by MessageWriter's default implementation of + WriteMessageTo and provides the generic two-line display. + + The constraint that failed + + + + Display Expected and Actual lines for given values. This + method may be called by constraints that need more control over + the display of actual and expected values than is provided + by the default implementation. + + The expected value + The actual value causing the failure + + + + Display Expected and Actual lines for given values, including + a tolerance value on the expected line. + + The expected value + The actual value causing the failure + The tolerance within which the test was made + + + + Display the expected and actual string values on separate lines. + If the mismatch parameter is >=0, an additional line is displayed + line containing a caret that points to the mismatch point. + + The expected string value + The actual string value + The point at which the strings don't match or -1 + If true, case is ignored in string comparisons + If true, clip the strings to fit the max line length + + + + Writes the text for a connector. + + The connector. + + + + Writes the text for a predicate. + + The predicate. + + + + Write the text for a modifier. + + The modifier. + + + + Writes the text for an expected value. + + The expected value. + + + + Writes the text for an actual value. + + The actual value. + + + + Writes the text for a generalized value. + + The value. + + + + Writes the text for a collection value, + starting at a particular point, to a max length + + The collection containing elements to write. + The starting point of the elements to write + The maximum number of elements to write + + + + Write the generic 'Expected' line for a constraint + + The constraint that failed + + + + Write the generic 'Expected' line for a given value + + The expected value + + + + Write the generic 'Expected' line for a given value + and tolerance. + + The expected value + The tolerance within which the test was made + + + + Write the generic 'Actual' line for a constraint + + The constraint for which the actual value is to be written + + + + Write the generic 'Actual' line for a given value + + The actual value causing a failure + + + + Gets or sets the maximum line length for this writer + + + + + Helper class with properties and methods that supply + constraints that operate on exceptions. + + + + + Creates a constraint specifying the exact type of exception expected + + + + + Creates a constraint specifying the exact type of exception expected + + + + + Creates a constraint specifying the type of exception expected + + + + + Creates a constraint specifying the type of exception expected + + + + + Creates a constraint specifying an expected exception + + + + + Creates a constraint specifying an exception with a given InnerException + + + + + Creates a constraint specifying an expected TargetInvocationException + + + + + Creates a constraint specifying an expected TargetInvocationException + + + + + Creates a constraint specifying an expected TargetInvocationException + + + + + Creates a constraint specifying that no exception is thrown + + + + + Attribute used to apply a category to a test + + + + + The name of the category + + + + + Construct attribute for a given category based on + a name. The name may not contain the characters ',', + '+', '-' or '!'. However, this is not checked in the + constructor since it would cause an error to arise at + as the test was loaded without giving a clear indication + of where the problem is located. The error is handled + in NUnitFramework.cs by marking the test as not + runnable. + + The name of the category + + + + Protected constructor uses the Type name as the name + of the category. + + + + + The name of the category + + + + + Used to mark a field for use as a datapoint when executing a theory + within the same fixture that requires an argument of the field's Type. + + + + + Used to mark an array as containing a set of datapoints to be used + executing a theory within the same fixture that requires an argument + of the Type of the array elements. + + + + + Attribute used to provide descriptive text about a + test case or fixture. + + + + + Construct the attribute + + Text describing the test + + + + Gets the test description + + + + + Enumeration indicating how the expected message parameter is to be used + + + + Expect an exact match + + + Expect a message containing the parameter string + + + Match the regular expression provided as a parameter + + + Expect a message that starts with the parameter string + + + + ExpectedExceptionAttribute + + + + + + Constructor for a non-specific exception + + + + + Constructor for a given type of exception + + The type of the expected exception + + + + Constructor for a given exception name + + The full name of the expected exception + + + + Gets or sets the expected exception type + + + + + Gets or sets the full Type name of the expected exception + + + + + Gets or sets the expected message text + + + + + Gets or sets the user message displayed in case of failure + + + + + Gets or sets the type of match to be performed on the expected message + + + + + Gets the name of a method to be used as an exception handler + + + + + ExplicitAttribute marks a test or test fixture so that it will + only be run if explicitly executed from the gui or command line + or if it is included by use of a filter. The test will not be + run simply because an enclosing suite is run. + + + + + Default constructor + + + + + Constructor with a reason + + The reason test is marked explicit + + + + The reason test is marked explicit + + + + + Attribute used to mark a test that is to be ignored. + Ignored tests result in a warning message when the + tests are run. + + + + + Constructs the attribute without giving a reason + for ignoring the test. + + + + + Constructs the attribute giving a reason for ignoring the test + + The reason for ignoring the test + + + + The reason for ignoring a test + + + + + Abstract base for Attributes that are used to include tests + in the test run based on environmental settings. + + + + + Constructor with no included items specified, for use + with named property syntax. + + + + + Constructor taking one or more included items + + Comma-delimited list of included items + + + + Name of the item that is needed in order for + a test to run. Multiple itemss may be given, + separated by a comma. + + + + + Name of the item to be excluded. Multiple items + may be given, separated by a comma. + + + + + The reason for including or excluding the test + + + + + PlatformAttribute is used to mark a test fixture or an + individual method as applying to a particular platform only. + + + + + Constructor with no platforms specified, for use + with named property syntax. + + + + + Constructor taking one or more platforms + + Comma-deliminted list of platforms + + + + CultureAttribute is used to mark a test fixture or an + individual method as applying to a particular Culture only. + + + + + Constructor with no cultures specified, for use + with named property syntax. + + + + + Constructor taking one or more cultures + + Comma-deliminted list of cultures + + + + Marks a test to use a combinatorial join of any argument data + provided. NUnit will create a test case for every combination of + the arguments provided. This can result in a large number of test + cases and so should be used judiciously. This is the default join + type, so the attribute need not be used except as documentation. + + + + + PropertyAttribute is used to attach information to a test as a name/value pair.. + + + + + Construct a PropertyAttribute with a name and string value + + The name of the property + The property value + + + + Construct a PropertyAttribute with a name and int value + + The name of the property + The property value + + + + Construct a PropertyAttribute with a name and double value + + The name of the property + The property value + + + + Constructor for derived classes that set the + property dictionary directly. + + + + + Constructor for use by derived classes that use the + name of the type as the property name. Derived classes + must ensure that the Type of the property value is + a standard type supported by the BCL. Any custom + types will cause a serialization Exception when + in the client. + + + + + Gets the property dictionary for this attribute + + + + + Default constructor + + + + + Marks a test to use pairwise join of any argument data provided. + NUnit will attempt too excercise every pair of argument values at + least once, using as small a number of test cases as it can. With + only two arguments, this is the same as a combinatorial join. + + + + + Default constructor + + + + + Marks a test to use a sequential join of any argument data + provided. NUnit will use arguements for each parameter in + sequence, generating test cases up to the largest number + of argument values provided and using null for any arguments + for which it runs out of values. Normally, this should be + used with the same number of arguments for each parameter. + + + + + Default constructor + + + + + Summary description for MaxTimeAttribute. + + + + + Construct a MaxTimeAttribute, given a time in milliseconds. + + The maximum elapsed time in milliseconds + + + + RandomAttribute is used to supply a set of random values + to a single parameter of a parameterized test. + + + + + ValuesAttribute is used to provide literal arguments for + an individual parameter of a test. + + + + + Abstract base class for attributes that apply to parameters + and supply data for the parameter. + + + + + Gets the data to be provided to the specified parameter + + + + + The collection of data to be returned. Must + be set by any derived attribute classes. + We use an object[] so that the individual + elements may have their type changed in GetData + if necessary. + + + + + Construct with one argument + + + + + + Construct with two arguments + + + + + + + Construct with three arguments + + + + + + + + Construct with an array of arguments + + + + + + Get the collection of values to be used as arguments + + + + + Construct a set of doubles from 0.0 to 1.0, + specifying only the count. + + + + + + Construct a set of doubles from min to max + + + + + + + + Construct a set of ints from min to max + + + + + + + + Get the collection of values to be used as arguments + + + + + RangeAttribute is used to supply a range of values to an + individual parameter of a parameterized test. + + + + + Construct a range of ints using default step of 1 + + + + + + + Construct a range of ints specifying the step size + + + + + + + + Construct a range of longs + + + + + + + + Construct a range of doubles + + + + + + + + Construct a range of floats + + + + + + + + RepeatAttribute may be applied to test case in order + to run it multiple times. + + + + + Construct a RepeatAttribute + + The number of times to run the test + + + + RequiredAddinAttribute may be used to indicate the names of any addins + that must be present in order to run some or all of the tests in an + assembly. If the addin is not loaded, the entire assembly is marked + as NotRunnable. + + + + + Initializes a new instance of the class. + + The required addin. + + + + Gets the name of required addin. + + The required addin name. + + + + Summary description for SetCultureAttribute. + + + + + Construct given the name of a culture + + + + + + Summary description for SetUICultureAttribute. + + + + + Construct given the name of a culture + + + + + + SetUpAttribute is used in a TestFixture to identify a method + that is called immediately before each test is run. It is + also used in a SetUpFixture to identify the method that is + called once, before any of the subordinate tests are run. + + + + + Attribute used to mark a class that contains one-time SetUp + and/or TearDown methods that apply to all the tests in a + namespace or an assembly. + + + + + Attribute used to mark a static (shared in VB) property + that returns a list of tests. + + + + + Attribute used in a TestFixture to identify a method that is + called immediately after each test is run. It is also used + in a SetUpFixture to identify the method that is called once, + after all subordinate tests have run. In either case, the method + is guaranteed to be called, even if an exception is thrown. + + + + + Provide actions to execute before and after tests. + + + + + When implemented by an attribute, this interface implemented to provide actions to execute before and after tests. + + + + + Executed before each test is run + + Provides details about the test that is going to be run. + + + + Executed after each test is run + + Provides details about the test that has just been run. + + + + Provides the target for the action attribute + + The target for the action attribute + + + + Method called before each test + + Info about the test to be run + + + + Method called after each test + + Info about the test that was just run + + + + Gets or sets the ActionTargets for this attribute + + + + + Adding this attribute to a method within a + class makes the method callable from the NUnit test runner. There is a property + called Description which is optional which you can provide a more detailed test + description. This class cannot be inherited. + + + + [TestFixture] + public class Fixture + { + [Test] + public void MethodToTest() + {} + + [Test(Description = "more detailed description")] + publc void TestDescriptionMethod() + {} + } + + + + + + Descriptive text for this test + + + + + TestCaseAttribute is used to mark parameterized test cases + and provide them with their arguments. + + + + + Construct a TestCaseAttribute with a list of arguments. + This constructor is not CLS-Compliant + + + + + + Construct a TestCaseAttribute with a single argument + + + + + + Construct a TestCaseAttribute with a two arguments + + + + + + + Construct a TestCaseAttribute with a three arguments + + + + + + + + Gets the list of arguments to a test case + + + + + Gets or sets the expected result. Use + ExpectedResult by preference. + + The result. + + + + Gets or sets the expected result. + + The result. + + + + Gets a flag indicating whether an expected + result has been set. + + + + + Gets a list of categories associated with this test; + + + + + Gets or sets the category associated with this test. + May be a single category or a comma-separated list. + + + + + Gets or sets the expected exception. + + The expected exception. + + + + Gets or sets the name the expected exception. + + The expected name of the exception. + + + + Gets or sets the expected message of the expected exception + + The expected message of the exception. + + + + Gets or sets the type of match to be performed on the expected message + + + + + Gets or sets the description. + + The description. + + + + Gets or sets the name of the test. + + The name of the test. + + + + Gets or sets the ignored status of the test + + + + + Gets or sets the ignored status of the test + + + + + Gets or sets the explicit status of the test + + + + + Gets or sets the reason for not running the test + + + + + Gets or sets the reason for not running the test. + Set has the side effect of marking the test as ignored. + + The ignore reason. + + + + FactoryAttribute indicates the source to be used to + provide test cases for a test method. + + + + + Construct with the name of the data source, which must + be a property, field or method of the test class itself. + + An array of the names of the factories that will provide data + + + + Construct with a Type, which must implement IEnumerable + + The Type that will provide data + + + + Construct with a Type and name. + that don't support params arrays. + + The Type that will provide data + The name of the method, property or field that will provide data + + + + The name of a the method, property or fiend to be used as a source + + + + + A Type to be used as a source + + + + + Gets or sets the category associated with this test. + May be a single category or a comma-separated list. + + + + + [TestFixture] + public class ExampleClass + {} + + + + + Default constructor + + + + + Construct with a object[] representing a set of arguments. + In .NET 2.0, the arguments may later be separated into + type arguments and constructor arguments. + + + + + + Descriptive text for this fixture + + + + + Gets and sets the category for this fixture. + May be a comma-separated list of categories. + + + + + Gets a list of categories for this fixture + + + + + The arguments originally provided to the attribute + + + + + Gets or sets a value indicating whether this should be ignored. + + true if ignore; otherwise, false. + + + + Gets or sets the ignore reason. May set Ignored as a side effect. + + The ignore reason. + + + + Get or set the type arguments. If not set + explicitly, any leading arguments that are + Types are taken as type arguments. + + + + + Attribute used to identify a method that is + called before any tests in a fixture are run. + + + + + Attribute used to identify a method that is called after + all the tests in a fixture have run. The method is + guaranteed to be called, even if an exception is thrown. + + + + + Adding this attribute to a method within a + class makes the method callable from the NUnit test runner. There is a property + called Description which is optional which you can provide a more detailed test + description. This class cannot be inherited. + + + + [TestFixture] + public class Fixture + { + [Test] + public void MethodToTest() + {} + + [Test(Description = "more detailed description")] + publc void TestDescriptionMethod() + {} + } + + + + + + Used on a method, marks the test with a timeout value in milliseconds. + The test will be run in a separate thread and is cancelled if the timeout + is exceeded. Used on a method or assembly, sets the default timeout + for all contained test methods. + + + + + Construct a TimeoutAttribute given a time in milliseconds + + The timeout value in milliseconds + + + + Marks a test that must run in the STA, causing it + to run in a separate thread if necessary. + + On methods, you may also use STAThreadAttribute + to serve the same purpose. + + + + + Construct a RequiresSTAAttribute + + + + + Marks a test that must run in the MTA, causing it + to run in a separate thread if necessary. + + On methods, you may also use MTAThreadAttribute + to serve the same purpose. + + + + + Construct a RequiresMTAAttribute + + + + + Marks a test that must run on a separate thread. + + + + + Construct a RequiresThreadAttribute + + + + + Construct a RequiresThreadAttribute, specifying the apartment + + + + + ValueSourceAttribute indicates the source to be used to + provide data for one parameter of a test method. + + + + + Construct with the name of the factory - for use with languages + that don't support params arrays. + + The name of the data source to be used + + + + Construct with a Type and name - for use with languages + that don't support params arrays. + + The Type that will provide data + The name of the method, property or field that will provide data + + + + The name of a the method, property or fiend to be used as a source + + + + + A Type to be used as a source + + + + + AllItemsConstraint applies another constraint to each + item in a collection, succeeding if they all succeed. + + + + + Abstract base class used for prefixes + + + + + The Constraint class is the base of all built-in constraints + within NUnit. It provides the operator overloads used to combine + constraints. + + + + + The IConstraintExpression interface is implemented by all + complete and resolvable constraints and expressions. + + + + + Return the top-level constraint for this expression + + + + + + Static UnsetObject used to detect derived constraints + failing to set the actual value. + + + + + The actual value being tested against a constraint + + + + + The display name of this Constraint for use by ToString() + + + + + Argument fields used by ToString(); + + + + + The builder holding this constraint + + + + + Construct a constraint with no arguments + + + + + Construct a constraint with one argument + + + + + Construct a constraint with two arguments + + + + + Sets the ConstraintBuilder holding this constraint + + + + + Write the failure message to the MessageWriter provided + as an argument. The default implementation simply passes + the constraint and the actual value to the writer, which + then displays the constraint description and the value. + + Constraints that need to provide additional details, + such as where the error occured can override this. + + The MessageWriter on which to display the message + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Test whether the constraint is satisfied by an + ActualValueDelegate that returns the value to be tested. + The default implementation simply evaluates the delegate + but derived classes may override it to provide for delayed + processing. + + An + True for success, false for failure + + + + Test whether the constraint is satisfied by a given reference. + The default implementation simply dereferences the value but + derived classes may override it to provide for delayed processing. + + A reference to the value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Write the actual value for a failing constraint test to a + MessageWriter. The default implementation simply writes + the raw value of actual, leaving it to the writer to + perform any formatting. + + The writer on which the actual value is displayed + + + + Default override of ToString returns the constraint DisplayName + followed by any arguments within angle brackets. + + + + + + Returns the string representation of this constraint + + + + + This operator creates a constraint that is satisfied only if both + argument constraints are satisfied. + + + + + This operator creates a constraint that is satisfied if either + of the argument constraints is satisfied. + + + + + This operator creates a constraint that is satisfied if the + argument constraint is not satisfied. + + + + + Returns a DelayedConstraint with the specified delay time. + + The delay in milliseconds. + + + + + Returns a DelayedConstraint with the specified delay time + and polling interval. + + The delay in milliseconds. + The interval at which to test the constraint. + + + + + The display name of this Constraint for use by ToString(). + The default value is the name of the constraint with + trailing "Constraint" removed. Derived classes may set + this to another name in their constructors. + + + + + Returns a ConstraintExpression by appending And + to the current constraint. + + + + + Returns a ConstraintExpression by appending And + to the current constraint. + + + + + Returns a ConstraintExpression by appending Or + to the current constraint. + + + + + Class used to detect any derived constraints + that fail to set the actual value in their + Matches override. + + + + + The base constraint + + + + + Construct given a base constraint + + + + + + Construct an AllItemsConstraint on top of an existing constraint + + + + + + Apply the item constraint to each item in the collection, + failing if any item fails. + + + + + + + Write a description of this constraint to a MessageWriter + + + + + + AndConstraint succeeds only if both members succeed. + + + + + BinaryConstraint is the abstract base of all constraints + that combine two other constraints in some fashion. + + + + + The first constraint being combined + + + + + The second constraint being combined + + + + + Construct a BinaryConstraint from two other constraints + + The first constraint + The second constraint + + + + Create an AndConstraint from two other constraints + + The first constraint + The second constraint + + + + Apply both member constraints to an actual value, succeeding + succeeding only if both of them succeed. + + The actual value + True if the constraints both succeeded + + + + Write a description for this contraint to a MessageWriter + + The MessageWriter to receive the description + + + + Write the actual value for a failing constraint test to a + MessageWriter. The default implementation simply writes + the raw value of actual, leaving it to the writer to + perform any formatting. + + The writer on which the actual value is displayed + + + + AssignableFromConstraint is used to test that an object + can be assigned from a given Type. + + + + + TypeConstraint is the abstract base for constraints + that take a Type as their expected value. + + + + + The expected Type used by the constraint + + + + + Construct a TypeConstraint for a given Type + + + + + + Write the actual value for a failing constraint test to a + MessageWriter. TypeConstraints override this method to write + the name of the type. + + The writer on which the actual value is displayed + + + + Construct an AssignableFromConstraint for the type provided + + + + + + Test whether an object can be assigned from the specified type + + The object to be tested + True if the object can be assigned a value of the expected Type, otherwise false. + + + + Write a description of this constraint to a MessageWriter + + The MessageWriter to use + + + + AssignableToConstraint is used to test that an object + can be assigned to a given Type. + + + + + Construct an AssignableToConstraint for the type provided + + + + + + Test whether an object can be assigned to the specified type + + The object to be tested + True if the object can be assigned a value of the expected Type, otherwise false. + + + + Write a description of this constraint to a MessageWriter + + The MessageWriter to use + + + + AttributeConstraint tests that a specified attribute is present + on a Type or other provider and that the value of the attribute + satisfies some other constraint. + + + + + Constructs an AttributeConstraint for a specified attriute + Type and base constraint. + + + + + + + Determines whether the Type or other provider has the + expected attribute and if its value matches the + additional constraint specified. + + + + + Writes a description of the attribute to the specified writer. + + + + + Writes the actual value supplied to the specified writer. + + + + + Returns a string representation of the constraint. + + + + + AttributeExistsConstraint tests for the presence of a + specified attribute on a Type. + + + + + Constructs an AttributeExistsConstraint for a specific attribute Type + + + + + + Tests whether the object provides the expected attribute. + + A Type, MethodInfo, or other ICustomAttributeProvider + True if the expected attribute is present, otherwise false + + + + Writes the description of the constraint to the specified writer + + + + + BasicConstraint is the abstract base for constraints that + perform a simple comparison to a constant value. + + + + + Initializes a new instance of the class. + + The expected. + The description. + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + BinarySerializableConstraint tests whether + an object is serializable in binary format. + + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Write the actual value for a failing constraint test to a + MessageWriter. The default implementation simply writes + the raw value of actual, leaving it to the writer to + perform any formatting. + + The writer on which the actual value is displayed + + + + Returns the string representation + + + + + CollectionConstraint is the abstract base class for + constraints that operate on collections. + + + + + Construct an empty CollectionConstraint + + + + + Construct a CollectionConstraint + + + + + + Determines whether the specified enumerable is empty. + + The enumerable. + + true if the specified enumerable is empty; otherwise, false. + + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Protected method to be implemented by derived classes + + + + + + + CollectionContainsConstraint is used to test whether a collection + contains an expected object as a member. + + + + + CollectionItemsEqualConstraint is the abstract base class for all + collection constraints that apply some notion of item equality + as a part of their operation. + + + + + Construct an empty CollectionConstraint + + + + + Construct a CollectionConstraint + + + + + + Flag the constraint to use the supplied EqualityAdapter. + NOTE: For internal use only. + + The EqualityAdapter to use. + Self. + + + + Flag the constraint to use the supplied IComparer object. + + The IComparer object to use. + Self. + + + + Flag the constraint to use the supplied IComparer object. + + The IComparer object to use. + Self. + + + + Flag the constraint to use the supplied Comparison object. + + The IComparer object to use. + Self. + + + + Flag the constraint to use the supplied IEqualityComparer object. + + The IComparer object to use. + Self. + + + + Flag the constraint to use the supplied IEqualityComparer object. + + The IComparer object to use. + Self. + + + + Compares two collection members for equality + + + + + Return a new CollectionTally for use in making tests + + The collection to be included in the tally + + + + Flag the constraint to ignore case and return self. + + + + + Construct a CollectionContainsConstraint + + + + + + Test whether the expected item is contained in the collection + + + + + + + Write a descripton of the constraint to a MessageWriter + + + + + + CollectionEquivalentCOnstraint is used to determine whether two + collections are equivalent. + + + + + Construct a CollectionEquivalentConstraint + + + + + + Test whether two collections are equivalent + + + + + + + Write a description of this constraint to a MessageWriter + + + + + + CollectionOrderedConstraint is used to test whether a collection is ordered. + + + + + Construct a CollectionOrderedConstraint + + + + + Modifies the constraint to use an IComparer and returns self. + + + + + Modifies the constraint to use an IComparer<T> and returns self. + + + + + Modifies the constraint to use a Comparison<T> and returns self. + + + + + Modifies the constraint to test ordering by the value of + a specified property and returns self. + + + + + Test whether the collection is ordered + + + + + + + Write a description of the constraint to a MessageWriter + + + + + + Returns the string representation of the constraint. + + + + + + If used performs a reverse comparison + + + + + CollectionSubsetConstraint is used to determine whether + one collection is a subset of another + + + + + Construct a CollectionSubsetConstraint + + The collection that the actual value is expected to be a subset of + + + + Test whether the actual collection is a subset of + the expected collection provided. + + + + + + + Write a description of this constraint to a MessageWriter + + + + + + CollectionTally counts (tallies) the number of + occurences of each object in one or more enumerations. + + + + + Construct a CollectionTally object from a comparer and a collection + + + + + Try to remove an object from the tally + + The object to remove + True if successful, false if the object was not found + + + + Try to remove a set of objects from the tally + + The objects to remove + True if successful, false if any object was not found + + + + The number of objects remaining in the tally + + + + + ComparisonAdapter class centralizes all comparisons of + values in NUnit, adapting to the use of any provided + IComparer, IComparer<T> or Comparison<T> + + + + + Returns a ComparisonAdapter that wraps an IComparer + + + + + Returns a ComparisonAdapter that wraps an IComparer<T> + + + + + Returns a ComparisonAdapter that wraps a Comparison<T> + + + + + Compares two objects + + + + + Gets the default ComparisonAdapter, which wraps an + NUnitComparer object. + + + + + Construct a ComparisonAdapter for an IComparer + + + + + Compares two objects + + + + + + + + Construct a default ComparisonAdapter + + + + + ComparisonAdapter<T> extends ComparisonAdapter and + allows use of an IComparer<T> or Comparison<T> + to actually perform the comparison. + + + + + Construct a ComparisonAdapter for an IComparer<T> + + + + + Compare a Type T to an object + + + + + Construct a ComparisonAdapter for a Comparison<T> + + + + + Compare a Type T to an object + + + + + Abstract base class for constraints that compare values to + determine if one is greater than, equal to or less than + the other. This class supplies the Using modifiers. + + + + + ComparisonAdapter to be used in making the comparison + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + + + + Modifies the constraint to use an IComparer and returns self + + + + + Modifies the constraint to use an IComparer<T> and returns self + + + + + Modifies the constraint to use a Comparison<T> and returns self + + + + + Delegate used to delay evaluation of the actual value + to be used in evaluating a constraint + + + + + ConstraintBuilder maintains the stacks that are used in + processing a ConstraintExpression. An OperatorStack + is used to hold operators that are waiting for their + operands to be reognized. a ConstraintStack holds + input constraints as well as the results of each + operator applied. + + + + + Initializes a new instance of the class. + + + + + Appends the specified operator to the expression by first + reducing the operator stack and then pushing the new + operator on the stack. + + The operator to push. + + + + Appends the specified constraint to the expresson by pushing + it on the constraint stack. + + The constraint to push. + + + + Sets the top operator right context. + + The right context. + + + + Reduces the operator stack until the topmost item + precedence is greater than or equal to the target precedence. + + The target precedence. + + + + Resolves this instance, returning a Constraint. If the builder + is not currently in a resolvable state, an exception is thrown. + + The resolved constraint + + + + Gets a value indicating whether this instance is resolvable. + + + true if this instance is resolvable; otherwise, false. + + + + + OperatorStack is a type-safe stack for holding ConstraintOperators + + + + + Initializes a new instance of the class. + + The builder. + + + + Pushes the specified operator onto the stack. + + The op. + + + + Pops the topmost operator from the stack. + + + + + + Gets a value indicating whether this is empty. + + true if empty; otherwise, false. + + + + Gets the topmost operator without modifying the stack. + + The top. + + + + ConstraintStack is a type-safe stack for holding Constraints + + + + + Initializes a new instance of the class. + + The builder. + + + + Pushes the specified constraint. As a side effect, + the constraint's builder field is set to the + ConstraintBuilder owning this stack. + + The constraint. + + + + Pops this topmost constrait from the stack. + As a side effect, the constraint's builder + field is set to null. + + + + + + Gets a value indicating whether this is empty. + + true if empty; otherwise, false. + + + + Gets the topmost constraint without modifying the stack. + + The topmost constraint + + + + ConstraintExpression represents a compound constraint in the + process of being constructed from a series of syntactic elements. + + Individual elements are appended to the expression as they are + reognized. Once an actual Constraint is appended, the expression + returns a resolvable Constraint. + + + + + ConstraintExpressionBase is the abstract base class for the + ConstraintExpression class, which represents a + compound constraint in the process of being constructed + from a series of syntactic elements. + + NOTE: ConstraintExpressionBase is separate because the + ConstraintExpression class was generated in earlier + versions of NUnit. The two classes may be combined + in a future version. + + + + + The ConstraintBuilder holding the elements recognized so far + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the + class passing in a ConstraintBuilder, which may be pre-populated. + + The builder. + + + + Returns a string representation of the expression as it + currently stands. This should only be used for testing, + since it has the side-effect of resolving the expression. + + + + + + Appends an operator to the expression and returns the + resulting expression itself. + + + + + Appends a self-resolving operator to the expression and + returns a new ResolvableConstraintExpression. + + + + + Appends a constraint to the expression and returns that + constraint, which is associated with the current state + of the expression being built. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the + class passing in a ConstraintBuilder, which may be pre-populated. + + The builder. + + + + Returns a ConstraintExpression, which will apply + the following constraint to all members of a collection, + succeeding only if a specified number of them succeed. + + + + + Returns a new PropertyConstraintExpression, which will either + test for the existence of the named property on the object + being tested or apply any following constraint to that property. + + + + + Returns a new AttributeConstraint checking for the + presence of a particular attribute on an object. + + + + + Returns a new AttributeConstraint checking for the + presence of a particular attribute on an object. + + + + + Returns the constraint provided as an argument - used to allow custom + custom constraints to easily participate in the syntax. + + + + + Returns the constraint provided as an argument - used to allow custom + custom constraints to easily participate in the syntax. + + + + + Returns a constraint that tests two items for equality + + + + + Returns a constraint that tests that two references are the same object + + + + + Returns a constraint that tests whether the + actual value is greater than the suppled argument + + + + + Returns a constraint that tests whether the + actual value is greater than or equal to the suppled argument + + + + + Returns a constraint that tests whether the + actual value is greater than or equal to the suppled argument + + + + + Returns a constraint that tests whether the + actual value is less than the suppled argument + + + + + Returns a constraint that tests whether the + actual value is less than or equal to the suppled argument + + + + + Returns a constraint that tests whether the + actual value is less than or equal to the suppled argument + + + + + Returns a constraint that tests whether the actual + value is of the exact type supplied as an argument. + + + + + Returns a constraint that tests whether the actual + value is of the exact type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is of the type supplied as an argument or a derived type. + + + + + Returns a constraint that tests whether the actual value + is of the type supplied as an argument or a derived type. + + + + + Returns a constraint that tests whether the actual value + is of the type supplied as an argument or a derived type. + + + + + Returns a constraint that tests whether the actual value + is of the type supplied as an argument or a derived type. + + + + + Returns a constraint that tests whether the actual value + is assignable from the type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is assignable from the type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is assignable from the type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is assignable from the type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is a collection containing the same elements as the + collection supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is a subset of the collection supplied as an argument. + + + + + Returns a new CollectionContainsConstraint checking for the + presence of a particular object in the collection. + + + + + Returns a new CollectionContainsConstraint checking for the + presence of a particular object in the collection. + + + + + Returns a new ContainsConstraint. This constraint + will, in turn, make use of the appropriate second-level + constraint, depending on the type of the actual argument. + This overload is only used if the item sought is a string, + since any other type implies that we are looking for a + collection member. + + + + + Returns a constraint that succeeds if the actual + value contains the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value contains the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value starts with the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value starts with the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value ends with the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value ends with the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value matches the regular expression supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value matches the regular expression supplied as an argument. + + + + + Returns a constraint that tests whether the path provided + is the same as an expected path after canonicalization. + + + + + Returns a constraint that tests whether the path provided + is the same path or under an expected path after canonicalization. + + + + + Returns a constraint that tests whether the path provided + is the same path or under an expected path after canonicalization. + + + + + Returns a constraint that tests whether the actual value falls + within a specified range. + + + + + Returns a ConstraintExpression that negates any + following constraint. + + + + + Returns a ConstraintExpression that negates any + following constraint. + + + + + Returns a ConstraintExpression, which will apply + the following constraint to all members of a collection, + succeeding if all of them succeed. + + + + + Returns a ConstraintExpression, which will apply + the following constraint to all members of a collection, + succeeding if at least one of them succeeds. + + + + + Returns a ConstraintExpression, which will apply + the following constraint to all members of a collection, + succeeding if all of them fail. + + + + + Returns a new ConstraintExpression, which will apply the following + constraint to the Length property of the object being tested. + + + + + Returns a new ConstraintExpression, which will apply the following + constraint to the Count property of the object being tested. + + + + + Returns a new ConstraintExpression, which will apply the following + constraint to the Message property of the object being tested. + + + + + Returns a new ConstraintExpression, which will apply the following + constraint to the InnerException property of the object being tested. + + + + + With is currently a NOP - reserved for future use. + + + + + Returns a constraint that tests for null + + + + + Returns a constraint that tests for True + + + + + Returns a constraint that tests for False + + + + + Returns a constraint that tests for a positive value + + + + + Returns a constraint that tests for a negative value + + + + + Returns a constraint that tests for NaN + + + + + Returns a constraint that tests for empty + + + + + Returns a constraint that tests whether a collection + contains all unique items. + + + + + Returns a constraint that tests whether an object graph is serializable in binary format. + + + + + Returns a constraint that tests whether an object graph is serializable in xml format. + + + + + Returns a constraint that tests whether a collection is ordered + + + + + ContainsConstraint tests a whether a string contains a substring + or a collection contains an object. It postpones the decision of + which test to use until the type of the actual argument is known. + This allows testing whether a string is contained in a collection + or as a substring of another string using the same syntax. + + + + + Initializes a new instance of the class. + + The expected. + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Flag the constraint to use the supplied IComparer object. + + The IComparer object to use. + Self. + + + + Flag the constraint to use the supplied IComparer object. + + The IComparer object to use. + Self. + + + + Flag the constraint to use the supplied Comparison object. + + The IComparer object to use. + Self. + + + + Flag the constraint to use the supplied IEqualityComparer object. + + The IComparer object to use. + Self. + + + + Flag the constraint to use the supplied IEqualityComparer object. + + The IComparer object to use. + Self. + + + + Flag the constraint to ignore case and return self. + + + + + Applies a delay to the match so that a match can be evaluated in the future. + + + + + Creates a new DelayedConstraint + + The inner constraint two decorate + The time interval after which the match is performed + If the value of is less than 0 + + + + Creates a new DelayedConstraint + + The inner constraint two decorate + The time interval after which the match is performed + The time interval used for polling + If the value of is less than 0 + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for if the base constraint fails, false if it succeeds + + + + Test whether the constraint is satisfied by a delegate + + The delegate whose value is to be tested + True for if the base constraint fails, false if it succeeds + + + + Test whether the constraint is satisfied by a given reference. + Overridden to wait for the specified delay period before + calling the base constraint with the dereferenced value. + + A reference to the value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Write the actual value for a failing constraint test to a MessageWriter. + + The writer on which the actual value is displayed + + + + Returns the string representation of the constraint. + + + + + EmptyCollectionConstraint tests whether a collection is empty. + + + + + Check that the collection is empty + + + + + + + Write the constraint description to a MessageWriter + + + + + + EmptyConstraint tests a whether a string or collection is empty, + postponing the decision about which test is applied until the + type of the actual argument is known. + + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + EmptyDirectoryConstraint is used to test that a directory is empty + + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Write the actual value for a failing constraint test to a + MessageWriter. The default implementation simply writes + the raw value of actual, leaving it to the writer to + perform any formatting. + + The writer on which the actual value is displayed + + + + EmptyStringConstraint tests whether a string is empty. + + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + EndsWithConstraint can test whether a string ends + with an expected substring. + + + + + StringConstraint is the abstract base for constraints + that operate on strings. It supports the IgnoreCase + modifier for string operations. + + + + + The expected value + + + + + Indicates whether tests should be case-insensitive + + + + + Constructs a StringConstraint given an expected value + + The expected value + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Test whether the constraint is satisfied by a given string + + The string to be tested + True for success, false for failure + + + + Modify the constraint to ignore case in matching. + + + + + Initializes a new instance of the class. + + The expected string + + + + Test whether the constraint is matched by the actual value. + This is a template method, which calls the IsMatch method + of the derived class. + + + + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + EqualConstraint is able to compare an actual value with the + expected value provided in its constructor. Two objects are + considered equal if both are null, or if both have the same + value. NUnit has special semantics for some object types. + + + + + If true, strings in error messages will be clipped + + + + + NUnitEqualityComparer used to test equality. + + + + + Initializes a new instance of the class. + + The expected value. + + + + Flag the constraint to use a tolerance when determining equality. + + Tolerance value to be used + Self. + + + + Flag the constraint to use the supplied IComparer object. + + The IComparer object to use. + Self. + + + + Flag the constraint to use the supplied IComparer object. + + The IComparer object to use. + Self. + + + + Flag the constraint to use the supplied IComparer object. + + The IComparer object to use. + Self. + + + + Flag the constraint to use the supplied Comparison object. + + The IComparer object to use. + Self. + + + + Flag the constraint to use the supplied IEqualityComparer object. + + The IComparer object to use. + Self. + + + + Flag the constraint to use the supplied IEqualityComparer object. + + The IComparer object to use. + Self. + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write a failure message. Overridden to provide custom + failure messages for EqualConstraint. + + The MessageWriter to write to + + + + Write description of this constraint + + The MessageWriter to write to + + + + Display the failure information for two collections that did not match. + + The MessageWriter on which to display + The expected collection. + The actual collection + The depth of this failure in a set of nested collections + + + + Displays a single line showing the types and sizes of the expected + and actual enumerations, collections or arrays. If both are identical, + the value is only shown once. + + The MessageWriter on which to display + The expected collection or array + The actual collection or array + The indentation level for the message line + + + + Displays a single line showing the point in the expected and actual + arrays at which the comparison failed. If the arrays have different + structures or dimensions, both values are shown. + + The MessageWriter on which to display + The expected array + The actual array + Index of the failure point in the underlying collections + The indentation level for the message line + + + + Display the failure information for two IEnumerables that did not match. + + The MessageWriter on which to display + The expected enumeration. + The actual enumeration + The depth of this failure in a set of nested collections + + + + Flag the constraint to ignore case and return self. + + + + + Flag the constraint to suppress string clipping + and return self. + + + + + Flag the constraint to compare arrays as collections + and return self. + + + + + Switches the .Within() modifier to interpret its tolerance as + a distance in representable values (see remarks). + + Self. + + Ulp stands for "unit in the last place" and describes the minimum + amount a given value can change. For any integers, an ulp is 1 whole + digit. For floating point values, the accuracy of which is better + for smaller numbers and worse for larger numbers, an ulp depends + on the size of the number. Using ulps for comparison of floating + point results instead of fixed tolerances is safer because it will + automatically compensate for the added inaccuracy of larger numbers. + + + + + Switches the .Within() modifier to interpret its tolerance as + a percentage that the actual values is allowed to deviate from + the expected value. + + Self + + + + Causes the tolerance to be interpreted as a TimeSpan in days. + + Self + + + + Causes the tolerance to be interpreted as a TimeSpan in hours. + + Self + + + + Causes the tolerance to be interpreted as a TimeSpan in minutes. + + Self + + + + Causes the tolerance to be interpreted as a TimeSpan in seconds. + + Self + + + + Causes the tolerance to be interpreted as a TimeSpan in milliseconds. + + Self + + + + Causes the tolerance to be interpreted as a TimeSpan in clock ticks. + + Self + + + + EqualityAdapter class handles all equality comparisons + that use an IEqualityComparer, IEqualityComparer<T> + or a ComparisonAdapter. + + + + + Compares two objects, returning true if they are equal + + + + + Returns true if the two objects can be compared by this adapter. + The base adapter cannot handle IEnumerables except for strings. + + + + + Returns an EqualityAdapter that wraps an IComparer. + + + + + Returns an EqualityAdapter that wraps an IEqualityComparer. + + + + + Returns an EqualityAdapter that wraps an IEqualityComparer<T>. + + + + + Returns an EqualityAdapter that wraps an IComparer<T>. + + + + + Returns an EqualityAdapter that wraps a Comparison<T>. + + + + + EqualityAdapter that wraps an IComparer. + + + + + Returns true if the two objects can be compared by this adapter. + Generic adapter requires objects of the specified type. + + + + + EqualityAdapter that wraps an IComparer. + + + + + EqualityAdapterList represents a list of EqualityAdapters + in a common class across platforms. + + + + + ExactCountConstraint applies another constraint to each + item in a collection, succeeding only if a specified + number of items succeed. + + + + + Construct an ExactCountConstraint on top of an existing constraint + + + + + + + Apply the item constraint to each item in the collection, + succeeding only if the expected number of items pass. + + + + + + + Write a description of this constraint to a MessageWriter + + + + + + ExactTypeConstraint is used to test that an object + is of the exact type provided in the constructor + + + + + Construct an ExactTypeConstraint for a given Type + + The expected Type. + + + + Test that an object is of the exact type specified + + The actual value. + True if the tested object is of the exact type provided, otherwise false. + + + + Write the description of this constraint to a MessageWriter + + The MessageWriter to use + + + + ExceptionTypeConstraint is a special version of ExactTypeConstraint + used to provided detailed info about the exception thrown in + an error message. + + + + + Constructs an ExceptionTypeConstraint + + + + + Write the actual value for a failing constraint test to a + MessageWriter. Overriden to write additional information + in the case of an Exception. + + The MessageWriter to use + + + + FailurePoint class represents one point of failure + in an equality test. + + + + + The location of the failure + + + + + The expected value + + + + + The actual value + + + + + Indicates whether the expected value is valid + + + + + Indicates whether the actual value is valid + + + + + FailurePointList represents a set of FailurePoints + in a cross-platform way. + + + + + FalseConstraint tests that the actual value is false + + + + + Initializes a new instance of the class. + + + + Helper routines for working with floating point numbers + + + The floating point comparison code is based on this excellent article: + http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm + + + "ULP" means Unit in the Last Place and in the context of this library refers to + the distance between two adjacent floating point numbers. IEEE floating point + numbers can only represent a finite subset of natural numbers, with greater + accuracy for smaller numbers and lower accuracy for very large numbers. + + + If a comparison is allowed "2 ulps" of deviation, that means the values are + allowed to deviate by up to 2 adjacent floating point values, which might be + as low as 0.0000001 for small numbers or as high as 10.0 for large numbers. + + + + + Compares two floating point values for equality + First floating point value to be compared + Second floating point value t be compared + + Maximum number of representable floating point values that are allowed to + be between the left and the right floating point values + + True if both numbers are equal or close to being equal + + + Floating point values can only represent a finite subset of natural numbers. + For example, the values 2.00000000 and 2.00000024 can be stored in a float, + but nothing inbetween them. + + + This comparison will count how many possible floating point values are between + the left and the right number. If the number of possible values between both + numbers is less than or equal to maxUlps, then the numbers are considered as + being equal. + + + Implementation partially follows the code outlined here: + http://www.anttirt.net/2007/08/19/proper-floating-point-comparisons/ + + + + + Compares two double precision floating point values for equality + First double precision floating point value to be compared + Second double precision floating point value t be compared + + Maximum number of representable double precision floating point values that are + allowed to be between the left and the right double precision floating point values + + True if both numbers are equal or close to being equal + + + Double precision floating point values can only represent a limited series of + natural numbers. For example, the values 2.0000000000000000 and 2.0000000000000004 + can be stored in a double, but nothing inbetween them. + + + This comparison will count how many possible double precision floating point + values are between the left and the right number. If the number of possible + values between both numbers is less than or equal to maxUlps, then the numbers + are considered as being equal. + + + Implementation partially follows the code outlined here: + http://www.anttirt.net/2007/08/19/proper-floating-point-comparisons/ + + + + + + Reinterprets the memory contents of a floating point value as an integer value + + + Floating point value whose memory contents to reinterpret + + + The memory contents of the floating point value interpreted as an integer + + + + + Reinterprets the memory contents of a double precision floating point + value as an integer value + + + Double precision floating point value whose memory contents to reinterpret + + + The memory contents of the double precision floating point value + interpreted as an integer + + + + + Reinterprets the memory contents of an integer as a floating point value + + Integer value whose memory contents to reinterpret + + The memory contents of the integer value interpreted as a floating point value + + + + + Reinterprets the memory contents of an integer value as a double precision + floating point value + + Integer whose memory contents to reinterpret + + The memory contents of the integer interpreted as a double precision + floating point value + + + + Union of a floating point variable and an integer + + + The union's value as a floating point variable + + + The union's value as an integer + + + The union's value as an unsigned integer + + + Union of a double precision floating point variable and a long + + + The union's value as a double precision floating point variable + + + The union's value as a long + + + The union's value as an unsigned long + + + + Tests whether a value is greater than the value supplied to its constructor + + + + + The value against which a comparison is to be made + + + + + Initializes a new instance of the class. + + The expected value. + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Tests whether a value is greater than or equal to the value supplied to its constructor + + + + + The value against which a comparison is to be made + + + + + Initializes a new instance of the class. + + The expected value. + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + InstanceOfTypeConstraint is used to test that an object + is of the same type provided or derived from it. + + + + + Construct an InstanceOfTypeConstraint for the type provided + + The expected Type + + + + Test whether an object is of the specified type or a derived type + + The object to be tested + True if the object is of the provided type or derives from it, otherwise false. + + + + Write a description of this constraint to a MessageWriter + + The MessageWriter to use + + + + Tests whether a value is less than the value supplied to its constructor + + + + + The value against which a comparison is to be made + + + + + Initializes a new instance of the class. + + The expected value. + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Tests whether a value is less than or equal to the value supplied to its constructor + + + + + The value against which a comparison is to be made + + + + + Initializes a new instance of the class. + + The expected value. + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Static methods used in creating messages + + + + + Static string used when strings are clipped + + + + + Returns the representation of a type as used in NUnitLite. + This is the same as Type.ToString() except for arrays, + which are displayed with their declared sizes. + + + + + + + Converts any control characters in a string + to their escaped representation. + + The string to be converted + The converted string + + + + Return the a string representation for a set of indices into an array + + Array of indices for which a string is needed + + + + Get an array of indices representing the point in a enumerable, + collection or array corresponding to a single int index into the + collection. + + The collection to which the indices apply + Index in the collection + Array of indices + + + + Clip a string to a given length, starting at a particular offset, returning the clipped + string with ellipses representing the removed parts + + The string to be clipped + The maximum permitted length of the result string + The point at which to start clipping + The clipped string + + + + Clip the expected and actual strings in a coordinated fashion, + so that they may be displayed together. + + + + + + + + + Shows the position two strings start to differ. Comparison + starts at the start index. + + The expected string + The actual string + The index in the strings at which comparison should start + Boolean indicating whether case should be ignored + -1 if no mismatch found, or the index where mismatch found + + + + NaNConstraint tests that the actual value is a double or float NaN + + + + + Test that the actual value is an NaN + + + + + + + Write the constraint description to a specified writer + + + + + + NoItemConstraint applies another constraint to each + item in a collection, failing if any of them succeeds. + + + + + Construct a NoItemConstraint on top of an existing constraint + + + + + + Apply the item constraint to each item in the collection, + failing if any item fails. + + + + + + + Write a description of this constraint to a MessageWriter + + + + + + NotConstraint negates the effect of some other constraint + + + + + Initializes a new instance of the class. + + The base constraint to be negated. + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for if the base constraint fails, false if it succeeds + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Write the actual value for a failing constraint test to a MessageWriter. + + The writer on which the actual value is displayed + + + + NullConstraint tests that the actual value is null + + + + + Initializes a new instance of the class. + + + + + NullEmptyStringConstraint tests whether a string is either null or empty. + + + + + Constructs a new NullOrEmptyStringConstraint + + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + The Numerics class contains common operations on numeric values. + + + + + Checks the type of the object, returning true if + the object is a numeric type. + + The object to check + true if the object is a numeric type + + + + Checks the type of the object, returning true if + the object is a floating point numeric type. + + The object to check + true if the object is a floating point numeric type + + + + Checks the type of the object, returning true if + the object is a fixed point numeric type. + + The object to check + true if the object is a fixed point numeric type + + + + Test two numeric values for equality, performing the usual numeric + conversions and using a provided or default tolerance. If the tolerance + provided is Empty, this method may set it to a default tolerance. + + The expected value + The actual value + A reference to the tolerance in effect + True if the values are equal + + + + Compare two numeric values, performing the usual numeric conversions. + + The expected value + The actual value + The relationship of the values to each other + + + + NUnitComparer encapsulates NUnit's default behavior + in comparing two objects. + + + + + Compares two objects + + + + + + + + Returns the default NUnitComparer. + + + + + Generic version of NUnitComparer + + + + + + Compare two objects of the same type + + + + + NUnitEqualityComparer encapsulates NUnit's handling of + equality tests between objects. + + + + + + + + + + Compares two objects for equality within a tolerance + + The first object to compare + The second object to compare + The tolerance to use in the comparison + + + + + If true, all string comparisons will ignore case + + + + + If true, arrays will be treated as collections, allowing + those of different dimensions to be compared + + + + + Comparison objects used in comparisons for some constraints. + + + + + List of points at which a failure occured. + + + + + RecursionDetector used to check for recursion when + evaluating self-referencing enumerables. + + + + + Compares two objects for equality within a tolerance, setting + the tolerance to the actual tolerance used if an empty + tolerance is supplied. + + + + + Helper method to compare two arrays + + + + + Method to compare two DirectoryInfo objects + + first directory to compare + second directory to compare + true if equivalent, false if not + + + + Returns the default NUnitEqualityComparer + + + + + Gets and sets a flag indicating whether case should + be ignored in determining equality. + + + + + Gets and sets a flag indicating that arrays should be + compared as collections, without regard to their shape. + + + + + Gets the list of external comparers to be used to + test for equality. They are applied to members of + collections, in place of NUnit's own logic. + + + + + Gets the list of failure points for the last Match performed. + The list consists of objects to be interpreted by the caller. + This generally means that the caller may only make use of + objects it has placed on the list at a particular depthy. + + + + + RecursionDetector detects when a comparison + between two enumerables has reached a point + where the same objects that were previously + compared are again being compared. This allows + the caller to stop the comparison if desired. + + + + + Check whether two objects have previously + been compared, returning true if they have. + The two objects are remembered, so that a + second call will always return true. + + + + + OrConstraint succeeds if either member succeeds + + + + + Create an OrConstraint from two other constraints + + The first constraint + The second constraint + + + + Apply the member constraints to an actual value, succeeding + succeeding as soon as one of them succeeds. + + The actual value + True if either constraint succeeded + + + + Write a description for this contraint to a MessageWriter + + The MessageWriter to receive the description + + + + PathConstraint serves as the abstract base of constraints + that operate on paths and provides several helper methods. + + + + + The expected path used in the constraint + + + + + Flag indicating whether a caseInsensitive comparison should be made + + + + + Construct a PathConstraint for a give expected path + + The expected path + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Returns true if the expected path and actual path match + + + + + Returns the string representation of this constraint + + + + + Transform the provided path to its canonical form so that it + may be more easily be compared with other paths. + + The original path + The path in canonical form + + + + Test whether one path in canonical form is under another. + + The first path - supposed to be the parent path + The second path - supposed to be the child path + Indicates whether case should be ignored + + + + + Modifies the current instance to be case-insensitve + and returns it. + + + + + Modifies the current instance to be case-sensitve + and returns it. + + + + + Predicate constraint wraps a Predicate in a constraint, + returning success if the predicate is true. + + + + + Construct a PredicateConstraint from a predicate + + + + + Determines whether the predicate succeeds when applied + to the actual value. + + + + + Writes the description to a MessageWriter + + + + + PropertyConstraint extracts a named property and uses + its value as the actual value for a chained constraint. + + + + + Initializes a new instance of the class. + + The name. + The constraint to apply to the property. + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Write the actual value for a failing constraint test to a + MessageWriter. The default implementation simply writes + the raw value of actual, leaving it to the writer to + perform any formatting. + + The writer on which the actual value is displayed + + + + Returns the string representation of the constraint. + + + + + + PropertyExistsConstraint tests that a named property + exists on the object provided through Match. + + Originally, PropertyConstraint provided this feature + in addition to making optional tests on the vaue + of the property. The two constraints are now separate. + + + + + Initializes a new instance of the class. + + The name of the property. + + + + Test whether the property exists for a given object + + The object to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Write the actual value for a failing constraint test to a + MessageWriter. + + The writer on which the actual value is displayed + + + + Returns the string representation of the constraint. + + + + + + RangeConstraint tests whether two values are within a + specified range. + + + + + Initializes a new instance of the class. + + From. + To. + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + RegexConstraint can test whether a string matches + the pattern provided. + + + + + Initializes a new instance of the class. + + The pattern. + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + ResolvableConstraintExpression is used to represent a compound + constraint being constructed at a point where the last operator + may either terminate the expression or may have additional + qualifying constraints added to it. + + It is used, for example, for a Property element or for + an Exception element, either of which may be optionally + followed by constraints that apply to the property or + exception. + + + + + Create a new instance of ResolvableConstraintExpression + + + + + Create a new instance of ResolvableConstraintExpression, + passing in a pre-populated ConstraintBuilder. + + + + + Resolve the current expression to a Constraint + + + + + This operator creates a constraint that is satisfied only if both + argument constraints are satisfied. + + + + + This operator creates a constraint that is satisfied only if both + argument constraints are satisfied. + + + + + This operator creates a constraint that is satisfied only if both + argument constraints are satisfied. + + + + + This operator creates a constraint that is satisfied if either + of the argument constraints is satisfied. + + + + + This operator creates a constraint that is satisfied if either + of the argument constraints is satisfied. + + + + + This operator creates a constraint that is satisfied if either + of the argument constraints is satisfied. + + + + + This operator creates a constraint that is satisfied if the + argument constraint is not satisfied. + + + + + Appends an And Operator to the expression + + + + + Appends an Or operator to the expression. + + + + + ReusableConstraint wraps a constraint expression after + resolving it so that it can be reused consistently. + + + + + Construct a ReusableConstraint from a constraint expression + + The expression to be resolved and reused + + + + Converts a constraint to a ReusableConstraint + + The constraint to be converted + A ReusableConstraint + + + + Returns the string representation of the constraint. + + A string representing the constraint + + + + Resolves the ReusableConstraint by returning the constraint + that it originally wrapped. + + A resolved constraint + + + + SameAsConstraint tests whether an object is identical to + the object passed to its constructor + + + + + Initializes a new instance of the class. + + The expected object. + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Summary description for SamePathConstraint. + + + + + Initializes a new instance of the class. + + The expected path + + + + Test whether the constraint is satisfied by a given value + + The expected path + The actual path + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + SamePathOrUnderConstraint tests that one path is under another + + + + + Initializes a new instance of the class. + + The expected path + + + + Test whether the constraint is satisfied by a given value + + The expected path + The actual path + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + SomeItemsConstraint applies another constraint to each + item in a collection, succeeding if any of them succeeds. + + + + + Construct a SomeItemsConstraint on top of an existing constraint + + + + + + Apply the item constraint to each item in the collection, + succeeding if any item succeeds. + + + + + + + Write a description of this constraint to a MessageWriter + + + + + + StartsWithConstraint can test whether a string starts + with an expected substring. + + + + + Initializes a new instance of the class. + + The expected string + + + + Test whether the constraint is matched by the actual value. + This is a template method, which calls the IsMatch method + of the derived class. + + + + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + SubPathConstraint tests that the actual path is under the expected path + + + + + Initializes a new instance of the class. + + The expected path + + + + Test whether the constraint is satisfied by a given value + + The expected path + The actual path + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + SubstringConstraint can test whether a string contains + the expected substring. + + + + + Initializes a new instance of the class. + + The expected. + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + ThrowsConstraint is used to test the exception thrown by + a delegate by applying a constraint to it. + + + + + Initializes a new instance of the class, + using a constraint to be applied to the exception. + + A constraint to apply to the caught exception. + + + + Executes the code of the delegate and captures any exception. + If a non-null base constraint was provided, it applies that + constraint to the exception. + + A delegate representing the code to be tested + True if an exception is thrown and the constraint succeeds, otherwise false + + + + Converts an ActualValueDelegate to a TestDelegate + before calling the primary overload. + + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Write the actual value for a failing constraint test to a + MessageWriter. The default implementation simply writes + the raw value of actual, leaving it to the writer to + perform any formatting. + + The writer on which the actual value is displayed + + + + Returns the string representation of this constraint + + + + + Get the actual exception thrown - used by Assert.Throws. + + + + + ThrowsNothingConstraint tests that a delegate does not + throw an exception. + + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True if no exception is thrown, otherwise false + + + + Test whether the constraint is satisfied by a given delegate + + Delegate returning the value to be tested + True if no exception is thrown, otherwise false + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Write the actual value for a failing constraint test to a + MessageWriter. Overridden in ThrowsNothingConstraint to write + information about the exception that was actually caught. + + The writer on which the actual value is displayed + + + + The Tolerance class generalizes the notion of a tolerance + within which an equality test succeeds. Normally, it is + used with numeric types, but it can be used with any + type that supports taking a difference between two + objects and comparing that difference to a value. + + + + + Constructs a linear tolerance of a specdified amount + + + + + Constructs a tolerance given an amount and ToleranceMode + + + + + Tests that the current Tolerance is linear with a + numeric value, throwing an exception if it is not. + + + + + Returns an empty Tolerance object, equivalent to + specifying no tolerance. In most cases, it results + in an exact match but for floats and doubles a + default tolerance may be used. + + + + + Returns a zero Tolerance object, equivalent to + specifying an exact match. + + + + + Gets the ToleranceMode for the current Tolerance + + + + + Gets the value of the current Tolerance instance. + + + + + Returns a new tolerance, using the current amount as a percentage. + + + + + Returns a new tolerance, using the current amount in Ulps. + + + + + Returns a new tolerance with a TimeSpan as the amount, using + the current amount as a number of days. + + + + + Returns a new tolerance with a TimeSpan as the amount, using + the current amount as a number of hours. + + + + + Returns a new tolerance with a TimeSpan as the amount, using + the current amount as a number of minutes. + + + + + Returns a new tolerance with a TimeSpan as the amount, using + the current amount as a number of seconds. + + + + + Returns a new tolerance with a TimeSpan as the amount, using + the current amount as a number of milliseconds. + + + + + Returns a new tolerance with a TimeSpan as the amount, using + the current amount as a number of clock ticks. + + + + + Returns true if the current tolerance is empty. + + + + + Modes in which the tolerance value for a comparison can be interpreted. + + + + + The tolerance was created with a value, without specifying + how the value would be used. This is used to prevent setting + the mode more than once and is generally changed to Linear + upon execution of the test. + + + + + The tolerance is used as a numeric range within which + two compared values are considered to be equal. + + + + + Interprets the tolerance as the percentage by which + the two compared values my deviate from each other. + + + + + Compares two values based in their distance in + representable numbers. + + + + + TrueConstraint tests that the actual value is true + + + + + Initializes a new instance of the class. + + + + + UniqueItemsConstraint tests whether all the items in a + collection are unique. + + + + + Check that all items are unique. + + + + + + + Write a description of this constraint to a MessageWriter + + + + + + XmlSerializableConstraint tests whether + an object is serializable in XML format. + + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Write the actual value for a failing constraint test to a + MessageWriter. The default implementation simply writes + the raw value of actual, leaving it to the writer to + perform any formatting. + + The writer on which the actual value is displayed + + + + Returns the string representation of this constraint + + + + + Represents a constraint that succeeds if all the + members of a collection match a base constraint. + + + + + Abstract base for operators that indicate how to + apply a constraint to items in a collection. + + + + + PrefixOperator takes a single constraint and modifies + it's action in some way. + + + + + The ConstraintOperator class is used internally by a + ConstraintBuilder to represent an operator that + modifies or combines constraints. + + Constraint operators use left and right precedence + values to determine whether the top operator on the + stack should be reduced before pushing a new operator. + + + + + The precedence value used when the operator + is about to be pushed to the stack. + + + + + The precedence value used when the operator + is on the top of the stack. + + + + + Reduce produces a constraint from the operator and + any arguments. It takes the arguments from the constraint + stack and pushes the resulting constraint on it. + + + + + + The syntax element preceding this operator + + + + + The syntax element folowing this operator + + + + + The precedence value used when the operator + is about to be pushed to the stack. + + + + + The precedence value used when the operator + is on the top of the stack. + + + + + Reduce produces a constraint from the operator and + any arguments. It takes the arguments from the constraint + stack and pushes the resulting constraint on it. + + + + + + Returns the constraint created by applying this + prefix to another constraint. + + + + + + + Constructs a CollectionOperator + + + + + Returns a constraint that will apply the argument + to the members of a collection, succeeding if + they all succeed. + + + + + Operator that requires both it's arguments to succeed + + + + + Abstract base class for all binary operators + + + + + Reduce produces a constraint from the operator and + any arguments. It takes the arguments from the constraint + stack and pushes the resulting constraint on it. + + + + + + Abstract method that produces a constraint by applying + the operator to its left and right constraint arguments. + + + + + Gets the left precedence of the operator + + + + + Gets the right precedence of the operator + + + + + Construct an AndOperator + + + + + Apply the operator to produce an AndConstraint + + + + + Operator that tests for the presence of a particular attribute + on a type and optionally applies further tests to the attribute. + + + + + Abstract base class for operators that are able to reduce to a + constraint whether or not another syntactic element follows. + + + + + Construct an AttributeOperator for a particular Type + + The Type of attribute tested + + + + Reduce produces a constraint from the operator and + any arguments. It takes the arguments from the constraint + stack and pushes the resulting constraint on it. + + + + + Represents a constraint that succeeds if the specified + count of members of a collection match a base constraint. + + + + + Construct an ExactCountOperator for a specified count + + The expected count + + + + Returns a constraint that will apply the argument + to the members of a collection, succeeding if + none of them succeed. + + + + + Represents a constraint that succeeds if none of the + members of a collection match a base constraint. + + + + + Returns a constraint that will apply the argument + to the members of a collection, succeeding if + none of them succeed. + + + + + Negates the test of the constraint it wraps. + + + + + Constructs a new NotOperator + + + + + Returns a NotConstraint applied to its argument. + + + + + Operator that requires at least one of it's arguments to succeed + + + + + Construct an OrOperator + + + + + Apply the operator to produce an OrConstraint + + + + + Operator used to test for the presence of a named Property + on an object and optionally apply further tests to the + value of that property. + + + + + Constructs a PropOperator for a particular named property + + + + + Reduce produces a constraint from the operator and + any arguments. It takes the arguments from the constraint + stack and pushes the resulting constraint on it. + + + + + + Gets the name of the property to which the operator applies + + + + + Represents a constraint that succeeds if any of the + members of a collection match a base constraint. + + + + + Returns a constraint that will apply the argument + to the members of a collection, succeeding if + any of them succeed. + + + + + Operator that tests that an exception is thrown and + optionally applies further tests to the exception. + + + + + Construct a ThrowsOperator + + + + + Reduce produces a constraint from the operator and + any arguments. It takes the arguments from the constraint + stack and pushes the resulting constraint on it. + + + + + Represents a constraint that simply wraps the + constraint provided as an argument, without any + further functionality, but which modifes the + order of evaluation because of its precedence. + + + + + Constructor for the WithOperator + + + + + Returns a constraint that wraps its argument + + + + + Thrown when an assertion failed. + + + + The error message that explains + the reason for the exception + + + The error message that explains + the reason for the exception + The exception that caused the + current exception + + + + Serialization Constructor + + + + + Thrown when an assertion failed. + + + + + + + The error message that explains + the reason for the exception + The exception that caused the + current exception + + + + Serialization Constructor + + + + + Thrown when a test executes inconclusively. + + + + The error message that explains + the reason for the exception + + + The error message that explains + the reason for the exception + The exception that caused the + current exception + + + + Serialization Constructor + + + + + Thrown when an assertion failed. + + + + + + + The error message that explains + the reason for the exception + The exception that caused the + current exception + + + + Serialization Constructor + + + + + + + + + + + Compares two objects of a given Type for equality within a tolerance + + The first object to compare + The second object to compare + The tolerance to use in the comparison + + + + diff --git a/packages/NUnit.2.6.4/license.txt b/packages/NUnit.2.6.4/license.txt new file mode 100644 --- /dev/null +++ b/packages/NUnit.2.6.4/license.txt @@ -0,0 +1,15 @@ +Copyright 2002-2014 Charlie Poole +Copyright 2002-2004 James W. Newkirk, Michael C. Two, Alexei A. Vorontsov +Copyright 2000-2002 Philip A. Craig + +This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment (see the following) in the product documentation is required. + +Portions Copyright 2002-2014 Charlie Poole or Copyright 2002-2004 James W. Newkirk, Michael C. Two, Alexei A. Vorontsov or Copyright 2000-2002 Philip A. Craig + +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source distribution. diff --git a/packages/System.Text.Json.2.0.0.11/System.Text.Json.2.0.0.11.nupkg b/packages/System.Text.Json.2.0.0.11/System.Text.Json.2.0.0.11.nupkg new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a9fd4da0c2ee6c8d260502a21c573a07cf9afd24 GIT binary patch literal 31312 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 diff --git a/packages/System.Text.Json.2.0.0.11/lib/net40/System.Text.Json.dll b/packages/System.Text.Json.2.0.0.11/lib/net40/System.Text.Json.dll new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..73a2270296b5702d708ad0aee623f62b094a8b3a GIT binary patch literal 65024 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 + \ No newline at end of file