diff --git a/Implab.Fx.Test/Implab.Fx.Test.csproj b/Implab.Fx.Test/Implab.Fx.Test.csproj
--- a/Implab.Fx.Test/Implab.Fx.Test.csproj
+++ b/Implab.Fx.Test/Implab.Fx.Test.csproj
@@ -3,8 +3,7 @@
Debug
AnyCPU
-
-
+ 8.0.30703
2.0
{2F31E405-E267-4195-A05D-574093C21209}
Library
@@ -45,11 +44,6 @@
-
- False
-
-
-
@@ -68,9 +62,13 @@
MainForm.cs
+
+
OverlayForm.cs
+
+
diff --git a/Implab.Fx/ControlBoundPromise.cs b/Implab.Fx/ControlBoundPromise.cs
new file mode 100644
--- /dev/null
+++ b/Implab.Fx/ControlBoundPromise.cs
@@ -0,0 +1,30 @@
+using System.Windows.Forms;
+using System;
+
+
+namespace Implab.Fx {
+ public class ControlBoundPromise : Promise {
+ readonly Control m_target;
+
+ public ControlBoundPromise(Control target) {
+ Safe.ArgumentNotNull(target, "target");
+
+ m_target = target;
+ }
+
+ public ControlBoundPromise(Control target, IPromise parent, bool cancellable)
+ : base(parent, cancellable) {
+ Safe.ArgumentNotNull(target, "target");
+
+ m_target = target;
+ }
+
+ protected override void InvokeHandler(HandlerDescriptor handler) {
+ if (m_target.InvokeRequired)
+ m_target.BeginInvoke(new Action(base.InvokeHandler), handler);
+ else
+ base.InvokeHandler(handler);
+ }
+ }
+}
+
diff --git a/Implab.Fx/Implab.Fx.csproj b/Implab.Fx/Implab.Fx.csproj
--- a/Implab.Fx/Implab.Fx.csproj
+++ b/Implab.Fx/Implab.Fx.csproj
@@ -46,10 +46,11 @@
+
- {99B95D0D-9CF9-4F70-8ADF-F4D0AA5CB0D9}
+ {F550F1F8-8746-4AD0-9614-855F4C4B7F05}
Implab
diff --git a/Implab.Fx/PromiseHelpers.cs b/Implab.Fx/PromiseHelpers.cs
--- a/Implab.Fx/PromiseHelpers.cs
+++ b/Implab.Fx/PromiseHelpers.cs
@@ -1,7 +1,4 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
using System.Windows.Forms;
using System.Threading;
@@ -32,22 +29,14 @@ namespace Implab.Fx
if (ctl == null)
throw new ArgumentNullException("ctl");
- var directed = new Promise();
+ var directed = new ControlBoundPromise(ctl,that,true);
that.Then(
- res =>
- {
- if (ctl.InvokeRequired)
- ctl.Invoke(new Action(directed.Resolve), res);
- else
- directed.Resolve(res);
- },
+ directed.Resolve,
err =>
{
- if (ctl.InvokeRequired)
- ctl.Invoke(new Action(directed.Reject), err);
- else
- directed.Reject(err);
+ directed.Reject(err);
+ return default(T);
}
);
@@ -86,7 +75,10 @@ namespace Implab.Fx
that.Then(
res => sync.Post(state => d.Resolve(res), null),
- err => sync.Post(state => d.Reject(err), null)
+ err => {
+ sync.Post(state => d.Reject(err), null);
+ return default(T);
+ }
);
return d;
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
@@ -3,8 +3,7 @@
Debug
AnyCPU
-
-
+ 8.0.30703
2.0
{63F92C0C-61BF-48C0-A377-8D67C3C661D0}
Library
@@ -40,11 +39,6 @@
-
- False
-
-
-
diff --git a/Implab.sln b/Implab.sln
--- a/Implab.sln
+++ b/Implab.sln
@@ -17,22 +17,11 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Fx.Test", "Implab.Fx.Test\Implab.Fx.Test.csproj", "{2F31E405-E267-4195-A05D-574093C21209}"
EndProject
Global
- GlobalSection(TestCaseManagementSettings) = postSolution
- CategoryFile = Implab.vsmdi
- EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.Build.0 = Release|Any CPU
- {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.Build.0 = Release|Any CPU
{06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -41,11 +30,223 @@ Global
{2F31E405-E267-4195-A05D-574093C21209}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2F31E405-E267-4195-A05D-574093C21209}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2F31E405-E267-4195-A05D-574093C21209}.Release|Any CPU.Build.0 = Release|Any CPU
+ {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ EndGlobalSection
+ GlobalSection(MonoDevelopProperties) = preSolution
+ StartupItem = Implab\Implab.csproj
+ Policies = $0
+ $0.CSharpFormattingPolicy = $1
+ $1.IndentSwitchBody = True
+ $1.NamespaceBraceStyle = EndOfLine
+ $1.ClassBraceStyle = EndOfLine
+ $1.InterfaceBraceStyle = EndOfLine
+ $1.StructBraceStyle = EndOfLine
+ $1.EnumBraceStyle = EndOfLine
+ $1.MethodBraceStyle = EndOfLine
+ $1.ConstructorBraceStyle = EndOfLine
+ $1.DestructorBraceStyle = EndOfLine
+ $1.BeforeMethodDeclarationParentheses = False
+ $1.BeforeMethodCallParentheses = False
+ $1.BeforeConstructorDeclarationParentheses = False
+ $1.NewLineBeforeConstructorInitializerColon = NewLine
+ $1.NewLineAfterConstructorInitializerColon = SameLine
+ $1.BeforeIndexerDeclarationBracket = False
+ $1.BeforeDelegateDeclarationParentheses = False
+ $1.NewParentheses = False
+ $1.SpacesBeforeBrackets = False
+ $1.inheritsSet = Mono
+ $1.inheritsScope = text/x-csharp
+ $1.scope = text/x-csharp
+ $0.TextStylePolicy = $2
+ $2.FileWidth = 120
+ $2.EolMarker = Unix
+ $2.inheritsSet = VisualStudio
+ $2.inheritsScope = text/plain
+ $2.scope = text/x-csharp
+ $0.DotNetNamingPolicy = $3
+ $3.DirectoryNamespaceAssociation = PrefixedHierarchical
+ $3.ResourceNamePolicy = MSBuild
+ $0.TextStylePolicy = $4
+ $4.FileWidth = 120
+ $4.TabsToSpaces = False
+ $4.inheritsSet = VisualStudio
+ $4.inheritsScope = text/plain
+ $4.scope = application/xml
+ $0.XmlFormattingPolicy = $5
+ $5.inheritsSet = Mono
+ $5.inheritsScope = application/xml
+ $5.scope = application/xml
+ $0.TextStylePolicy = $6
+ $6.FileWidth = 120
+ $6.TabsToSpaces = False
+ $6.inheritsSet = VisualStudio
+ $6.inheritsScope = text/plain
+ $6.scope = text/plain
+ $0.NameConventionPolicy = $7
+ $7.Rules = $8
+ $8.NamingRule = $9
+ $9.Name = Namespaces
+ $9.AffectedEntity = Namespace
+ $9.VisibilityMask = VisibilityMask
+ $9.NamingStyle = PascalCase
+ $9.IncludeInstanceMembers = True
+ $9.IncludeStaticEntities = True
+ $8.NamingRule = $10
+ $10.Name = Types
+ $10.AffectedEntity = Class, Struct, Enum, Delegate
+ $10.VisibilityMask = VisibilityMask
+ $10.NamingStyle = PascalCase
+ $10.IncludeInstanceMembers = True
+ $10.IncludeStaticEntities = True
+ $8.NamingRule = $11
+ $11.Name = Interfaces
+ $11.RequiredPrefixes = $12
+ $12.String = I
+ $11.AffectedEntity = Interface
+ $11.VisibilityMask = VisibilityMask
+ $11.NamingStyle = PascalCase
+ $11.IncludeInstanceMembers = True
+ $11.IncludeStaticEntities = True
+ $8.NamingRule = $13
+ $13.Name = Attributes
+ $13.RequiredSuffixes = $14
+ $14.String = Attribute
+ $13.AffectedEntity = CustomAttributes
+ $13.VisibilityMask = VisibilityMask
+ $13.NamingStyle = PascalCase
+ $13.IncludeInstanceMembers = True
+ $13.IncludeStaticEntities = True
+ $8.NamingRule = $15
+ $15.Name = Event Arguments
+ $15.RequiredSuffixes = $16
+ $16.String = EventArgs
+ $15.AffectedEntity = CustomEventArgs
+ $15.VisibilityMask = VisibilityMask
+ $15.NamingStyle = PascalCase
+ $15.IncludeInstanceMembers = True
+ $15.IncludeStaticEntities = True
+ $8.NamingRule = $17
+ $17.Name = Exceptions
+ $17.RequiredSuffixes = $18
+ $18.String = Exception
+ $17.AffectedEntity = CustomExceptions
+ $17.VisibilityMask = VisibilityMask
+ $17.NamingStyle = PascalCase
+ $17.IncludeInstanceMembers = True
+ $17.IncludeStaticEntities = True
+ $8.NamingRule = $19
+ $19.Name = Methods
+ $19.AffectedEntity = Methods
+ $19.VisibilityMask = VisibilityMask
+ $19.NamingStyle = PascalCase
+ $19.IncludeInstanceMembers = True
+ $19.IncludeStaticEntities = True
+ $8.NamingRule = $20
+ $20.Name = Static Readonly Fields
+ $20.AffectedEntity = ReadonlyField
+ $20.VisibilityMask = Internal, Protected, Public
+ $20.NamingStyle = CamelCase
+ $20.IncludeInstanceMembers = False
+ $20.IncludeStaticEntities = True
+ $8.NamingRule = $21
+ $21.Name = Fields (Non Private)
+ $21.AffectedEntity = Field
+ $21.VisibilityMask = Internal, Public
+ $21.NamingStyle = CamelCase
+ $21.IncludeInstanceMembers = True
+ $21.IncludeStaticEntities = True
+ $8.NamingRule = $22
+ $22.Name = ReadOnly Fields (Non Private)
+ $22.AffectedEntity = ReadonlyField
+ $22.VisibilityMask = Internal, Public
+ $22.NamingStyle = CamelCase
+ $22.IncludeInstanceMembers = True
+ $22.IncludeStaticEntities = False
+ $8.NamingRule = $23
+ $23.Name = Fields (Private)
+ $23.RequiredPrefixes = $24
+ $24.String = m_
+ $23.AffectedEntity = Field, ReadonlyField
+ $23.VisibilityMask = Private, Protected
+ $23.NamingStyle = CamelCase
+ $23.IncludeInstanceMembers = True
+ $23.IncludeStaticEntities = False
+ $8.NamingRule = $25
+ $25.Name = Static Fields (Private)
+ $25.RequiredPrefixes = $26
+ $26.String = _
+ $25.AffectedEntity = Field
+ $25.VisibilityMask = Private
+ $25.NamingStyle = CamelCase
+ $25.IncludeInstanceMembers = False
+ $25.IncludeStaticEntities = True
+ $8.NamingRule = $27
+ $27.Name = ReadOnly Fields (Private)
+ $27.RequiredPrefixes = $28
+ $28.String = m_
+ $27.AffectedEntity = ReadonlyField
+ $27.VisibilityMask = Private, Protected
+ $27.NamingStyle = CamelCase
+ $27.IncludeInstanceMembers = True
+ $27.IncludeStaticEntities = False
+ $8.NamingRule = $29
+ $29.Name = Constant Fields
+ $29.AffectedEntity = ConstantField
+ $29.VisibilityMask = VisibilityMask
+ $29.NamingStyle = AllUpper
+ $29.IncludeInstanceMembers = True
+ $29.IncludeStaticEntities = True
+ $8.NamingRule = $30
+ $30.Name = Properties
+ $30.AffectedEntity = Property
+ $30.VisibilityMask = VisibilityMask
+ $30.NamingStyle = PascalCase
+ $30.IncludeInstanceMembers = True
+ $30.IncludeStaticEntities = True
+ $8.NamingRule = $31
+ $31.Name = Events
+ $31.AffectedEntity = Event
+ $31.VisibilityMask = VisibilityMask
+ $31.NamingStyle = PascalCase
+ $31.IncludeInstanceMembers = True
+ $31.IncludeStaticEntities = True
+ $8.NamingRule = $32
+ $32.Name = Enum Members
+ $32.AffectedEntity = EnumMember
+ $32.VisibilityMask = VisibilityMask
+ $32.NamingStyle = PascalCase
+ $32.IncludeInstanceMembers = True
+ $32.IncludeStaticEntities = True
+ $8.NamingRule = $33
+ $33.Name = Parameters
+ $33.AffectedEntity = Parameter, LocalVariable
+ $33.VisibilityMask = VisibilityMask
+ $33.NamingStyle = CamelCase
+ $33.IncludeInstanceMembers = True
+ $33.IncludeStaticEntities = True
+ $8.NamingRule = $34
+ $34.Name = Type Parameters
+ $34.RequiredPrefixes = $35
+ $35.String = T
+ $34.AffectedEntity = TypeParameter
+ $34.VisibilityMask = VisibilityMask
+ $34.NamingStyle = PascalCase
+ $34.IncludeInstanceMembers = True
+ $34.IncludeStaticEntities = True
+ EndGlobalSection
+ GlobalSection(TestCaseManagementSettings) = postSolution
+ CategoryFile = Implab.vsmdi
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
- GlobalSection(MonoDevelopProperties) = preSolution
- StartupItem = Implab\Implab.csproj
- EndGlobalSection
EndGlobal
diff --git a/Implab/IPromiseT.cs b/Implab/IPromiseT.cs
--- a/Implab/IPromiseT.cs
+++ b/Implab/IPromiseT.cs
@@ -11,16 +11,14 @@ namespace Implab
new T Join();
new T Join(int timeout);
- IPromise Then(ResultHandler success, ErrorHandler error);
IPromise Then(ResultHandler success, ErrorHandler error);
IPromise Then(ResultHandler success);
- new IPromise Error(ErrorHandler error);
IPromise Error(ErrorHandler error);
- IPromise Map(ResultMapper mapper, ErrorHandler error);
+ IPromise Map(ResultMapper mapper, ErrorHandler error);
IPromise Map(ResultMapper mapper);
- IPromise Chain(ChainedOperation chained, ErrorHandler error);
+ IPromise Chain(ChainedOperation chained, ErrorHandler error);
IPromise Chain(ChainedOperation chained);
new IPromise Cancelled(Action handler);
diff --git a/Implab/Implab.csproj b/Implab/Implab.csproj
--- a/Implab/Implab.csproj
+++ b/Implab/Implab.csproj
@@ -7,6 +7,8 @@
Library
Implab
Implab
+ 8.0.30703
+ 2.0
true
@@ -99,7 +101,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ I
+
+
+
+
+ Attribute
+
+
+
+
+ EventArgs
+
+
+
+
+ Exception
+
+
+
+
+
+
+
+
+ m_
+
+
+
+
+ _
+
+
+
+
+ m_
+
+
+
+
+
+
+
+
+
+ T
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Implab/JSON/JSONGrammar.cs b/Implab/JSON/JSONGrammar.cs
--- a/Implab/JSON/JSONGrammar.cs
+++ b/Implab/JSON/JSONGrammar.cs
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace Implab.JSON {
internal class JSONGrammar : Grammar {
- public enum TokenType : int{
+ public enum TokenType : int {
None,
BeginObject,
EndObject,
@@ -53,7 +53,6 @@ namespace Implab.JSON {
var backSlash = SymbolToken('\\');
var specialEscapeChars = SymbolSetToken('\\', '"', '/', 'b', 'f', 't', 'n', 'r');
var unicodeEspace = SymbolToken('u').Cat(hexDigit.Repeat(4));
- var escape = backSlash.Cat(specialEscapeChars.Or(unicodeEspace));
var whitespace = SymbolSetToken('\n', '\r', '\t', ' ').EClosure();
var beginObject = whitespace.Cat(SymbolToken('{')).Cat(whitespace);
var endObject = whitespace.Cat(SymbolToken('}')).Cat(whitespace);
@@ -65,9 +64,6 @@ namespace Implab.JSON {
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 character = unescaped.Or(escape);
- var str = quote.Cat(character.EClosure()).Cat(quote);
-
var jsonExpression =
number.Tag(TokenType.Number)
@@ -86,13 +82,7 @@ namespace Implab.JSON {
.Or(backSlash.Cat(specialEscapeChars).Tag(TokenType.EscapedChar))
.Or(backSlash.Cat(unicodeEspace).Tag(TokenType.EscapedUnicode))
.Or(unescaped.Closure().Tag(TokenType.UnescapedChar));
-
- var jsonNumberExpression =
- minus.Tag(TokenType.Minus)
- .Or(SymbolToken('+').Tag(TokenType.Plus))
- .Or(digit.Closure().Tag(TokenType.Integer))
- .Or(dot.Tag(TokenType.Dot))
- .Or(expSign.Tag(TokenType.Exp));
+
m_jsonDFA = BuildDFA(jsonExpression);
m_stringDFA = BuildDFA(jsonStringExpression);
diff --git a/Implab/JSON/JSONWriter.cs b/Implab/JSON/JSONWriter.cs
--- a/Implab/JSON/JSONWriter.cs
+++ b/Implab/JSON/JSONWriter.cs
@@ -15,7 +15,8 @@ namespace Implab.JSON {
Context m_context;
TextWriter m_writer;
- bool m_indent;
+ readonly bool m_indent = true;
+ readonly int m_indentSize = 4;
static readonly char [] _escapeBKS,
_escapeFWD,
@@ -43,13 +44,33 @@ namespace Implab.JSON {
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(", ");
- // TODO indent
+ m_writer.Write(",");
+
+ WriteIndent();
m_context.needComma = true;
Write(name);
m_writer.Write(" : ");
@@ -70,13 +91,12 @@ namespace Implab.JSON {
Write(value);
}
-
-
public void WriteValue(string value) {
if (m_context.element != JSONElementContext.Array)
OperationNotApplicable("WriteValue");
if (m_context.needComma)
- m_writer.Write(", ");
+ m_writer.Write(",");
+ WriteIndent();
m_context.needComma = true;
Write(value);
@@ -86,9 +106,10 @@ namespace Implab.JSON {
if (m_context.element != JSONElementContext.Array)
OperationNotApplicable("WriteValue");
if (m_context.needComma)
- m_writer.Write(", ");
+ m_writer.Write(",");
m_context.needComma = true;
+ WriteIndent();
Write(value);
}
@@ -96,9 +117,10 @@ namespace Implab.JSON {
if (m_context.element != JSONElementContext.Array)
OperationNotApplicable("WriteValue");
if (m_context.needComma)
- m_writer.Write(", ");
+ m_writer.Write(",");
m_context.needComma = true;
+ WriteIndent();
Write(value);
}
@@ -106,13 +128,16 @@ namespace Implab.JSON {
if (m_context.element != JSONElementContext.None && m_context.element != JSONElementContext.Array)
OperationNotApplicable("BeginObject");
if (m_context.needComma)
- m_writer.Write(", ");
+ 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("{ ");
+ m_writer.Write("{");
}
public void BeginObject(string name) {
@@ -121,28 +146,31 @@ namespace Implab.JSON {
m_contextStack.Push(m_context);
m_context = new Context { element = JSONElementContext.Object, needComma = false };
- m_writer.Write("{ ");
+ m_writer.Write("{");
}
public void EndObject() {
if (m_context.element != JSONElementContext.Object)
OperationNotApplicable("EndArray");
-
- m_writer.Write(" }");
+
m_context = m_contextStack.Pop();
+ 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(", ");
+ 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("[ ");
+ m_writer.Write("[");
}
public void BeginArray(string name) {
@@ -151,15 +179,16 @@ namespace Implab.JSON {
m_contextStack.Push(m_context);
m_context = new Context { element = JSONElementContext.Array, needComma = false };
- m_writer.Write("[ ");
+ m_writer.Write("[");
}
public void EndArray() {
if (m_context.element != JSONElementContext.Array)
OperationNotApplicable("EndArray");
- m_writer.Write(" ]");
m_context = m_contextStack.Pop();
+ WriteIndent();
+ m_writer.Write("]");
}
void Write(bool value) {
diff --git a/Implab/Parallels/ArrayTraits.cs b/Implab/Parallels/ArrayTraits.cs
--- a/Implab/Parallels/ArrayTraits.cs
+++ b/Implab/Parallels/ArrayTraits.cs
@@ -174,7 +174,10 @@ namespace Implab.Parallels {
if (left == 0)
promise.Resolve(res);
},
- e => promise.Reject(e)
+ e => {
+ promise.Reject(e);
+ throw new TransientPromiseException(e);
+ }
);
} catch (Exception e) {
diff --git a/Implab/Promise.cs b/Implab/Promise.cs
--- a/Implab/Promise.cs
+++ b/Implab/Promise.cs
@@ -10,8 +10,8 @@ namespace Implab {
public delegate void ErrorHandler(Exception e);
public delegate T ErrorHandler(Exception e);
public delegate void ResultHandler(T result);
- public delegate TNew ResultMapper(TSrc result);
- public delegate IPromise ChainedOperation(TSrc result);
+ public delegate TNew ResultMapper(TSrc result);
+ public delegate IPromise ChainedOperation(TSrc result);
///
/// Класс для асинхронного получения результатов. Так называемое "обещание".
@@ -51,32 +51,51 @@ namespace Implab {
protected struct HandlerDescriptor {
public ResultHandler resultHandler;
- public ErrorHandler errorHandler;
+ public ErrorHandler errorHandler;
public Action cancellHandler;
+ public Promise medium;
public void Resolve(T result) {
- if (resultHandler != null)
+ if (resultHandler != null) {
try {
resultHandler(result);
} catch (Exception e) {
Reject(e);
+ return;
}
+ }
+ if (medium != null)
+ medium.Resolve(result);
}
public void Reject(Exception err) {
- if (errorHandler != null)
+ if (errorHandler != null) {
try {
- errorHandler(err);
- } catch {
+ var res = errorHandler(err);
+ if (medium != null)
+ medium.Resolve(res);
+ } catch (TransientPromiseException err2) {
+ if (medium != null)
+ medium.Reject(err2.InnerException);
+ } catch (Exception err2) {
+ if (medium != null)
+ medium.Reject(err2);
}
+ } else if (medium != null)
+ medium.Reject(err);
}
public void Cancel() {
- if (cancellHandler != null)
+ if (cancellHandler != null) {
try {
cancellHandler();
- } catch {
+ } catch (Exception err) {
+ Reject(err);
+ return;
}
+ }
+ if (medium != null)
+ medium.Cancel();
}
}
@@ -102,14 +121,10 @@ namespace Implab {
public Promise(IPromise parent, bool cancellable) {
m_cancellable = cancellable;
if (parent != null)
- AddHandler(
- null,
- null,
- () => {
- if (parent.IsExclusive)
- parent.Cancel();
- }
- );
+ Cancelled(() => {
+ if (parent.IsExclusive)
+ parent.Cancel();
+ });
}
bool BeginTransit() {
@@ -197,13 +212,12 @@ namespace Implab {
///
/// true Операция была отменена, обработчики не будут вызваны.false отмена не возможна, поскольку обещание уже выполнено и обработчики отработали.
public bool Cancel() {
- if (BeginTransit()) {
+ if (m_cancellable && BeginTransit()) {
CompleteTransit(CANCELLED_STATE);
OnStateChanged();
return true;
- } else {
- return false;
}
+ return false;
}
// сделано для возвращаемого типа void
@@ -216,55 +230,6 @@ namespace Implab {
///
/// The handler of the successfully completed operation.
/// This handler will recieve an operation result as a parameter.
- /// Handles an exception that may occur during the operation.
- /// The new promise chained to this one.
- public IPromise Then(ResultHandler success, ErrorHandler error) {
- if (success == null && error == null)
- return this;
-
- var medium = new Promise(this, true);
-
- ResultHandler resultHandler;
- if (success != null)
- resultHandler = x => {
- success(x);
- medium.Resolve(x);
- };
- else
- resultHandler = medium.Resolve;
-
- ErrorHandler errorHandler;
- if (error != null)
- errorHandler = x => {
- // несмотря на то, что обработчик ошибки вызывается безопасно,
- // т.е. возникшие в нем ошибки будут подавлены, нам нужно
- // гарантировать, что ошибка будет передана дальше по цепочке обещаний
- try {
- error(x);
- } catch { }
- medium.Reject(x);
- };
- else
- errorHandler = medium.Reject;
-
- AddHandler(resultHandler, errorHandler, medium.InternalCancel);
-
- return medium;
- }
-
- public IPromise Then(Action success, ErrorHandler error) {
- return Then(x => success(), error);
- }
-
- public IPromise Then(Action success) {
- return Then(x => success());
- }
-
- ///
- /// Adds new handlers to this promise.
- ///
- /// The handler of the successfully completed operation.
- /// This handler will recieve an operation result as a parameter.
/// Handles an exception that may occur during the operation and returns the value which will be used as the result of the operation.
/// The new promise chained to this one.
public IPromise Then(ResultHandler success, ErrorHandler error) {
@@ -273,33 +238,25 @@ namespace Implab {
var medium = new Promise(this, true);
- ResultHandler resultHandler;
- ErrorHandler errorHandler;
-
- if (success != null)
- resultHandler = x => {
- success(x);
- medium.Resolve(x);
- };
- else
- resultHandler = medium.Resolve;
-
- if (error != null)
- errorHandler = x => {
- try {
- medium.Resolve(error(x));
- } catch (Exception e) {
- medium.Reject(e);
- }
- };
- else
- errorHandler = medium.Reject;
-
- AddHandler(resultHandler, errorHandler, medium.InternalCancel);
+ AddHandler(success, error, null, medium);
return medium;
}
+ public IPromise Then(Action success, ErrorHandler error) {
+ return Then(
+ x => success(),
+ e => {
+ error(e);
+ return default(T);
+ }
+ );
+ }
+
+ public IPromise Then(Action success) {
+ return Then(x => success());
+ }
+
public IPromise Then(ResultHandler success) {
if (success == null)
@@ -307,23 +264,28 @@ namespace Implab {
var medium = new Promise(this, true);
- ResultHandler resultHandler;
-
- if (success != null)
- resultHandler = x => {
- success(x);
- medium.Resolve(x);
- };
- else
- resultHandler = medium.Resolve;
-
- AddHandler(resultHandler, medium.Reject, medium.InternalCancel);
+ AddHandler(success, null, null, medium);
return medium;
}
- public IPromise Error(ErrorHandler error) {
- return Then((ResultHandler)null, error);
+ public IPromise Error(ErrorHandler error) {
+ if (error == null)
+ return this;
+
+ var medium = new Promise(this, true);
+
+ AddHandler(
+ null,
+ e => {
+ error(e);
+ return default(T);
+ },
+ null,
+ medium
+ );
+
+ return medium;
}
///
@@ -340,17 +302,7 @@ namespace Implab {
var medium = new Promise(this, true);
- AddHandler(
- x => medium.Resolve(x),
- e => {
- try {
- medium.Resolve(handler(e));
- } catch (Exception e2) {
- medium.Reject(e2);
- }
- },
- medium.InternalCancel
- );
+ AddHandler(null, handler, null, medium);
return medium;
}
@@ -359,27 +311,16 @@ namespace Implab {
if (handler == null)
return this;
- var medium = new Promise(this,true);
+ var medium = new Promise(this, true);
AddHandler(
- x => {
- // to avoid handler being called multiple times we handle exception by ourselfs
- try {
- handler();
- medium.Resolve(x);
- } catch (Exception e) {
- medium.Reject(e);
- }
+ x => handler(),
+ e => {
+ handler();
+ throw new TransientPromiseException(e);
},
-
- e => {
- try {
- handler();
- } catch { }
- medium.Reject(e);
- },
-
- medium.InternalCancel
+ null,
+ medium
);
return medium;
@@ -393,28 +334,37 @@ namespace Implab {
/// Обработчик ошибки. Данный обработчик получит
/// исключение возникшее при выполнении операции.
/// Новое обещание, которое будет выполнено при выполнении исходного обещания.
- public IPromise Map(ResultMapper mapper, ErrorHandler error) {
+ public IPromise Map(ResultMapper mapper, ErrorHandler error) {
if (mapper == null)
throw new ArgumentNullException("mapper");
// создаем прицепленное обещание
- var chained = new Promise(this,true);
+ var chained = new Promise(this, true);
ResultHandler resultHandler = result => chained.Resolve(mapper(result));
- ErrorHandler errorHandler = delegate(Exception e) {
- if (error != null)
+ ErrorHandler errorHandler;
+ if (error != null)
+ errorHandler = e => {
try {
- error(e);
- } catch { }
- // в случае ошибки нужно передать исключение дальше по цепочке
- chained.Reject(e);
- };
+ return error(e);
+ } catch (Exception e2) {
+ // в случае ошибки нужно передать исключение дальше по цепочке
+ chained.Reject(e2);
+ }
+ return default(T);
+ };
+ else
+ errorHandler = e => {
+ chained.Reject(e);
+ return default(T);
+ };
AddHandler(
resultHandler,
errorHandler,
- chained.InternalCancel
+ chained.InternalCancel,
+ null
);
return chained;
@@ -434,7 +384,7 @@ namespace Implab {
/// Обработчик ошибки. Данный обработчик получит
/// исключение возникшее при выполнении текуещй операции.
/// Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.
- public IPromise Chain(ChainedOperation chained, ErrorHandler error) {
+ public IPromise Chain(ChainedOperation chained, ErrorHandler error) {
// проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно
// создать посредника, к которому будут подвызяваться следующие обработчики.
@@ -449,15 +399,18 @@ namespace Implab {
var promise = chained(result);
promise.Then(
- x => medium.Resolve(x),
- e => medium.Reject(e)
+ medium.Resolve,
+ err => {
+ medium.Reject(err);
+ throw new TransientPromiseException(err);
+ }
);
// notify chained operation that it's not needed anymore
// порядок вызова Then, Cancelled важен, поскольку от этого
// зависит IsExclusive
medium.Cancelled(() => {
- if(promise.IsExclusive)
+ if (promise.IsExclusive)
promise.Cancel();
});
@@ -465,17 +418,25 @@ namespace Implab {
promise.Cancelled(() => medium.Reject(new OperationCanceledException()));
};
- ErrorHandler errorHandler = delegate(Exception e) {
- if (error != null)
- error(e);
+ ErrorHandler errorHandler = delegate(Exception e) {
+ if (error != null) {
+ try {
+ return error(e);
+ } catch (Exception e2) {
+ medium.Reject(e2);
+ return default(T);
+ }
+ }
// в случае ошибки нужно передать исключение дальше по цепочке
medium.Reject(e);
+ return default(T);
};
AddHandler(
resultHandler,
errorHandler,
- medium.InternalCancel
+ medium.InternalCancel,
+ null
);
return medium;
@@ -486,7 +447,7 @@ namespace Implab {
}
public IPromise Cancelled(Action handler) {
- AddHandler(null, null, handler);
+ AddHandler(null, null, handler, null);
return this;
}
@@ -500,8 +461,12 @@ namespace Implab {
throw new ArgumentNullException("handler");
AddHandler(
x => handler(),
- e => handler(),
- handler
+ e => {
+ handler();
+ throw new TransientPromiseException(e);
+ },
+ handler,
+ null
);
return this;
}
@@ -560,14 +525,15 @@ namespace Implab {
return Join(Timeout.Infinite);
}
- void AddHandler(ResultHandler success, ErrorHandler error, Action cancel) {
+ void AddHandler(ResultHandler success, ErrorHandler error, Action cancel, Promise medium) {
if (success != null || error != null)
Interlocked.Increment(ref m_childrenCount);
- HandlerDescriptor handler = new HandlerDescriptor {
+ var handler = new HandlerDescriptor {
resultHandler = success,
errorHandler = error,
- cancellHandler = cancel
+ cancellHandler = cancel,
+ medium = medium
};
bool queued;
@@ -653,7 +619,10 @@ namespace Implab {
if (Interlocked.Decrement(ref pending) == 0)
promise.Resolve(result);
},
- e => promise.Reject(e)
+ e => {
+ promise.Reject(e);
+ return default(T);
+ }
);
} else {
if (Interlocked.Decrement(ref pending) == 0)
diff --git a/Implab/PromiseExtensions.cs b/Implab/PromiseExtensions.cs
new file mode 100644
--- /dev/null
+++ b/Implab/PromiseExtensions.cs
@@ -0,0 +1,38 @@
+using System.Threading;
+
+namespace Implab {
+ public static class PromiseExtensions {
+ public static IPromise DispatchToCurrentContext(this IPromise that) {
+ var context = SynchronizationContext.Current;
+ if (context == null)
+ return that;
+
+ var p = new SyncContextPromise(context, that, true);
+
+ that.Then(
+ x => p.Resolve(x),
+ e => {
+ p.Reject(e);
+ return default(T);
+ }
+ );
+ return p;
+ }
+
+ public static IPromise DispatchToContext(this IPromise that, SynchronizationContext context) {
+ Safe.ArgumentNotNull(context, "context");
+
+ var p = new SyncContextPromise(context, that, true);
+
+ that.Then(
+ x => p.Resolve(x),
+ e => {
+ p.Reject(e);
+ return default(T);
+ }
+ );
+ return p;
+ }
+ }
+}
+
diff --git a/Implab/SyncContextPromise.cs b/Implab/SyncContextPromise.cs
new file mode 100644
--- /dev/null
+++ b/Implab/SyncContextPromise.cs
@@ -0,0 +1,22 @@
+using System.Threading;
+
+namespace Implab {
+ public class SyncContextPromise : Promise {
+ readonly SynchronizationContext m_context;
+
+ public SyncContextPromise(SynchronizationContext context) {
+ Safe.ArgumentNotNull(context, "context");
+ m_context = context;
+ }
+
+ public SyncContextPromise(SynchronizationContext context, IPromise parent, bool cancellable)
+ : base(parent, cancellable) {
+ Safe.ArgumentNotNull(context, "context");
+ m_context = context;
+ }
+ protected override void InvokeHandler(HandlerDescriptor handler) {
+ m_context.Post(x => base.InvokeHandler(handler),null);
+ }
+ }
+}
+
diff --git a/Implab/TransientPromiseException.cs b/Implab/TransientPromiseException.cs
new file mode 100644
--- /dev/null
+++ b/Implab/TransientPromiseException.cs
@@ -0,0 +1,33 @@
+using System;
+
+namespace Implab {
+
+ [Serializable]
+ public class TransientPromiseException : Exception {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The exception that is the cause of the current exception.
+ public TransientPromiseException(Exception inner) : base("The preceding promise has failed", inner) {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ ///
+ /// A that describes the exception.
+ /// The exception that is the cause of the current exception.
+ public TransientPromiseException(string message, Exception inner)
+ : base(message, inner) {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ ///
+ /// The contextual information about the source or destination.
+ /// The object that holds the serialized object data.
+ protected TransientPromiseException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
+ : base(info, context) {
+ }
+ }
+}
+