diff --git a/.gitignore b/.gitignore index 35ef4a02..f0595cb6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ [Bb]in/ [Oo]bj/ -[Ww]orking/ +[Ww]orking*/ Build/runbuild.txt Doc/doc.shfbproj_* TestResults/ diff --git a/Build/Newtonsoft.Json.nuspec b/Build/Newtonsoft.Json.nuspec index 0fe8ea58..35e47ca5 100644 --- a/Build/Newtonsoft.Json.nuspec +++ b/Build/Newtonsoft.Json.nuspec @@ -2,7 +2,7 @@ Newtonsoft.Json - 6.0.6 + 6.0.7 Json.NET Json.NET is a popular high-performance JSON framework for .NET James Newton-King diff --git a/Build/build.ps1 b/Build/build.ps1 index ffd728ef..04df4f4e 100644 --- a/Build/build.ps1 +++ b/Build/build.ps1 @@ -1,13 +1,14 @@ properties { - $zipFileName = "Json60r6.zip" + $zipFileName = "Json60r7.zip" $majorVersion = "6.0" - $majorWithReleaseVersion = "6.0.6" + $majorWithReleaseVersion = "6.0.7" $version = GetVersion $majorWithReleaseVersion $signAssemblies = $false $signKeyPath = "C:\Development\Releases\newtonsoft.snk" $buildDocumentation = $false $buildNuGet = $true $treatWarningsAsErrors = $false + $workingName = if ($workingName) {$workingName} else {"Working"} $baseDir = resolve-path .. $buildDir = "$baseDir\Build" @@ -15,12 +16,12 @@ $toolsDir = "$baseDir\Tools" $docDir = "$baseDir\Doc" $releaseDir = "$baseDir\Release" - $workingDir = "$baseDir\Working" + $workingDir = "$baseDir\$workingName" $builds = @( @{Name = "Newtonsoft.Json"; TestsName = "Newtonsoft.Json.Tests"; TestsFunction = "NUnitTests"; Constants=""; FinalDir="Net45"; NuGetDir = "net45"; Framework="net-4.0"; Sign=$true}, @{Name = "Newtonsoft.Json.Portable"; TestsName = "Newtonsoft.Json.Tests.Portable"; TestsFunction = "NUnitTests"; Constants="PORTABLE"; FinalDir="Portable"; NuGetDir = "portable-net45+wp80+win8+wpa81+aspnetcore50"; Framework="net-4.0"; Sign=$true}, @{Name = "Newtonsoft.Json.Portable40"; TestsName = "Newtonsoft.Json.Tests.Portable40"; TestsFunction = "NUnitTests"; Constants="PORTABLE40"; FinalDir="Portable40"; NuGetDir = "portable-net40+sl5+wp80+win8+wpa81"; Framework="net-4.0"; Sign=$true}, - @{Name = $null; TestsName = "Newtonsoft.Json.Tests.AspNetCore50"; TestsFunction = "CoreClrTests"; Constants="ASPNETCORE50"; FinalDir="ASPNETCORE50"; NuGetDir = $null; Framework=$null; Sign=$null}, + #@{Name = $null; TestsName = "Newtonsoft.Json.Tests.AspNetCore50"; TestsFunction = "CoreClrTests"; Constants="ASPNETCORE50"; FinalDir="ASPNETCORE50"; NuGetDir = $null; Framework=$null; Sign=$null}, @{Name = "Newtonsoft.Json.WinRT"; TestsName = "Newtonsoft.Json.Tests.WinRT"; TestsFunction = "WinRTTests"; Constants="NETFX_CORE"; FinalDir="WinRT"; NuGetDir = "netcore45"; Framework="net-4.5"; Sign=$true}, @{Name = "Newtonsoft.Json.Net40"; TestsName = "Newtonsoft.Json.Tests.Net40"; TestsFunction = "NUnitTests"; Constants="NET40"; FinalDir="Net40"; NuGetDir = "net40"; Framework="net-4.0"; Sign=$true}, @{Name = "Newtonsoft.Json.Net35"; TestsName = "Newtonsoft.Json.Tests.Net35"; TestsFunction = "NUnitTests"; Constants="NET35"; FinalDir="Net35"; NuGetDir = "net35"; Framework="net-2.0"; Sign=$true}, @@ -28,7 +29,7 @@ ) } -$framework = '4.0x86' +framework '4.0x86' task default -depends Test @@ -38,11 +39,12 @@ task Clean { if (Test-Path -path $workingDir) { - Write-Output "Deleting Working Directory" + Write-Output "Deleting existing working directory $workingDir" del $workingDir -Recurse -Force } + Write-Output "Creating working directory $workingDir" New-Item -Path $workingDir -ItemType Directory } diff --git a/Src/Newtonsoft.Json.TestConsole/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.TestConsole/Properties/AssemblyInfo.cs index 46669350..6d2371ec 100644 --- a/Src/Newtonsoft.Json.TestConsole/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.TestConsole/Properties/AssemblyInfo.cs @@ -33,4 +33,4 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: // [assembly: AssemblyVersion("6.0.0.0")] [assembly: AssemblyVersion("6.0.0.0")] -[assembly: AssemblyFileVersion("6.0.6.17929")] +[assembly: AssemblyFileVersion("6.0.7.18022")] diff --git a/Src/Newtonsoft.Json.Tests/Converters/DataSetConverterTests.cs b/Src/Newtonsoft.Json.Tests/Converters/DataSetConverterTests.cs index a6df0834..03a91e6b 100644 --- a/Src/Newtonsoft.Json.Tests/Converters/DataSetConverterTests.cs +++ b/Src/Newtonsoft.Json.Tests/Converters/DataSetConverterTests.cs @@ -462,6 +462,85 @@ namespace Newtonsoft.Json.Tests.Converters Assert.AreEqual("234", ds.Customers[0].CustomerID); } + + [Test] + public void ContractResolverInsideConverter() + { + var test = new MultipleDataTablesJsonTest + { + TableWrapper1 = new DataTableWrapper { DataTableProperty = CreateDataTable(3, "Table1Col") }, + TableWrapper2 = new DataTableWrapper { DataTableProperty = CreateDataTable(3, "Table2Col") } + }; + + string json = JsonConvert.SerializeObject(test, Formatting.Indented, new LowercaseDataTableConverter()); + + Assert.AreEqual(@"{ + ""TableWrapper1"": { + ""DataTableProperty"": [ + { + ""table1col1"": ""1"", + ""table1col2"": ""2"", + ""table1col3"": ""3"" + } + ], + ""StringProperty"": null, + ""IntProperty"": 0 + }, + ""TableWrapper2"": { + ""DataTableProperty"": [ + { + ""table2col1"": ""1"", + ""table2col2"": ""2"", + ""table2col3"": ""3"" + } + ], + ""StringProperty"": null, + ""IntProperty"": 0 + } +}", json); + } + + private static DataTable CreateDataTable(int cols, string colNamePrefix) + { + var table = new DataTable(); + for (int i = 1; i <= cols; i++) + { + table.Columns.Add(new DataColumn() { ColumnName = colNamePrefix + i, DefaultValue = i }); + } + table.Rows.Add(table.NewRow()); + return table; + } + + public class DataTableWrapper + { + public DataTable DataTableProperty { get; set; } + public String StringProperty { get; set; } + public Int32 IntProperty { get; set; } + } + + public class MultipleDataTablesJsonTest + { + public DataTableWrapper TableWrapper1 { get; set; } + public DataTableWrapper TableWrapper2 { get; set; } + } + + public class LowercaseDataTableConverter : DataTableConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var dataTableSerializer = new JsonSerializer { ContractResolver = new LowercaseContractResolver() }; + + base.WriteJson(writer, value, dataTableSerializer); + } + } + + public class LowercaseContractResolver : DefaultContractResolver + { + protected internal override string ResolvePropertyName(string propertyName) + { + return propertyName.ToLower(); + } + } } } diff --git a/Src/Newtonsoft.Json.Tests/Converters/StringEnumConverterTests.cs b/Src/Newtonsoft.Json.Tests/Converters/StringEnumConverterTests.cs index a84f3ad3..fba1a69d 100644 --- a/Src/Newtonsoft.Json.Tests/Converters/StringEnumConverterTests.cs +++ b/Src/Newtonsoft.Json.Tests/Converters/StringEnumConverterTests.cs @@ -491,7 +491,7 @@ namespace Newtonsoft.Json.Tests.Converters Value2 = DuplicateNameEnum2.foo_bar_NOT_USED }); - Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray())); + Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray(), 0, (int)ms.Length)); string xml = @" foo_bar diff --git a/Src/Newtonsoft.Json.Tests/JsonTextWriterTest.cs b/Src/Newtonsoft.Json.Tests/JsonTextWriterTest.cs index b0335bed..194dd38c 100644 --- a/Src/Newtonsoft.Json.Tests/JsonTextWriterTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonTextWriterTest.cs @@ -825,6 +825,49 @@ namespace Newtonsoft.Json.Tests ]", sb.ToString()); } + [Test] + public void WriteTokenDirect() + { + StringBuilder sb = new StringBuilder(); + StringWriter sw = new StringWriter(sb); + + using (JsonWriter jsonWriter = new JsonTextWriter(sw)) + { + jsonWriter.WriteToken(JsonToken.StartArray); + jsonWriter.WriteToken(JsonToken.Integer, 1); + jsonWriter.WriteToken(JsonToken.StartObject); + jsonWriter.WriteToken(JsonToken.PropertyName, "string"); + jsonWriter.WriteToken(JsonToken.Integer, int.MaxValue); + jsonWriter.WriteToken(JsonToken.EndObject); + jsonWriter.WriteToken(JsonToken.EndArray); + } + + Assert.AreEqual(@"[1,{""string"":2147483647}]", sb.ToString()); + } + + [Test] + public void WriteTokenDirect_BadValue() + { + StringBuilder sb = new StringBuilder(); + StringWriter sw = new StringWriter(sb); + + using (JsonWriter jsonWriter = new JsonTextWriter(sw)) + { + jsonWriter.WriteToken(JsonToken.StartArray); + + ExceptionAssert.Throws(() => + { + jsonWriter.WriteToken(JsonToken.Integer, "three"); + }, "Input string was not in a correct format."); + + ExceptionAssert.Throws(() => + { + jsonWriter.WriteToken(JsonToken.Integer); + }, @"Value cannot be null. +Parameter name: value"); + } + } + [Test] public void BadWriteEndArray() { diff --git a/Src/Newtonsoft.Json.Tests/Linq/AnnotationsTests.cs b/Src/Newtonsoft.Json.Tests/Linq/AnnotationsTests.cs new file mode 100644 index 00000000..10a4a3da --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Linq/AnnotationsTests.cs @@ -0,0 +1,250 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json.Linq; +using NUnit.Framework; + +namespace Newtonsoft.Json.Tests.Linq +{ + [TestFixture] + public class AnnotationsTests : TestFixtureBase + { + [Test] + public void AddAnnotation() + { + JObject o = new JObject(); + o.AddAnnotation("A string!"); + + string s = o.Annotation(); + Assert.AreEqual("A string!", s); + + s = (string)o.Annotation(typeof(string)); + Assert.AreEqual("A string!", s); + } + + [Test] + public void AddAnnotation_MultipleOfTheSameType() + { + JObject o = new JObject(); + o.AddAnnotation("A string!"); + o.AddAnnotation("Another string!"); + + string s = o.Annotation(); + Assert.AreEqual("A string!", s); + + s = (string)o.Annotation(typeof(string)); + Assert.AreEqual("A string!", s); + } + + [Test] + public void AddAnnotation_MultipleOfDifferentTypes() + { + JObject o = new JObject(); + o.AddAnnotation("A string!"); + o.AddAnnotation(new Uri("http://www.google.com/")); + + string s = o.Annotation(); + Assert.AreEqual("A string!", s); + + s = (string)o.Annotation(typeof(string)); + Assert.AreEqual("A string!", s); + + Uri i = o.Annotation(); + Assert.AreEqual(new Uri("http://www.google.com/"), i); + + i = (Uri)o.Annotation(typeof(Uri)); + Assert.AreEqual(new Uri("http://www.google.com/"), i); + } + + [Test] + public void GetAnnotation_NeverSet() + { + JObject o = new JObject(); + + string s = o.Annotation(); + Assert.AreEqual(null, s); + + s = (string)o.Annotation(typeof(string)); + Assert.AreEqual(null, s); + } + + [Test] + public void GetAnnotations() + { + JObject o = new JObject(); + o.AddAnnotation("A string!"); + o.AddAnnotation("A string 2!"); + o.AddAnnotation("A string 3!"); + + IList l = o.Annotations().ToList(); + + Assert.AreEqual(3, l.Count); + Assert.AreEqual("A string!", l[0]); + Assert.AreEqual("A string 2!", l[1]); + Assert.AreEqual("A string 3!", l[2]); + + l = o.Annotations(typeof(string)).Cast().ToList(); + + Assert.AreEqual(3, l.Count); + Assert.AreEqual("A string!", l[0]); + Assert.AreEqual("A string 2!", l[1]); + Assert.AreEqual("A string 3!", l[2]); + } + + [Test] + public void GetAnnotations_MultipleTypes() + { + JObject o = new JObject(); + o.AddAnnotation("A string!"); + o.AddAnnotation("A string 2!"); + o.AddAnnotation("A string 3!"); + o.AddAnnotation(new Uri("http://www.google.com/")); + + IList l = o.Annotations().ToList(); + + Assert.AreEqual(4, l.Count); + Assert.AreEqual("A string!", l[0]); + Assert.AreEqual("A string 2!", l[1]); + Assert.AreEqual("A string 3!", l[2]); + Assert.AreEqual(new Uri("http://www.google.com/"), l[3]); + + l = o.Annotations(typeof(object)).ToList(); + + Assert.AreEqual(4, l.Count); + Assert.AreEqual("A string!", l[0]); + Assert.AreEqual("A string 2!", l[1]); + Assert.AreEqual("A string 3!", l[2]); + Assert.AreEqual(new Uri("http://www.google.com/"), l[3]); + } + + [Test] + public void RemoveAnnotation() + { + JObject o = new JObject(); + o.AddAnnotation("A string!"); + + o.RemoveAnnotations(); + + string s = o.Annotation(); + Assert.AreEqual(null, s); + } + + [Test] + public void RemoveAnnotation_NonGeneric() + { + JObject o = new JObject(); + o.AddAnnotation("A string!"); + + o.RemoveAnnotations(typeof(string)); + + string s = o.Annotation(); + Assert.AreEqual(null, s); + + s = (string)o.Annotation(typeof(string)); + Assert.AreEqual(null, s); + } + + [Test] + public void RemoveAnnotation_Multiple() + { + JObject o = new JObject(); + o.AddAnnotation("A string!"); + o.AddAnnotation("A string 2!"); + o.AddAnnotation("A string 3!"); + + o.RemoveAnnotations(); + + string s = o.Annotation(); + Assert.AreEqual(null, s); + + o.AddAnnotation("A string 4!"); + + s = o.Annotation(); + Assert.AreEqual("A string 4!", s); + + Uri i = (Uri)o.Annotation(typeof(Uri)); + Assert.AreEqual(null, i); + } + + [Test] + public void RemoveAnnotation_MultipleCalls() + { + JObject o = new JObject(); + o.AddAnnotation("A string!"); + o.AddAnnotation(new Uri("http://www.google.com/")); + + o.RemoveAnnotations(); + o.RemoveAnnotations(); + + string s = o.Annotation(); + Assert.AreEqual(null, s); + + Uri i = o.Annotation(); + Assert.AreEqual(null, i); + } + + [Test] + public void RemoveAnnotation_Multiple_NonGeneric() + { + JObject o = new JObject(); + o.AddAnnotation("A string!"); + o.AddAnnotation("A string 2!"); + + o.RemoveAnnotations(typeof(string)); + + string s = o.Annotation(); + Assert.AreEqual(null, s); + } + + [Test] + public void RemoveAnnotation_MultipleCalls_NonGeneric() + { + JObject o = new JObject(); + o.AddAnnotation("A string!"); + o.AddAnnotation(new Uri("http://www.google.com/")); + + o.RemoveAnnotations(typeof(string)); + o.RemoveAnnotations(typeof(Uri)); + + string s = o.Annotation(); + Assert.AreEqual(null, s); + + Uri i = o.Annotation(); + Assert.AreEqual(null, i); + } + + [Test] + public void RemoveAnnotation_MultipleWithDifferentTypes() + { + JObject o = new JObject(); + o.AddAnnotation("A string!"); + o.AddAnnotation(new Uri("http://www.google.com/")); + + o.RemoveAnnotations(); + + string s = o.Annotation(); + Assert.AreEqual(null, s); + + Uri i = o.Annotation(); + Assert.AreEqual(new Uri("http://www.google.com/"), i); + } + + [Test] + public void RemoveAnnotation_MultipleWithDifferentTypes_NonGeneric() + { + JObject o = new JObject(); + o.AddAnnotation("A string!"); + o.AddAnnotation(new Uri("http://www.google.com/")); + + o.RemoveAnnotations(typeof(string)); + + string s = o.Annotation(); + Assert.AreEqual(null, s); + + Uri i = o.Annotation(); + Assert.AreEqual(new Uri("http://www.google.com/"), i); + } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs b/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs index 3b47a37d..a2327999 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs @@ -67,6 +67,7 @@ namespace Newtonsoft.Json.Tests.Linq counts[s] = c; } +#if !NETFX_CORE [Test] public void DeserializeLargeDynamic() { @@ -100,6 +101,7 @@ namespace Newtonsoft.Json.Tests.Linq Console.WriteLine("Time (secs): " + sw.Elapsed.TotalSeconds); } +#endif [Test] public void JObjectPropertyNames() diff --git a/Src/Newtonsoft.Json.Tests/Linq/JTokenReaderTest.cs b/Src/Newtonsoft.Json.Tests/Linq/JTokenReaderTest.cs index d1448bfb..42a6c059 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JTokenReaderTest.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JTokenReaderTest.cs @@ -208,8 +208,6 @@ namespace Newtonsoft.Json.Tests.Linq ] }"; - StringReader sr = new StringReader(input); - JObject o = JObject.Parse(input); using (JTokenReader jsonReader = new JTokenReader(o)) @@ -220,12 +218,14 @@ namespace Newtonsoft.Json.Tests.Linq Assert.AreEqual(0, lineInfo.LineNumber); Assert.AreEqual(0, lineInfo.LinePosition); Assert.AreEqual(false, lineInfo.HasLineInfo()); + Assert.AreEqual(null, jsonReader.CurrentToken); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.StartObject); Assert.AreEqual(1, lineInfo.LineNumber); Assert.AreEqual(1, lineInfo.LinePosition); Assert.AreEqual(true, lineInfo.HasLineInfo()); + Assert.AreEqual(o, jsonReader.CurrentToken); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.PropertyName); @@ -233,6 +233,7 @@ namespace Newtonsoft.Json.Tests.Linq Assert.AreEqual(2, lineInfo.LineNumber); Assert.AreEqual(7, lineInfo.LinePosition); Assert.AreEqual(true, lineInfo.HasLineInfo()); + Assert.AreEqual(o.Property("CPU"), jsonReader.CurrentToken); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.String); @@ -240,6 +241,7 @@ namespace Newtonsoft.Json.Tests.Linq Assert.AreEqual(2, lineInfo.LineNumber); Assert.AreEqual(15, lineInfo.LinePosition); Assert.AreEqual(true, lineInfo.HasLineInfo()); + Assert.AreEqual(o.Property("CPU").Value, jsonReader.CurrentToken); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.PropertyName); @@ -247,12 +249,14 @@ namespace Newtonsoft.Json.Tests.Linq Assert.AreEqual(3, lineInfo.LineNumber); Assert.AreEqual(10, lineInfo.LinePosition); Assert.AreEqual(true, lineInfo.HasLineInfo()); + Assert.AreEqual(o.Property("Drives"), jsonReader.CurrentToken); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.StartArray); Assert.AreEqual(3, lineInfo.LineNumber); Assert.AreEqual(12, lineInfo.LinePosition); Assert.AreEqual(true, lineInfo.HasLineInfo()); + Assert.AreEqual(o.Property("Drives").Value, jsonReader.CurrentToken); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.String); @@ -260,6 +264,7 @@ namespace Newtonsoft.Json.Tests.Linq Assert.AreEqual(4, lineInfo.LineNumber); Assert.AreEqual(22, lineInfo.LinePosition); Assert.AreEqual(true, lineInfo.HasLineInfo()); + Assert.AreEqual(o["Drives"][0], jsonReader.CurrentToken); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.String); @@ -267,21 +272,29 @@ namespace Newtonsoft.Json.Tests.Linq Assert.AreEqual(5, lineInfo.LineNumber); Assert.AreEqual(30, lineInfo.LinePosition); Assert.AreEqual(true, lineInfo.HasLineInfo()); + Assert.AreEqual(o["Drives"][1], jsonReader.CurrentToken); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.EndArray); Assert.AreEqual(3, lineInfo.LineNumber); Assert.AreEqual(12, lineInfo.LinePosition); Assert.AreEqual(true, lineInfo.HasLineInfo()); + Assert.AreEqual(o["Drives"], jsonReader.CurrentToken); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.EndObject); Assert.AreEqual(1, lineInfo.LineNumber); Assert.AreEqual(1, lineInfo.LinePosition); Assert.AreEqual(true, lineInfo.HasLineInfo()); + Assert.AreEqual(o, jsonReader.CurrentToken); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.None); + Assert.AreEqual(null, jsonReader.CurrentToken); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.None); + Assert.AreEqual(null, jsonReader.CurrentToken); } } @@ -471,15 +484,18 @@ namespace Newtonsoft.Json.Tests.Linq JObject o = JObject.Parse(json); - JsonReader reader = o.CreateReader(); + JTokenReader reader = (JTokenReader)o.CreateReader(); Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + Assert.AreEqual(o, reader.CurrentToken); Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual(o.Property("Name"), reader.CurrentToken); reader.ReadAsInt32(); + Assert.AreEqual(o["Name"], reader.CurrentToken); Assert.AreEqual(JsonToken.Integer, reader.TokenType); Assert.AreEqual(typeof(int), reader.ValueType); Assert.AreEqual(1, reader.Value); diff --git a/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs index caff897b..7a19a56c 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs @@ -717,6 +717,27 @@ namespace Newtonsoft.Json.Tests.Linq Assert.AreEqual(3, (int)descendants[descendants.Count - 1]); } + [Test] + public void DescendantsAndSelf() + { + JArray a = + new JArray( + 5, + new JArray(1), + new JArray(1, 2), + new JArray(1, 2, 3) + ); + + List descendantsAndSelf = a.DescendantsAndSelf().ToList(); + Assert.AreEqual(11, descendantsAndSelf.Count()); + Assert.AreEqual(a, descendantsAndSelf[0]); + Assert.AreEqual(5, (int)descendantsAndSelf[1]); + Assert.IsTrue(JToken.DeepEquals(new JArray(1, 2, 3), descendantsAndSelf[descendantsAndSelf.Count - 4])); + Assert.AreEqual(1, (int)descendantsAndSelf[descendantsAndSelf.Count - 3]); + Assert.AreEqual(2, (int)descendantsAndSelf[descendantsAndSelf.Count - 2]); + Assert.AreEqual(3, (int)descendantsAndSelf[descendantsAndSelf.Count - 1]); + } + [Test] public void CreateWriter() { diff --git a/Src/Newtonsoft.Json.Tests/Linq/JTokenWriterTest.cs b/Src/Newtonsoft.Json.Tests/Linq/JTokenWriterTest.cs index 81d06bd4..4e1218b8 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JTokenWriterTest.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JTokenWriterTest.cs @@ -140,6 +140,62 @@ namespace Newtonsoft.Json.Tests.Linq } } + [Test] + public void CurrentToken() + { + using (JTokenWriter jsonWriter = new JTokenWriter()) + { + Assert.AreEqual(WriteState.Start, jsonWriter.WriteState); + Assert.AreEqual(null, jsonWriter.CurrentToken); + + jsonWriter.WriteStartObject(); + Assert.AreEqual(WriteState.Object, jsonWriter.WriteState); + Assert.AreEqual(jsonWriter.Token, jsonWriter.CurrentToken); + + JObject o = (JObject)jsonWriter.Token; + + jsonWriter.WritePropertyName("CPU"); + Assert.AreEqual(WriteState.Property, jsonWriter.WriteState); + Assert.AreEqual(o.Property("CPU"), jsonWriter.CurrentToken); + + jsonWriter.WriteValue("Intel"); + Assert.AreEqual(WriteState.Object, jsonWriter.WriteState); + Assert.AreEqual(o["CPU"], jsonWriter.CurrentToken); + + jsonWriter.WritePropertyName("Drives"); + Assert.AreEqual(WriteState.Property, jsonWriter.WriteState); + Assert.AreEqual(o.Property("Drives"), jsonWriter.CurrentToken); + + jsonWriter.WriteStartArray(); + Assert.AreEqual(WriteState.Array, jsonWriter.WriteState); + Assert.AreEqual(o["Drives"], jsonWriter.CurrentToken); + + JArray a = (JArray)jsonWriter.CurrentToken; + + jsonWriter.WriteValue("DVD read/writer"); + Assert.AreEqual(WriteState.Array, jsonWriter.WriteState); + Assert.AreEqual(a[a.Count - 1], jsonWriter.CurrentToken); + +#if !(NET20 || NET35 || PORTABLE || ASPNETCORE50 || PORTABLE40) + jsonWriter.WriteValue(new BigInteger(123)); + Assert.AreEqual(WriteState.Array, jsonWriter.WriteState); + Assert.AreEqual(a[a.Count - 1], jsonWriter.CurrentToken); +#endif + + jsonWriter.WriteValue(new byte[0]); + Assert.AreEqual(WriteState.Array, jsonWriter.WriteState); + Assert.AreEqual(a[a.Count - 1], jsonWriter.CurrentToken); + + jsonWriter.WriteEnd(); + Assert.AreEqual(WriteState.Object, jsonWriter.WriteState); + Assert.AreEqual(a, jsonWriter.CurrentToken); + + jsonWriter.WriteEndObject(); + Assert.AreEqual(WriteState.Start, jsonWriter.WriteState); + Assert.AreEqual(o, jsonWriter.CurrentToken); + } + } + [Test] public void WriteComment() { diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj index c074fe29..c29b6927 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj @@ -213,6 +213,7 @@ + @@ -240,6 +241,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index 889ca7c4..ade23c1e 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -76,4 +76,4 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("6.0.0.0")] -[assembly: AssemblyFileVersion("6.0.6.17929")] +[assembly: AssemblyFileVersion("6.0.7.18022")] diff --git a/Src/Newtonsoft.Json.Tests/Schema/PerformanceTests.cs b/Src/Newtonsoft.Json.Tests/Schema/PerformanceTests.cs new file mode 100644 index 00000000..2b6c3ac2 --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Schema/PerformanceTests.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json.Schema; +using NUnit.Framework; + +namespace Newtonsoft.Json.Tests.Schema +{ + [TestFixture] + public class PerformanceTests : TestFixtureBase + { + [Test] + public void ReaderPerformance() + { + string json = @"[ + { + ""id"": 2, + ""name"": ""An ice sculpture"", + ""price"": 12.50, + ""tags"": [""cold"", ""ice""], + ""dimensions"": { + ""length"": 7.0, + ""width"": 12.0, + ""height"": 9.5 + }, + ""warehouseLocation"": { + ""latitude"": -78.75, + ""longitude"": 20.4 + } + }, + { + ""id"": 3, + ""name"": ""A blue mouse"", + ""price"": 25.50, + ""dimensions"": { + ""length"": 3.1, + ""width"": 1.0, + ""height"": 1.0 + }, + ""warehouseLocation"": { + ""latitude"": 54.4, + ""longitude"": -32.7 + } + } +]"; + + JsonSchema schema = JsonSchema.Parse(@"{ + ""$schema"": ""http://json-schema.org/draft-04/schema#"", + ""title"": ""Product set"", + ""type"": ""array"", + ""items"": { + ""title"": ""Product"", + ""type"": ""object"", + ""properties"": { + ""id"": { + ""description"": ""The unique identifier for a product"", + ""type"": ""number"", + ""required"": true + }, + ""name"": { + ""type"": ""string"", + ""required"": true + }, + ""price"": { + ""type"": ""number"", + ""minimum"": 0, + ""exclusiveMinimum"": true, + ""required"": true + }, + ""tags"": { + ""type"": ""array"", + ""items"": { + ""type"": ""string"" + }, + ""minItems"": 1, + ""uniqueItems"": true + }, + ""dimensions"": { + ""type"": ""object"", + ""properties"": { + ""length"": {""type"": ""number"",""required"": true}, + ""width"": {""type"": ""number"",""required"": true}, + ""height"": {""type"": ""number"",""required"": true} + } + }, + ""warehouseLocation"": { + ""description"": ""A geographical coordinate"", + ""type"": ""object"", + ""properties"": { + ""latitude"": { ""type"": ""number"" }, + ""longitude"": { ""type"": ""number"" } + } + } + } + } +}"); + + using (var tester = new PerformanceTester("Reader")) + { + for (int i = 0; i < 5000; i++) + { + JsonTextReader reader = new JsonTextReader(new StringReader(json)); + JsonValidatingReader validatingReader = new JsonValidatingReader(reader); + validatingReader.Schema = schema; + + while (validatingReader.Read()) + { + } + } + } + } + } + + public class PerformanceTester : IDisposable + { + private readonly Stopwatch _stopwatch = new Stopwatch(); + private readonly Action _callback; + + public PerformanceTester(string description) + : this(ts => Console.WriteLine(description + ": " + ts.TotalSeconds)) + { + } + + public PerformanceTester(Action callback) + { + _callback = callback; + _stopwatch.Start(); + } + + public static PerformanceTester Start(Action callback) + { + return new PerformanceTester(callback); + } + + public void Dispose() + { + _stopwatch.Stop(); + if (_callback != null) + _callback(Result); + } + + public TimeSpan Result + { + get { return _stopwatch.Elapsed; } + } + } +} diff --git a/Src/Newtonsoft.Json.Tests/Serialization/DefaultValueHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/DefaultValueHandlingTests.cs index 04d06713..4d64271d 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/DefaultValueHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/DefaultValueHandlingTests.cs @@ -368,6 +368,20 @@ namespace Newtonsoft.Json.Tests.Serialization Assert.AreEqual("fff", obj.Field1); } #endif + + [Test] + public void PopulateTest() + { + var test = JsonConvert.DeserializeObject("{\"IntValue\":null}"); + Console.WriteLine("IntValue:{0}", test.IntValue); + } + + public class PopulateWithNullJsonTest + { + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate, NullValueHandling = NullValueHandling.Ignore)] + [DefaultValue(6)] + public int IntValue { get; set; } + } } #if !NET20 diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index fd07b9b5..c2423c6c 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -8374,6 +8374,46 @@ Path '', line 1, position 1."); } #endif + [Test] + public void ParameterizedConstructorWithBasePrivateProperties() + { + var original = new DerivedConstructorType("Base", "Derived"); + + var serializerSettings = new JsonSerializerSettings(); + var jsonCopy = JsonConvert.SerializeObject(original, serializerSettings); + + Console.WriteLine(original); + + var clonedObject = JsonConvert.DeserializeObject(jsonCopy, serializerSettings); + + Assert.AreEqual("Base", clonedObject.BaseProperty); + Assert.AreEqual("Derived", clonedObject.DerivedProperty); + } + + public class DerivedConstructorType : BaseConstructorType + { + public DerivedConstructorType(string baseProperty, string derivedProperty) + : base(baseProperty) + { + DerivedProperty = derivedProperty; + } + + [JsonProperty] + public string DerivedProperty { get; private set; } + } + + + public class BaseConstructorType + { + [JsonProperty] + public string BaseProperty { get; private set; } + + public BaseConstructorType(string baseProperty) + { + BaseProperty = baseProperty; + } + } + public class ErroringJsonConverter : JsonConverter { public ErroringJsonConverter(string s) diff --git a/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs index 409faaca..00498675 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs @@ -23,6 +23,11 @@ // OTHER DEALINGS IN THE SOFTWARE. #endregion +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; +#endif #if !(PORTABLE || PORTABLE40) using System.Collections.ObjectModel; #if !(NET35 || NET20) @@ -1836,6 +1841,55 @@ namespace Newtonsoft.Json.Tests.Serialization Assert.AreEqual(1UL, item.WantedUnitID); } #endif + +#if !(NET20 || NET35) + [Test] + public void GenericItemTypeCollection() + { + DataType data = new DataType(); + data.Rows.Add("key", new List { new MyInterfaceImplementationType() { SomeProperty = "property" } }); + string serialized = JsonConvert.SerializeObject(data, Formatting.Indented); + + StringAssert.AreEqual(@"{ + ""Rows"": { + ""key"": { + ""$type"": ""System.Collections.Generic.List`1[[Newtonsoft.Json.Tests.Serialization.MyInterfaceImplementationType, Newtonsoft.Json.Tests]], mscorlib"", + ""$values"": [ + { + ""SomeProperty"": ""property"" + } + ] + } + } +}", serialized); + + DataType deserialized = JsonConvert.DeserializeObject(serialized); + + Assert.AreEqual("property", deserialized.Rows["key"].First().SomeProperty); + } +#endif + } + + public class DataType + { + public DataType() + { + Rows = new Dictionary>(); + } + + [JsonProperty(ItemTypeNameHandling = TypeNameHandling.Auto, TypeNameHandling = TypeNameHandling.Auto)] + public Dictionary> Rows { get; private set; } + } + + + public interface IMyInterfaceType + { + string SomeProperty { get; set; } + } + + public class MyInterfaceImplementationType : IMyInterfaceType + { + public string SomeProperty { get; set; } } #if !(NETFX_CORE || ASPNETCORE50) diff --git a/Src/Newtonsoft.Json.Tests/TestFixtureBase.cs b/Src/Newtonsoft.Json.Tests/TestFixtureBase.cs index 4fc71cd0..2715dfc1 100644 --- a/Src/Newtonsoft.Json.Tests/TestFixtureBase.cs +++ b/Src/Newtonsoft.Json.Tests/TestFixtureBase.cs @@ -261,11 +261,14 @@ namespace Newtonsoft.Json.Tests protected void TestSetup() #endif { -//#if !NETFX_CORE +#if !NETFX_CORE // CultureInfo turkey = CultureInfo.CreateSpecificCulture("tr"); // Thread.CurrentThread.CurrentCulture = turkey; // Thread.CurrentThread.CurrentUICulture = turkey; -//#endif + + Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; + Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture; +#endif JsonConvert.DefaultSettings = null; } diff --git a/Src/Newtonsoft.Json/Bson/BsonReader.cs b/Src/Newtonsoft.Json/Bson/BsonReader.cs index ccd5662f..9f07f3fc 100644 --- a/Src/Newtonsoft.Json/Bson/BsonReader.cs +++ b/Src/Newtonsoft.Json/Bson/BsonReader.cs @@ -35,7 +35,7 @@ using Newtonsoft.Json.Linq; namespace Newtonsoft.Json.Bson { /// - /// Represents a reader that provides fast, non-cached, forward-only access to serialized Json data. + /// Represents a reader that provides fast, non-cached, forward-only access to serialized JSON data. /// public class BsonReader : JsonReader { @@ -61,15 +61,15 @@ namespace Newtonsoft.Json.Bson private enum BsonReaderState { - Normal, - ReferenceStart, - ReferenceRef, - ReferenceId, - CodeWScopeStart, - CodeWScopeCode, - CodeWScopeScope, - CodeWScopeScopeObject, - CodeWScopeScopeEnd + Normal = 0, + ReferenceStart = 1, + ReferenceRef = 2, + ReferenceId = 3, + CodeWScopeStart = 4, + CodeWScopeCode = 5, + CodeWScopeScope = 6, + CodeWScopeScopeObject = 7, + CodeWScopeScopeEnd = 8 } private class ContainerContext diff --git a/Src/Newtonsoft.Json/Converters/ExpandoObjectConverter.cs b/Src/Newtonsoft.Json/Converters/ExpandoObjectConverter.cs index d07599d9..a1fbfee3 100644 --- a/Src/Newtonsoft.Json/Converters/ExpandoObjectConverter.cs +++ b/Src/Newtonsoft.Json/Converters/ExpandoObjectConverter.cs @@ -79,7 +79,7 @@ namespace Newtonsoft.Json.Converters case JsonToken.StartArray: return ReadList(reader); default: - if (JsonReader.IsPrimitiveToken(reader.TokenType)) + if (JsonTokenUtils.IsPrimitiveToken(reader.TokenType)) return reader.Value; throw JsonSerializationException.Create(reader, "Unexpected token when converting ExpandoObject: {0}".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); diff --git a/Src/Newtonsoft.Json/DateParseHandling.cs b/Src/Newtonsoft.Json/DateParseHandling.cs index 84abaf56..359ac355 100644 --- a/Src/Newtonsoft.Json/DateParseHandling.cs +++ b/Src/Newtonsoft.Json/DateParseHandling.cs @@ -33,17 +33,17 @@ namespace Newtonsoft.Json /// /// Date formatted strings are not parsed to a date type and are read as strings. /// - None, + None = 0, /// /// Date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed to . /// - DateTime, + DateTime = 1, #if !NET20 /// /// Date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed to . /// - DateTimeOffset + DateTimeOffset = 2 #endif } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/DateTimeZoneHandling.cs b/Src/Newtonsoft.Json/DateTimeZoneHandling.cs index df889089..9877d6e7 100644 --- a/Src/Newtonsoft.Json/DateTimeZoneHandling.cs +++ b/Src/Newtonsoft.Json/DateTimeZoneHandling.cs @@ -35,22 +35,22 @@ namespace Newtonsoft.Json /// /// Treat as local time. If the object represents a Coordinated Universal Time (UTC), it is converted to the local time. /// - Local, + Local = 0, /// /// Treat as a UTC. If the object represents a local time, it is converted to a UTC. /// - Utc, + Utc = 1, /// /// Treat as a local time if a is being converted to a string. /// If a string is being converted to , convert to a local time if a time zone is specified. /// - Unspecified, + Unspecified = 2, /// /// Time zone information should be preserved when converting. /// - RoundtripKind + RoundtripKind = 3 } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/FloatFormatHandling.cs b/Src/Newtonsoft.Json/FloatFormatHandling.cs index f39f7cc4..1c7f424b 100644 --- a/Src/Newtonsoft.Json/FloatFormatHandling.cs +++ b/Src/Newtonsoft.Json/FloatFormatHandling.cs @@ -34,17 +34,17 @@ namespace Newtonsoft.Json /// /// Write special floating point values as strings in JSON, e.g. "NaN", "Infinity", "-Infinity". /// - String, + String = 0, /// /// Write special floating point values as symbols in JSON, e.g. NaN, Infinity, -Infinity. /// Note that this will produce non-valid JSON. /// - Symbol, + Symbol = 1, /// /// Write special floating point values as the property's default value in JSON, e.g. 0.0 for a property, null for a property. /// - DefaultValue + DefaultValue = 2 } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/FloatParseHandling.cs b/Src/Newtonsoft.Json/FloatParseHandling.cs index 093d6e98..30583944 100644 --- a/Src/Newtonsoft.Json/FloatParseHandling.cs +++ b/Src/Newtonsoft.Json/FloatParseHandling.cs @@ -33,11 +33,11 @@ namespace Newtonsoft.Json /// /// Floating point numbers are parsed to . /// - Double, + Double = 0, /// /// Floating point numbers are parsed to . /// - Decimal + Decimal = 1 } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/FormatterAssemblyStyle.cs b/Src/Newtonsoft.Json/FormatterAssemblyStyle.cs index 0029781f..39d3f73a 100644 --- a/Src/Newtonsoft.Json/FormatterAssemblyStyle.cs +++ b/Src/Newtonsoft.Json/FormatterAssemblyStyle.cs @@ -11,12 +11,12 @@ namespace System.Runtime.Serialization.Formatters /// /// In simple mode, the assembly used during deserialization need not match exactly the assembly used during serialization. Specifically, the version numbers need not match as the LoadWithPartialName method is used to load the assembly. /// - Simple, + Simple = 0, /// /// In full mode, the assembly used during deserialization must match exactly the assembly used during serialization. The Load method of the Assembly class is used to load the assembly. /// - Full + Full = 1 } } diff --git a/Src/Newtonsoft.Json/Formatting.cs b/Src/Newtonsoft.Json/Formatting.cs index 55043b52..9438e63b 100644 --- a/Src/Newtonsoft.Json/Formatting.cs +++ b/Src/Newtonsoft.Json/Formatting.cs @@ -33,11 +33,11 @@ namespace Newtonsoft.Json /// /// No special formatting is applied. This is the default. /// - None, + None = 0, /// /// Causes child objects to be indented according to the and settings. /// - Indented + Indented = 1 } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonPosition.cs b/Src/Newtonsoft.Json/JsonPosition.cs index 7bd879e7..bfb64660 100644 --- a/Src/Newtonsoft.Json/JsonPosition.cs +++ b/Src/Newtonsoft.Json/JsonPosition.cs @@ -33,10 +33,10 @@ namespace Newtonsoft.Json { internal enum JsonContainerType { - None, - Object, - Array, - Constructor + None = 0, + Object = 1, + Array = 2, + Constructor = 3 } internal struct JsonPosition diff --git a/Src/Newtonsoft.Json/JsonReader.cs b/Src/Newtonsoft.Json/JsonReader.cs index c14c4da1..71ace1e4 100644 --- a/Src/Newtonsoft.Json/JsonReader.cs +++ b/Src/Newtonsoft.Json/JsonReader.cs @@ -39,7 +39,7 @@ using System.Linq; namespace Newtonsoft.Json { /// - /// Represents a reader that provides fast, non-cached, forward-only access to serialized Json data. + /// Represents a reader that provides fast, non-cached, forward-only access to serialized JSON data. /// public abstract class JsonReader : IDisposable { @@ -251,7 +251,7 @@ namespace Newtonsoft.Json get { int depth = _stack.Count; - if (IsStartToken(TokenType) || _currentPosition.Type == JsonContainerType.None) + if (JsonTokenUtils.IsStartToken(TokenType) || _currentPosition.Type == JsonContainerType.None) return depth; else return depth + 1; @@ -712,7 +712,7 @@ namespace Newtonsoft.Json if (t == JsonToken.Null) return null; - if (IsPrimitiveToken(t)) + if (JsonTokenUtils.IsPrimitiveToken(t)) { if (Value != null) { @@ -823,7 +823,7 @@ namespace Newtonsoft.Json if (TokenType == JsonToken.PropertyName) Read(); - if (IsStartToken(TokenType)) + if (JsonTokenUtils.IsStartToken(TokenType)) { int depth = Depth; @@ -963,37 +963,6 @@ namespace Newtonsoft.Json _currentState = State.Finished; } - internal static bool IsPrimitiveToken(JsonToken token) - { - switch (token) - { - case JsonToken.Integer: - case JsonToken.Float: - case JsonToken.String: - case JsonToken.Boolean: - case JsonToken.Undefined: - case JsonToken.Null: - case JsonToken.Date: - case JsonToken.Bytes: - return true; - default: - return false; - } - } - - internal static bool IsStartToken(JsonToken token) - { - switch (token) - { - case JsonToken.StartObject: - case JsonToken.StartArray: - case JsonToken.StartConstructor: - return true; - default: - return false; - } - } - private JsonContainerType GetTypeForCloseToken(JsonToken token) { switch (token) diff --git a/Src/Newtonsoft.Json/JsonTextWriter.cs b/Src/Newtonsoft.Json/JsonTextWriter.cs index 7a48fecd..26605612 100644 --- a/Src/Newtonsoft.Json/JsonTextWriter.cs +++ b/Src/Newtonsoft.Json/JsonTextWriter.cs @@ -37,7 +37,7 @@ using Newtonsoft.Json.Utilities; namespace Newtonsoft.Json { /// - /// Represents a writer that provides a fast, non-cached, forward-only way of generating Json data. + /// Represents a writer that provides a fast, non-cached, forward-only way of generating JSON data. /// public class JsonTextWriter : JsonWriter { diff --git a/Src/Newtonsoft.Json/JsonToken.cs b/Src/Newtonsoft.Json/JsonToken.cs index a159902f..5e1e47f6 100644 --- a/Src/Newtonsoft.Json/JsonToken.cs +++ b/Src/Newtonsoft.Json/JsonToken.cs @@ -37,91 +37,91 @@ namespace Newtonsoft.Json /// /// This is returned by the if a method has not been called. /// - None, + None = 0, /// /// An object start token. /// - StartObject, + StartObject = 1, /// /// An array start token. /// - StartArray, + StartArray = 2, /// /// A constructor start token. /// - StartConstructor, + StartConstructor = 3, /// /// An object property name. /// - PropertyName, + PropertyName = 4, /// /// A comment. /// - Comment, + Comment = 5, /// /// Raw JSON. /// - Raw, + Raw = 6, /// /// An integer. /// - Integer, + Integer = 7, /// /// A float. /// - Float, + Float = 8, /// /// A string. /// - String, + String = 9, /// /// A boolean. /// - Boolean, + Boolean = 10, /// /// A null token. /// - Null, + Null = 11, /// /// An undefined token. /// - Undefined, + Undefined = 12, /// /// An object end token. /// - EndObject, + EndObject = 13, /// /// An array end token. /// - EndArray, + EndArray = 14, /// /// A constructor end token. /// - EndConstructor, + EndConstructor = 15, /// /// A Date. /// - Date, + Date = 16, /// /// Byte data. /// - Bytes + Bytes = 17 } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonValidatingReader.cs b/Src/Newtonsoft.Json/JsonValidatingReader.cs index a02d2de6..41a8b062 100644 --- a/Src/Newtonsoft.Json/JsonValidatingReader.cs +++ b/Src/Newtonsoft.Json/JsonValidatingReader.cs @@ -461,7 +461,7 @@ namespace Newtonsoft.Json JsonSchemaModelBuilder builder = new JsonSchemaModelBuilder(); _model = builder.Build(_schema); - if (!JsonWriter.IsStartToken(_reader.TokenType)) + if (!JsonTokenUtils.IsStartToken(_reader.TokenType)) Push(new SchemaScope(JTokenType.None, CurrentMemberSchemas)); } @@ -578,7 +578,7 @@ namespace Newtonsoft.Json { if (schemaScope.CurrentItemWriter == null) { - if (JsonWriter.IsEndToken(_reader.TokenType)) + if (JsonTokenUtils.IsEndToken(_reader.TokenType)) continue; schemaScope.CurrentItemWriter = new JTokenWriter(); diff --git a/Src/Newtonsoft.Json/JsonWriter.cs b/Src/Newtonsoft.Json/JsonWriter.cs index 6f2b0806..b6de6925 100644 --- a/Src/Newtonsoft.Json/JsonWriter.cs +++ b/Src/Newtonsoft.Json/JsonWriter.cs @@ -41,22 +41,22 @@ using System.Linq; namespace Newtonsoft.Json { /// - /// Represents a writer that provides a fast, non-cached, forward-only way of generating Json data. + /// Represents a writer that provides a fast, non-cached, forward-only way of generating JSON data. /// public abstract class JsonWriter : IDisposable { internal enum State { - Start, - Property, - ObjectStart, - Object, - ArrayStart, - Array, - ConstructorStart, - Constructor, - Closed, - Error + Start = 0, + Property = 1, + ObjectStart = 2, + Object = 3, + ArrayStart = 4, + Array = 5, + ConstructorStart = 6, + Constructor = 7, + Closed = 8, + Error = 9 } // array that gives a new state based on the current state an the token being written @@ -449,13 +449,35 @@ namespace Newtonsoft.Json WriteToken(reader, writeChildren, true); } + /// + /// Writes the token and its value. + /// + /// The to write. + /// + /// The value to write. + /// A value is only required for tokens that have an associated value, e.g. the property name for . + /// A null value can be passed to the method for token's that don't have a value, e.g. . + public void WriteToken(JsonToken token, object value) + { + WriteTokenInternal(token, value); + } + + /// + /// Writes the token. + /// + /// The to write. + public void WriteToken(JsonToken token) + { + WriteTokenInternal(token, null); + } + internal void WriteToken(JsonReader reader, bool writeChildren, bool writeDateConstructorAsDate) { int initialDepth; if (reader.TokenType == JsonToken.None) initialDepth = -1; - else if (!IsStartToken(reader.TokenType)) + else if (!JsonTokenUtils.IsStartToken(reader.TokenType)) initialDepth = reader.Depth + 1; else initialDepth = reader.Depth; @@ -467,103 +489,113 @@ namespace Newtonsoft.Json { do { - switch (reader.TokenType) - { - case JsonToken.None: - // read to next - break; - case JsonToken.StartObject: - WriteStartObject(); - break; - case JsonToken.StartArray: - WriteStartArray(); - break; - case JsonToken.StartConstructor: - string constructorName = reader.Value.ToString(); - // write a JValue date when the constructor is for a date - if (writeDateConstructorAsDate && string.Equals(constructorName, "Date", StringComparison.Ordinal)) - WriteConstructorDate(reader); - else - WriteStartConstructor(reader.Value.ToString()); - break; - case JsonToken.PropertyName: - WritePropertyName(reader.Value.ToString()); - break; - case JsonToken.Comment: - WriteComment((reader.Value != null) ? reader.Value.ToString() : null); - break; - case JsonToken.Integer: -#if !(NET20 || NET35 || PORTABLE || PORTABLE40) - if (reader.Value is BigInteger) - { - WriteValue((BigInteger)reader.Value); - } - else -#endif - { - WriteValue(Convert.ToInt64(reader.Value, CultureInfo.InvariantCulture)); - } - break; - case JsonToken.Float: - object value = reader.Value; - - if (value is decimal) - WriteValue((decimal)value); - else if (value is double) - WriteValue((double)value); - else if (value is float) - WriteValue((float)value); - else - WriteValue(Convert.ToDouble(value, CultureInfo.InvariantCulture)); - break; - case JsonToken.String: - WriteValue(reader.Value.ToString()); - break; - case JsonToken.Boolean: - WriteValue(Convert.ToBoolean(reader.Value, CultureInfo.InvariantCulture)); - break; - case JsonToken.Null: - WriteNull(); - break; - case JsonToken.Undefined: - WriteUndefined(); - break; - case JsonToken.EndObject: - WriteEndObject(); - break; - case JsonToken.EndArray: - WriteEndArray(); - break; - case JsonToken.EndConstructor: - WriteEndConstructor(); - break; - case JsonToken.Date: -#if !NET20 - if (reader.Value is DateTimeOffset) - WriteValue((DateTimeOffset)reader.Value); - else -#endif - WriteValue(Convert.ToDateTime(reader.Value, CultureInfo.InvariantCulture)); - break; - case JsonToken.Raw: - WriteRawValue((reader.Value != null) ? reader.Value.ToString() : null); - break; - case JsonToken.Bytes: - if (reader.Value is Guid) - WriteValue((Guid)reader.Value); - else - WriteValue((byte[])reader.Value); - break; - default: - throw MiscellaneousUtils.CreateArgumentOutOfRangeException("TokenType", reader.TokenType, "Unexpected token type."); - } + // write a JValue date when the constructor is for a date + if (writeDateConstructorAsDate && reader.TokenType == JsonToken.StartConstructor && string.Equals(reader.Value.ToString(), "Date", StringComparison.Ordinal)) + WriteConstructorDate(reader); + else + WriteTokenInternal(reader.TokenType, reader.Value); } while ( // stop if we have reached the end of the token being read - initialDepth - 1 < reader.Depth - (IsEndToken(reader.TokenType) ? 1 : 0) + initialDepth - 1 < reader.Depth - (JsonTokenUtils.IsEndToken(reader.TokenType) ? 1 : 0) && writeChildren && reader.Read()); } + private void WriteTokenInternal(JsonToken tokenType, object value) + { + switch (tokenType) + { + case JsonToken.None: + // read to next + break; + case JsonToken.StartObject: + WriteStartObject(); + break; + case JsonToken.StartArray: + WriteStartArray(); + break; + case JsonToken.StartConstructor: + ValidationUtils.ArgumentNotNull(value, "value"); + WriteStartConstructor(value.ToString()); + break; + case JsonToken.PropertyName: + ValidationUtils.ArgumentNotNull(value, "value"); + WritePropertyName(value.ToString()); + break; + case JsonToken.Comment: + WriteComment((value != null) ? value.ToString() : null); + break; + case JsonToken.Integer: + ValidationUtils.ArgumentNotNull(value, "value"); +#if !(NET20 || NET35 || PORTABLE || PORTABLE40) + if (value is BigInteger) + { + WriteValue((BigInteger)value); + } + else +#endif + { + WriteValue(Convert.ToInt64(value, CultureInfo.InvariantCulture)); + } + break; + case JsonToken.Float: + ValidationUtils.ArgumentNotNull(value, "value"); + if (value is decimal) + WriteValue((decimal)value); + else if (value is double) + WriteValue((double)value); + else if (value is float) + WriteValue((float)value); + else + WriteValue(Convert.ToDouble(value, CultureInfo.InvariantCulture)); + break; + case JsonToken.String: + ValidationUtils.ArgumentNotNull(value, "value"); + WriteValue(value.ToString()); + break; + case JsonToken.Boolean: + ValidationUtils.ArgumentNotNull(value, "value"); + WriteValue(Convert.ToBoolean(value, CultureInfo.InvariantCulture)); + break; + case JsonToken.Null: + WriteNull(); + break; + case JsonToken.Undefined: + WriteUndefined(); + break; + case JsonToken.EndObject: + WriteEndObject(); + break; + case JsonToken.EndArray: + WriteEndArray(); + break; + case JsonToken.EndConstructor: + WriteEndConstructor(); + break; + case JsonToken.Date: + ValidationUtils.ArgumentNotNull(value, "value"); +#if !NET20 + if (value is DateTimeOffset) + WriteValue((DateTimeOffset)value); + else +#endif + WriteValue(Convert.ToDateTime(value, CultureInfo.InvariantCulture)); + break; + case JsonToken.Raw: + WriteRawValue((value != null) ? value.ToString() : null); + break; + case JsonToken.Bytes: + ValidationUtils.ArgumentNotNull(value, "value"); + if (value is Guid) + WriteValue((Guid)value); + else + WriteValue((byte[])value); + break; + default: + throw MiscellaneousUtils.CreateArgumentOutOfRangeException("TokenType", tokenType, "Unexpected token type."); + } + } + private void WriteConstructorDate(JsonReader reader) { if (!reader.Read()) @@ -582,32 +614,6 @@ namespace Newtonsoft.Json WriteValue(date); } - internal static bool IsEndToken(JsonToken token) - { - switch (token) - { - case JsonToken.EndObject: - case JsonToken.EndArray: - case JsonToken.EndConstructor: - return true; - default: - return false; - } - } - - internal static bool IsStartToken(JsonToken token) - { - switch (token) - { - case JsonToken.StartObject: - case JsonToken.StartArray: - case JsonToken.StartConstructor: - return true; - default: - return false; - } - } - private void WriteEnd(JsonContainerType type) { switch (type) diff --git a/Src/Newtonsoft.Json/Linq/JContainer.cs b/Src/Newtonsoft.Json/Linq/JContainer.cs index 3d16778d..cb662d99 100644 --- a/Src/Newtonsoft.Json/Linq/JContainer.cs +++ b/Src/Newtonsoft.Json/Linq/JContainer.cs @@ -272,6 +272,23 @@ namespace Newtonsoft.Json.Linq /// An containing the descendant tokens of the . public IEnumerable Descendants() { + return GetDescendants(false); + } + + /// + /// Returns a collection of the tokens that contain this token, and all descendant tokens of this token, in document order. + /// + /// An containing this token, and all the descendant tokens of the . + public IEnumerable DescendantsAndSelf() + { + return GetDescendants(true); + } + + internal IEnumerable GetDescendants(bool self) + { + if (self) + yield return this; + foreach (JToken o in ChildrenTokens) { yield return o; diff --git a/Src/Newtonsoft.Json/Linq/JToken.cs b/Src/Newtonsoft.Json/Linq/JToken.cs index 9778949c..000853f5 100644 --- a/Src/Newtonsoft.Json/Linq/JToken.cs +++ b/Src/Newtonsoft.Json/Linq/JToken.cs @@ -59,13 +59,15 @@ namespace Newtonsoft.Json.Linq , IDynamicMetaObjectProvider #endif { + private static JTokenEqualityComparer _equalityComparer; + private JContainer _parent; private JToken _previous; private JToken _next; - private static JTokenEqualityComparer _equalityComparer; + private object _annotations; - private int? _lineNumber; - private int? _linePosition; + //private int? _lineNumber; + //private int? _linePosition; private static readonly JTokenType[] BooleanTypes = new[] { JTokenType.Integer, JTokenType.Float, JTokenType.String, JTokenType.Comment, JTokenType.Raw, JTokenType.Boolean }; private static readonly JTokenType[] NumberTypes = new[] { JTokenType.Integer, JTokenType.Float, JTokenType.String, JTokenType.Comment, JTokenType.Raw, JTokenType.Boolean }; @@ -1923,25 +1925,50 @@ namespace Newtonsoft.Json.Linq SetLineInfo(lineInfo.LineNumber, lineInfo.LinePosition); } + private class LineInfoAnnotation + { + internal readonly int LineNumber; + internal readonly int LinePosition; + + public LineInfoAnnotation(int lineNumber, int linePosition) + { + LineNumber = lineNumber; + LinePosition = linePosition; + } + } + internal void SetLineInfo(int lineNumber, int linePosition) { - _lineNumber = lineNumber; - _linePosition = linePosition; + AddAnnotation(new LineInfoAnnotation(lineNumber, linePosition)); } bool IJsonLineInfo.HasLineInfo() { - return (_lineNumber != null && _linePosition != null); + return (Annotation() != null); } int IJsonLineInfo.LineNumber { - get { return _lineNumber ?? 0; } + get + { + LineInfoAnnotation annotation = Annotation(); + if (annotation != null) + return annotation.LineNumber; + + return 0; + } } int IJsonLineInfo.LinePosition { - get { return _linePosition ?? 0; } + get + { + LineInfoAnnotation annotation = Annotation(); + if (annotation != null) + return annotation.LinePosition; + + return 0; + } } /// @@ -2047,5 +2074,266 @@ namespace Newtonsoft.Json.Linq { return CloneToken(); } + + /// + /// Adds an object to the annotation list of this . + /// + /// The annotation to add. + public void AddAnnotation(object annotation) + { + if (annotation == null) + throw new ArgumentNullException("annotation"); + + if (_annotations == null) + { + _annotations = (annotation is object[]) ? new [] { annotation } : annotation; + } + else + { + object[] annotations = _annotations as object[]; + if (annotations == null) + { + _annotations = new [] { _annotations, annotation }; + } + else + { + int index = 0; + while (index < annotations.Length && annotations[index] != null) + { + index++; + } + if (index == annotations.Length) + { + Array.Resize(ref annotations, index * 2); + _annotations = annotations; + } + annotations[index] = annotation; + } + } + } + + /// + /// Get the first annotation object of the specified type from this . + /// + /// The type of the annotation to retrieve. + /// The first annotation object that matches the specified type, or null if no annotation is of the specified type. + public T Annotation() where T : class + { + if (_annotations != null) + { + object[] annotations = _annotations as object[]; + if (annotations == null) + { + return (_annotations as T); + } + for (int i = 0; i < annotations.Length; i++) + { + object annotation = annotations[i]; + if (annotation == null) + break; + + T local = annotation as T; + if (local != null) + return local; + } + } + + return default(T); + } + + /// + /// Gets the first annotation object of the specified type from this . + /// + /// The of the annotation to retrieve. + /// The first annotation object that matches the specified type, or null if no annotation is of the specified type. + public object Annotation(Type type) + { + if (type == null) + throw new ArgumentNullException("type"); + + if (_annotations != null) + { + object[] annotations = _annotations as object[]; + if (annotations == null) + { + if (type.IsInstanceOfType(_annotations)) + return _annotations; + } + else + { + for (int i = 0; i < annotations.Length; i++) + { + object o = annotations[i]; + if (o == null) + break; + + if (type.IsInstanceOfType(o)) + return o; + } + } + } + + return null; + } + + /// + /// Gets a collection of annotations of the specified type for this . + /// + /// The type of the annotations to retrieve. + /// An that contains the annotations for this . + public IEnumerable Annotations() where T : class + { + if (_annotations == null) + yield break; + + object[] annotations = _annotations as object[]; + if (annotations != null) + { + for (int i = 0; i < annotations.Length; i++) + { + object o = annotations[i]; + if (o == null) + break; + + T casted = o as T; + if (casted != null) + yield return casted; + } + yield break; + } + + T annotation = _annotations as T; + if (annotation == null) + yield break; + + yield return annotation; + } + + /// + /// Gets a collection of annotations of the specified type for this . + /// + /// The of the annotations to retrieve. + /// An of that contains the annotations that match the specified type for this . + public IEnumerable Annotations(Type type) + { + if (type == null) + throw new ArgumentNullException("type"); + + if (_annotations == null) + yield break; + + object[] annotations = _annotations as object[]; + if (annotations != null) + { + for (int i = 0; i < annotations.Length; i++) + { + object o = annotations[i]; + if (o == null) + break; + + if (type.IsInstanceOfType(o)) + yield return o; + } + yield break; + } + + if (!type.IsInstanceOfType(_annotations)) + yield break; + + yield return _annotations; + } + + /// + /// Removes the annotations of the specified type from this . + /// + /// The type of annotations to remove. + public void RemoveAnnotations() where T : class + { + if (_annotations != null) + { + object[] annotations = _annotations as object[]; + if (annotations == null) + { + if (_annotations is T) + _annotations = null; + } + else + { + int index = 0; + int keepCount = 0; + while (index < annotations.Length) + { + object obj2 = annotations[index]; + if (obj2 == null) + break; + + if (!(obj2 is T)) + annotations[keepCount++] = obj2; + + index++; + } + + if (keepCount != 0) + { + while (keepCount < index) + { + annotations[keepCount++] = null; + } + } + else + { + _annotations = null; + } + } + } + } + + /// + /// Removes the annotations of the specified type from this . + /// + /// The of annotations to remove. + public void RemoveAnnotations(Type type) + { + if (type == null) + throw new ArgumentNullException("type"); + + if (_annotations != null) + { + object[] annotations = _annotations as object[]; + if (annotations == null) + { + if (type.IsInstanceOfType(_annotations)) + _annotations = null; + } + else + { + int index = 0; + int keepCount = 0; + while (index < annotations.Length) + { + object o = annotations[index]; + if (o == null) + break; + + if (!type.IsInstanceOfType(o)) + annotations[keepCount++] = o; + + index++; + } + + if (keepCount != 0) + { + while (keepCount < index) + { + annotations[keepCount++] = null; + } + } + else + { + _annotations = null; + } + } + } + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JTokenReader.cs b/Src/Newtonsoft.Json/Linq/JTokenReader.cs index dbeb0942..726bd02a 100644 --- a/Src/Newtonsoft.Json/Linq/JTokenReader.cs +++ b/Src/Newtonsoft.Json/Linq/JTokenReader.cs @@ -29,14 +29,22 @@ using Newtonsoft.Json.Utilities; namespace Newtonsoft.Json.Linq { /// - /// Represents a reader that provides fast, non-cached, forward-only access to serialized Json data. + /// Represents a reader that provides fast, non-cached, forward-only access to serialized JSON data. /// public class JTokenReader : JsonReader, IJsonLineInfo { private readonly string _initialPath; private readonly JToken _root; private JToken _parent; - internal JToken _current; + private JToken _current; + + /// + /// Gets the at the reader's current position. + /// + public JToken CurrentToken + { + get { return _current; } + } /// /// Initializes a new instance of the class. @@ -47,7 +55,6 @@ namespace Newtonsoft.Json.Linq ValidationUtils.ArgumentNotNull(token, "token"); _root = token; - _current = token; } internal JTokenReader(JToken token, string initialPath) @@ -118,6 +125,9 @@ namespace Newtonsoft.Json.Linq { if (CurrentState != State.Start) { + if (_current == null) + return false; + JContainer container = _current as JContainer; if (container != null && _parent != container) return ReadInto(container); @@ -125,6 +135,7 @@ namespace Newtonsoft.Json.Linq return ReadOver(_current); } + _current = _root; SetToken(_current); return true; } @@ -165,6 +176,7 @@ namespace Newtonsoft.Json.Linq private bool ReadToEnd() { + _current = null; SetToken(JsonToken.None); return false; } diff --git a/Src/Newtonsoft.Json/Linq/JTokenType.cs b/Src/Newtonsoft.Json/Linq/JTokenType.cs index 00e04a40..36918733 100644 --- a/Src/Newtonsoft.Json/Linq/JTokenType.cs +++ b/Src/Newtonsoft.Json/Linq/JTokenType.cs @@ -33,91 +33,91 @@ namespace Newtonsoft.Json.Linq /// /// No token type has been set. /// - None, + None = 0, /// /// A JSON object. /// - Object, + Object = 1, /// /// A JSON array. /// - Array, + Array = 2, /// /// A JSON constructor. /// - Constructor, + Constructor = 3, /// /// A JSON object property. /// - Property, + Property = 4, /// /// A comment. /// - Comment, + Comment = 5, /// /// An integer value. /// - Integer, + Integer = 6, /// /// A float value. /// - Float, + Float = 7, /// /// A string value. /// - String, + String = 8, /// /// A boolean value. /// - Boolean, + Boolean = 9, /// /// A null value. /// - Null, + Null = 10, /// /// An undefined value. /// - Undefined, + Undefined = 11, /// /// A date value. /// - Date, + Date = 12, /// /// A raw JSON value. /// - Raw, + Raw = 13, /// /// A collection of bytes value. /// - Bytes, + Bytes = 14, /// /// A Guid value. /// - Guid, + Guid = 15, /// /// A Uri value. /// - Uri, + Uri = 16, /// /// A TimeSpan value. /// - TimeSpan + TimeSpan = 17 } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JTokenWriter.cs b/Src/Newtonsoft.Json/Linq/JTokenWriter.cs index 4bce4477..bbe985b8 100644 --- a/Src/Newtonsoft.Json/Linq/JTokenWriter.cs +++ b/Src/Newtonsoft.Json/Linq/JTokenWriter.cs @@ -33,7 +33,7 @@ using Newtonsoft.Json.Utilities; namespace Newtonsoft.Json.Linq { /// - /// Represents a writer that provides a fast, non-cached, forward-only way of generating Json data. + /// Represents a writer that provides a fast, non-cached, forward-only way of generating JSON data. /// public class JTokenWriter : JsonWriter { @@ -41,6 +41,15 @@ namespace Newtonsoft.Json.Linq private JContainer _parent; // used when writer is writing single value and the value has no containing parent private JValue _value; + private JToken _current; + + /// + /// Gets the at the writer's current position. + /// + public JToken CurrentToken + { + get { return _current; } + } /// /// Gets the token being writen. @@ -109,10 +118,12 @@ namespace Newtonsoft.Json.Linq _parent.AddAndSkipParentCheck(container); _parent = container; + _current = container; } private void RemoveParent() { + _current = _parent; _parent = _parent.Parent; if (_parent != null && _parent.Type == JTokenType.Property) @@ -172,6 +183,7 @@ namespace Newtonsoft.Json.Linq if (_parent != null) { _parent.Add(value); + _current = _parent.Last; if (_parent.Type == JTokenType.Property) _parent = _parent.Parent; @@ -179,6 +191,7 @@ namespace Newtonsoft.Json.Linq else { _value = value ?? JValue.CreateNull(); + _current = _value; } } diff --git a/Src/Newtonsoft.Json/Linq/JsonPath/QueryExpression.cs b/Src/Newtonsoft.Json/Linq/JsonPath/QueryExpression.cs index 4ff4aabf..c07f3a08 100644 --- a/Src/Newtonsoft.Json/Linq/JsonPath/QueryExpression.cs +++ b/Src/Newtonsoft.Json/Linq/JsonPath/QueryExpression.cs @@ -5,16 +5,16 @@ namespace Newtonsoft.Json.Linq.JsonPath { internal enum QueryOperator { - None, - Equals, - NotEquals, - Exists, - LessThan, - LessThanOrEquals, - GreaterThan, - GreaterThanOrEquals, - And, - Or + None = 0, + Equals = 1, + NotEquals = 2, + Exists = 3, + LessThan = 4, + LessThanOrEquals = 5, + GreaterThan = 6, + GreaterThanOrEquals = 7, + And = 8, + Or = 9 } internal abstract class QueryExpression diff --git a/Src/Newtonsoft.Json/Linq/MergeArrayHandling.cs b/Src/Newtonsoft.Json/Linq/MergeArrayHandling.cs index 8055dc2f..5e7fadf1 100644 --- a/Src/Newtonsoft.Json/Linq/MergeArrayHandling.cs +++ b/Src/Newtonsoft.Json/Linq/MergeArrayHandling.cs @@ -6,15 +6,15 @@ namespace Newtonsoft.Json.Linq public enum MergeArrayHandling { /// Concatenate arrays. - Concat, + Concat = 0, /// Union arrays, skipping items that already exist. - Union, + Union = 1, /// Replace all array items. - Replace, + Replace = 2, /// Merge array items together, matched by index. - Merge + Merge = 3 } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/MemberSerialization.cs b/Src/Newtonsoft.Json/MemberSerialization.cs index 8ed52304..bb68cd50 100644 --- a/Src/Newtonsoft.Json/MemberSerialization.cs +++ b/Src/Newtonsoft.Json/MemberSerialization.cs @@ -39,20 +39,20 @@ namespace Newtonsoft.Json /// All public members are serialized by default. Members can be excluded using or . /// This is the default member serialization mode. /// - OptOut, + OptOut = 0, /// /// Only members must be marked with or are serialized. /// This member serialization mode can also be set by marking the class with . /// - OptIn, + OptIn = 1, /// /// All public and private fields are serialized. Members can be excluded using or . /// This member serialization mode can also be set by marking the class with /// and setting IgnoreSerializableAttribute on to false. /// - Fields + Fields = 2 #pragma warning restore 1584,1711,1572,1581,1580,1574 } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj index 900041a8..4131e63f 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj @@ -136,6 +136,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj index 21819979..05d5c555 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj @@ -244,6 +244,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net40.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net40.csproj index 7e56cb31..da1dd081 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net40.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net40.csproj @@ -244,6 +244,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj index 17642ace..2136d184 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj @@ -216,6 +216,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable40.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable40.csproj index 164b407e..c1194aa9 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable40.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable40.csproj @@ -220,6 +220,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index 94fbc38c..2bfacf80 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -213,6 +213,7 @@ + diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index 7a15626e..3c696bf8 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -53,9 +53,10 @@ using System.Security; #endif #if !SIGNED - +[assembly: InternalsVisibleTo("Newtonsoft.Json.Schema")] [assembly: InternalsVisibleTo("Newtonsoft.Json.Tests")] #else +[assembly: InternalsVisibleTo("Newtonsoft.Json.Schema, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f561df277c6c0b497d629032b410cdcf286e537c054724f7ffa0164345f62b3e642029d7a80cc351918955328c4adc8a048823ef90b0cf38ea7db0d729caf2b633c3babe08b0310198c1081995c19029bc675193744eab9d7345b8a67258ec17d112cebdbbb2a281487dceeafb9d83aa930f32103fbe1d2911425bc5744002c7")] [assembly: InternalsVisibleTo("Newtonsoft.Json.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f561df277c6c0b497d629032b410cdcf286e537c054724f7ffa0164345f62b3e642029d7a80cc351918955328c4adc8a048823ef90b0cf38ea7db0d729caf2b633c3babe08b0310198c1081995c19029bc675193744eab9d7345b8a67258ec17d112cebdbbb2a281487dceeafb9d83aa930f32103fbe1d2911425bc5744002c7")] #endif @@ -91,5 +92,5 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("6.0.0.0")] -[assembly: AssemblyFileVersion("6.0.6.17929")] +[assembly: AssemblyFileVersion("6.0.7.18022")] [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Required.cs b/Src/Newtonsoft.Json/Required.cs index 889aa6d0..5e145234 100644 --- a/Src/Newtonsoft.Json/Required.cs +++ b/Src/Newtonsoft.Json/Required.cs @@ -33,16 +33,16 @@ namespace Newtonsoft.Json /// /// The property is not required. The default state. /// - Default, + Default = 0, /// /// The property must be defined in JSON but can be a null value. /// - AllowNull, + AllowNull = 1, /// /// The property must be defined in JSON and cannot be a null value. /// - Always + Always = 2 } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonContract.cs b/Src/Newtonsoft.Json/Serialization/JsonContract.cs index dfc8a19b..574becc2 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonContract.cs @@ -35,19 +35,15 @@ namespace Newtonsoft.Json.Serialization { internal enum JsonContractType { - None, - Object, - Array, - Primitive, - String, - Dictionary, -#if !(NET35 || NET20 || PORTABLE40) - Dynamic, -#endif -#if !(NETFX_CORE || PORTABLE || PORTABLE40) - Serializable, -#endif - Linq + None = 0, + Object = 1, + Array = 2, + Primitive = 3, + String = 4, + Dictionary = 5, + Dynamic = 6, + Serializable = 7, + Linq = 8 } /// diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index e90c7e4d..e1fe088c 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -52,9 +52,9 @@ namespace Newtonsoft.Json.Serialization { internal enum PropertyPresence { - None, - Null, - Value + None = 0, + Null = 1, + Value = 2 } private JsonSerializerProxy _internalSerializer; @@ -526,7 +526,7 @@ namespace Newtonsoft.Json.Serialization if (reader.TokenType == JsonToken.StartObject) { - JObject current = (JObject)reader._current; + JObject current = (JObject)reader.CurrentToken; JToken refToken = current[JsonTypeReflector.RefPropertyName]; if (refToken != null) @@ -967,7 +967,7 @@ namespace Newtonsoft.Json.Serialization // test tokentype here because default value might not be convertable to actual type, e.g. default of "" for DateTime if (HasFlag(property.DefaultValueHandling.GetValueOrDefault(Serializer._defaultValueHandling), DefaultValueHandling.Ignore) && !HasFlag(property.DefaultValueHandling.GetValueOrDefault(Serializer._defaultValueHandling), DefaultValueHandling.Populate) - && JsonReader.IsPrimitiveToken(tokenType) + && JsonTokenUtils.IsPrimitiveToken(tokenType) && MiscellaneousUtils.ValueEquals(reader.Value, property.GetResolvedDefaultValue())) return true; @@ -1563,7 +1563,7 @@ namespace Newtonsoft.Json.Serialization } else { - Type t = (JsonReader.IsPrimitiveToken(reader.TokenType)) ? reader.ValueType : typeof(IDynamicMetaObjectProvider); + Type t = (JsonTokenUtils.IsPrimitiveToken(reader.TokenType)) ? reader.ValueType : typeof(IDynamicMetaObjectProvider); JsonContract dynamicMemberContract = GetContractSafe(t); JsonConverter dynamicMemberConverter = GetConverter(dynamicMemberContract, null, null, member); diff --git a/Src/Newtonsoft.Json/StringEscapeHandling.cs b/Src/Newtonsoft.Json/StringEscapeHandling.cs index 13a332b3..4016eb6e 100644 --- a/Src/Newtonsoft.Json/StringEscapeHandling.cs +++ b/Src/Newtonsoft.Json/StringEscapeHandling.cs @@ -33,16 +33,16 @@ namespace Newtonsoft.Json /// /// Only control characters (e.g. newline) are escaped. /// - Default, + Default = 0, /// /// All non-ASCII and control characters (e.g. newline) are escaped. /// - EscapeNonAscii, + EscapeNonAscii = 1, /// /// HTML (<, >, &, ', ") and control characters (e.g. newline) are escaped. /// - EscapeHtml + EscapeHtml = 2 } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/TraceLevel.cs b/Src/Newtonsoft.Json/TraceLevel.cs index e80d532d..a6244d8e 100644 --- a/Src/Newtonsoft.Json/TraceLevel.cs +++ b/Src/Newtonsoft.Json/TraceLevel.cs @@ -12,27 +12,27 @@ namespace Newtonsoft.Json /// /// Output no tracing and debugging messages. /// - Off, + Off = 0, /// /// Output error-handling messages. /// - Error, + Error = 1, /// /// Output warnings and error-handling messages. /// - Warning, + Warning = 2, /// /// Output informational messages, warnings, and error-handling messages. /// - Info, + Info = 3, /// /// Output all debugging and tracing messages. /// - Verbose + Verbose = 4 } } diff --git a/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs b/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs index 5d4627ea..dc5d4706 100644 --- a/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs @@ -46,52 +46,48 @@ namespace Newtonsoft.Json.Utilities { internal enum PrimitiveTypeCode { - Empty, - Object, - Char, - CharNullable, - Boolean, - BooleanNullable, - SByte, - SByteNullable, - Int16, - Int16Nullable, - UInt16, - UInt16Nullable, - Int32, - Int32Nullable, - Byte, - ByteNullable, - UInt32, - UInt32Nullable, - Int64, - Int64Nullable, - UInt64, - UInt64Nullable, - Single, - SingleNullable, - Double, - DoubleNullable, - DateTime, - DateTimeNullable, -#if !NET20 - DateTimeOffset, - DateTimeOffsetNullable, -#endif - Decimal, - DecimalNullable, - Guid, - GuidNullable, - TimeSpan, - TimeSpanNullable, -#if !(PORTABLE || NET35 || NET20) - BigInteger, - BigIntegerNullable, -#endif - Uri, - String, - Bytes, - DBNull + Empty = 0, + Object = 1, + Char = 2, + CharNullable = 3, + Boolean = 4, + BooleanNullable = 5, + SByte = 6, + SByteNullable = 7, + Int16 = 8, + Int16Nullable = 9, + UInt16 = 10, + UInt16Nullable = 11, + Int32 = 12, + Int32Nullable = 13, + Byte = 14, + ByteNullable = 15, + UInt32 = 16, + UInt32Nullable = 17, + Int64 = 18, + Int64Nullable = 19, + UInt64 = 20, + UInt64Nullable = 21, + Single = 22, + SingleNullable = 23, + Double = 24, + DoubleNullable = 25, + DateTime = 26, + DateTimeNullable = 27, + DateTimeOffset = 28, + DateTimeOffsetNullable = 29, + Decimal = 30, + DecimalNullable = 31, + Guid = 32, + GuidNullable = 33, + TimeSpan = 34, + TimeSpanNullable = 35, + BigInteger = 36, + BigIntegerNullable = 37, + Uri = 38, + String = 39, + Bytes = 40, + DBNull = 41 } internal class TypeInformation @@ -102,10 +98,10 @@ namespace Newtonsoft.Json.Utilities internal enum ParseResult { - None, - Success, - Overflow, - Invalid + None = 0, + Success = 1, + Overflow = 2, + Invalid = 3 } internal static class ConvertUtils @@ -360,10 +356,10 @@ namespace Newtonsoft.Json.Utilities #region TryConvert internal enum ConvertResult { - Success, - CannotConvertNull, - NotInstantiableType, - NoValidConversion + Success = 0, + CannotConvertNull = 1, + NotInstantiableType = 2, + NoValidConversion = 3 } public static object Convert(object initialValue, CultureInfo culture, Type targetType) diff --git a/Src/Newtonsoft.Json/Utilities/DateTimeParser.cs b/Src/Newtonsoft.Json/Utilities/DateTimeParser.cs index 3188d560..0e506885 100644 --- a/Src/Newtonsoft.Json/Utilities/DateTimeParser.cs +++ b/Src/Newtonsoft.Json/Utilities/DateTimeParser.cs @@ -29,10 +29,10 @@ namespace Newtonsoft.Json.Utilities { internal enum ParserTimeZone { - Unspecified, - Utc, - LocalWestOfUtc, - LocalEastOfUtc + Unspecified = 0, + Utc = 1, + LocalWestOfUtc = 2, + LocalEastOfUtc = 3 } internal struct DateTimeParser diff --git a/Src/Newtonsoft.Json/Utilities/JsonTokenUtils.cs b/Src/Newtonsoft.Json/Utilities/JsonTokenUtils.cs new file mode 100644 index 00000000..fa45a5eb --- /dev/null +++ b/Src/Newtonsoft.Json/Utilities/JsonTokenUtils.cs @@ -0,0 +1,77 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Text; + +namespace Newtonsoft.Json.Utilities +{ + internal static class JsonTokenUtils + { + internal static bool IsEndToken(JsonToken token) + { + switch (token) + { + case JsonToken.EndObject: + case JsonToken.EndArray: + case JsonToken.EndConstructor: + return true; + default: + return false; + } + } + + internal static bool IsStartToken(JsonToken token) + { + switch (token) + { + case JsonToken.StartObject: + case JsonToken.StartArray: + case JsonToken.StartConstructor: + return true; + default: + return false; + } + } + + internal static bool IsPrimitiveToken(JsonToken token) + { + switch (token) + { + case JsonToken.Integer: + case JsonToken.Float: + case JsonToken.String: + case JsonToken.Boolean: + case JsonToken.Undefined: + case JsonToken.Null: + case JsonToken.Date: + case JsonToken.Bytes: + return true; + default: + return false; + } + } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs index ca32ebb5..2de8dca3 100644 --- a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs @@ -46,11 +46,11 @@ namespace Newtonsoft.Json.Utilities #if (NETFX_CORE || PORTABLE || PORTABLE40) internal enum MemberTypes { - Property, - Field, - Event, - Method, - Other + Property = 0, + Field = 1, + Event = 2, + Method = 3, + Other = 4 } #endif diff --git a/Src/Newtonsoft.Json/Utilities/TypeExtensions.cs b/Src/Newtonsoft.Json/Utilities/TypeExtensions.cs index 3618caee..3b4d1454 100644 --- a/Src/Newtonsoft.Json/Utilities/TypeExtensions.cs +++ b/Src/Newtonsoft.Json/Utilities/TypeExtensions.cs @@ -76,6 +76,14 @@ namespace Newtonsoft.Json.Utilities { return type.GetTypeInfo().IsAssignableFrom(c.GetTypeInfo()); } + + public static bool IsInstanceOfType(this Type type, object o) + { + if (o == null) + return false; + + return IsAssignableFrom(type, o.GetType()); + } #endif public static MethodInfo Method(this Delegate d) diff --git a/Src/Newtonsoft.Json/WriteState.cs b/Src/Newtonsoft.Json/WriteState.cs index c5352a53..b28b542d 100644 --- a/Src/Newtonsoft.Json/WriteState.cs +++ b/Src/Newtonsoft.Json/WriteState.cs @@ -37,36 +37,36 @@ namespace Newtonsoft.Json /// You may call the method to put the in the Closed state. /// Any other method calls results in an being thrown. /// - Error, + Error = 0, /// /// The method has been called. /// - Closed, + Closed = 1, /// /// An object is being written. /// - Object, + Object = 2, /// /// A array is being written. /// - Array, + Array = 3, /// /// A constructor is being written. /// - Constructor, + Constructor = 4, /// /// A property is being written. /// - Property, + Property = 5, /// /// A write method has not been called. /// - Start + Start = 6 } } \ No newline at end of file diff --git a/Tools/PSake/psake.psm1 b/Tools/PSake/psake.psm1 index 46410286..e5261ff1 100644 --- a/Tools/PSake/psake.psm1 +++ b/Tools/PSake/psake.psm1 @@ -1,5 +1,5 @@ # psake -# Copyright (c) 2010 James Kovacs +# Copyright (c) 2012 James Kovacs # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights @@ -20,1189 +20,841 @@ #Requires -Version 2.0 -#-- Private Module Variables (Listed here for quick reference) -[system.collections.stack]$script:context +#-- Public Module Functions --# -#-- Public Module Variables -- The psake hashtable variable is initialized in the invoke-psake function -$script:psake = @{} -$script:psake.use_exit_on_error = $false # determines if psake uses the "exit()" function when an exception occurs -$script:psake.log_error = $false # determines if the exception details are written to a file -$script:psake.build_success = $false # indicates that the current build was successful -$script:psake.version = "4.00" # contains the current version of psake -$script:psake.build_script_file = $null # contains a System.IO.FileInfo for the current build file -$script:psake.framework_version = "" # contains the framework version # for the current build -$script:psake.default_build_file_name = 'default.ps1' - -Export-ModuleMember -Variable "psake" - -#-- Private Module Functions -function ExecuteTask +# .ExternalHelp psake.psm1-help.xml +function Invoke-Task { - param([string]$taskName) - - Assert (![string]::IsNullOrEmpty($taskName)) "Task name should not be null or empty string" - - $taskKey = $taskName.ToLower() - - Assert ($script:context.Peek().tasks.Contains($taskKey)) "task [$taskName] does not exist" - - if ($script:context.Peek().executedTasks.Contains($taskKey)) - { - return - } - - Assert (!$script:context.Peek().callStack.Contains($taskKey)) "Error: Circular reference found for task, $taskName" - - $script:context.Peek().callStack.Push($taskKey) - - $task = $script:context.Peek().tasks.$taskKey - - $taskName = $task.Name - - $precondition_is_valid = if ($task.Precondition -ne $null) {& $task.Precondition} else {$true} - - if (!$precondition_is_valid) - { - "Precondition was false not executing $name" - } - else - { - if ($taskKey -ne 'default') - { - $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() - - if ( ($task.PreAction -ne $null) -or ($task.PostAction -ne $null) ) - { - Assert ($task.Action -ne $null) "Error: Action parameter must be specified when using PreAction or PostAction parameters" - } - - if ($task.Action -ne $null) - { - try - { - foreach($childTask in $task.DependsOn) - { - ExecuteTask $childTask - } - - $script:context.Peek().currentTaskName = $taskName - - if ($script:context.Peek().taskSetupScriptBlock -ne $null) - { - & $script:context.Peek().taskSetupScriptBlock - } - - if ($task.PreAction -ne $null) - { - & $task.PreAction - } - - $script:context.Peek().formatTaskNameString -f $taskName - & $task.Action - - if ($task.PostAction -ne $null) - { - & $task.PostAction - } - - if ($script:context.Peek().taskTearDownScriptBlock -ne $null) - { - & $script:context.Peek().taskTearDownScriptBlock - } - } - catch - { - if ($task.ContinueOnError) - { - "-"*70 - "Error in Task [$taskName] $_" - "-"*70 - } - else - { - throw $_ - } - } - } # if ($task.Action -ne $null) - else - { - #no Action was specified but we still execute all the dependencies - foreach($childTask in $task.DependsOn) - { - ExecuteTask $childTask - } - } - $stopwatch.stop() - $task.Duration = $stopwatch.Elapsed - } # if ($name.ToLower() -ne 'default') - else - { - foreach($childTask in $task.DependsOn) - { - ExecuteTask $childTask - } - } - - if ($task.Postcondition -ne $null) - { - Assert (& $task.Postcondition) "Error: Postcondition failed for $taskName" - } - } - - $poppedTaskKey = $script:context.Peek().callStack.Pop() - - Assert ($poppedTaskKey -eq $taskKey) "Error: CallStack was corrupt. Expected $taskKey, but got $poppedTaskKey." - - $script:context.Peek().executedTasks.Push($taskKey) -} - -function Configure-BuildEnvironment -{ - if ($framework.Length -ne 3 -and $framework.Length -ne 6) { - throw "Error: Invalid .NET Framework version, $framework, specified" - } - $versionPart = $framework.Substring(0,3) - $bitnessPart = $framework.Substring(3) - $versions = $null - switch ($versionPart) - { - '1.0' { $versions = @('v1.0.3705') } - '1.1' { $versions = @('v1.1.4322') } - '2.0' { $versions = @('v2.0.50727') } - '3.0' { $versions = @('v2.0.50727') } - '3.5' { $versions = @('v3.5','v2.0.50727') } - '4.0' { $versions = @('v4.0.30319') } - default { throw "Error: Unknown .NET Framework version, $versionPart, specified in $framework" } - } - - $bitness = 'Framework' - if($versionPart -ne '1.0' -and $versionPart -ne '1.1') { - switch ($bitnessPart) - { - 'x86' { $bitness = 'Framework' } - 'x64' { $bitness = 'Framework64' } - $null { - $ptrSize = [System.IntPtr]::Size - switch ($ptrSize) - { - 4 { $bitness = 'Framework' } - 8 { $bitness = 'Framework64' } - default { throw "Error: Unknown pointer size ($ptrSize) returned from System.IntPtr." } - } - } - default { throw "Error: Unknown .NET Framework bitness, $bitnessPart, specified in $framework" } - } - } - $frameworkDirs = $versions | foreach { "$env:windir\Microsoft.NET\$bitness\$_\" } - - $frameworkDirs | foreach { Assert (test-path $_) "Error: No .NET Framework installation directory found at $_" } - - $env:path = [string]::Join(';', $frameworkDirs) + ";$env:path" - #if any error occurs in a PS function then "stop" processing immediately - # this does not effect any external programs that return a non-zero exit code - $global:ErrorActionPreference = "Stop" -} - -function Cleanup-Environment -{ - $env:path = $script:context.Peek().originalEnvPath - Set-Location $script:context.Peek().originalDirectory - $global:ErrorActionPreference = $script:context.Peek().originalErrorActionPreference -} - -#borrowed from Jeffrey Snover http://blogs.msdn.com/powershell/archive/2006/12/07/resolve-error.aspx -function Resolve-Error($ErrorRecord=$Error[0]) -{ - "ErrorRecord" - $ErrorRecord | Format-List * -Force | Out-String -Stream | ? {$_} - "" - "ErrorRecord.InvocationInfo" - $ErrorRecord.InvocationInfo | Format-List * | Out-String -Stream | ? {$_} - "" - "Exception" - $Exception = $ErrorRecord.Exception - for ($i = 0; $Exception; $i++, ($Exception = $Exception.InnerException)) - { - "$i" * 70 - $Exception | Format-List * -Force | Out-String -Stream | ? {$_} - "" - } -} - -function Write-Documentation -{ - $list = New-Object System.Collections.ArrayList - foreach($key in $script:context.Peek().tasks.Keys) - { - if($key -eq "default") - { - continue - } - $task = $script:context.Peek().tasks.$key - $content = "" | Select-Object Name, Description - $content.Name = $task.Name - $content.Description = $task.Description - $index = $list.Add($content) - } - - $list | Sort 'Name' | Format-Table -Auto -} - -function Write-TaskTimeSummary -{ - "-"*70 - "Build Time Report" - "-"*70 - $list = @() - while ($script:context.Peek().executedTasks.Count -gt 0) - { - $taskKey = $script:context.Peek().executedTasks.Pop() - $task = $script:context.Peek().tasks.$taskKey - if($taskKey -eq "default") - { - continue - } - $list += "" | Select-Object @{Name="Name";Expression={$task.Name}}, @{Name="Duration";Expression={$task.Duration}} - } - [Array]::Reverse($list) - $list += "" | Select-Object @{Name="Name";Expression={"Total:"}}, @{Name="Duration";Expression={$stopwatch.Elapsed}} - $list | Format-Table -Auto | Out-String -Stream | ? {$_} # using "Out-String -Stream" to filter out the blank line that Format-Table prepends -} - -#-- Public Module Functions -function Exec -{ -<# -.SYNOPSIS -Helper function for executing command-line programs. - -.DESCRIPTION -This is a helper function that runs a scriptblock and checks the PS variable $lastexitcode to see if an error occcured. -If an error is detected then an exception is thrown. This function allows you to run command-line programs without -having to explicitly check fthe $lastexitcode variable. - -.PARAMETER cmd -The scriptblock to execute. This scriptblock will typically contain the command-line invocation. -Required - -.PARAMETER errorMessage -The error message used for the exception that is thrown. -Optional - -.EXAMPLE -exec { svn info $repository_trunk } "Error executing SVN. Please verify SVN command-line client is installed" - -This example calls the svn command-line client. - -.LINK -Assert -Invoke-psake -Task -Properties -Include -FormatTaskName -TaskSetup -TaskTearDown -#> -[CmdletBinding( - SupportsShouldProcess=$False, - SupportsTransactions=$False, - ConfirmImpact="None", - DefaultParameterSetName="")] - - param( - [Parameter(Position=0,Mandatory=1)][scriptblock]$cmd, - [Parameter(Position=1,Mandatory=0)][string]$errorMessage = "Error executing command: " + $cmd - ) - & $cmd - if ($lastexitcode -ne 0) - { - throw $errorMessage - } -} - -function Assert -{ -<# -.SYNOPSIS -Helper function for "Design by Contract" assertion checking. - -.DESCRIPTION -This is a helper function that makes the code less noisy by eliminating many of the "if" statements -that are normally required to verify assumptions in the code. - -.PARAMETER conditionToCheck -The boolean condition to evaluate -Required - -.PARAMETER failureMessage -The error message used for the exception if the conditionToCheck parameter is false -Required - -.EXAMPLE -Assert $false "This always throws an exception" - -This example always throws an exception - -.EXAMPLE -Assert ( ($i % 2) -eq 0 ) "%i is not an even number" - -This exmaple may throw an exception if $i is not an even number - -.LINK -Invoke-psake -Task -Properties -Include -FormatTaskName -TaskSetup -TaskTearDown - -.NOTES -It might be necessary to wrap the condition with paranthesis to force PS to evaluate the condition -so that a boolean value is calculated and passed into the 'conditionToCheck' parameter. - -Example: - Assert 1 -eq 2 "1 doesn't equal 2" - -PS will pass 1 into the condtionToCheck variable and PS will look for a parameter called "eq" and -throw an exception with the following message "A parameter cannot be found that matches parameter name 'eq'" - -The solution is to wrap the condition in () so that PS will evaluate it first. - - Assert (1 -eq 2) "1 doesn't equal 2" -#> -[CmdletBinding( - SupportsShouldProcess=$False, - SupportsTransactions=$False, - ConfirmImpact="None", - DefaultParameterSetName="")] - - param( - [Parameter(Position=0,Mandatory=1)]$conditionToCheck, - [Parameter(Position=1,Mandatory=1)]$failureMessage - ) - if (!$conditionToCheck) { throw $failureMessage } -} - -function Task -{ -<# -.SYNOPSIS -Defines a build task to be executed by psake - -.DESCRIPTION -This function creates a 'task' object that will be used by the psake engine to execute a build task. -Note: There must be at least one task called 'default' in the build script - -.PARAMETER Name -The name of the task -Required - -.PARAMETER Action -A scriptblock containing the statements to execute -Optional - -.PARAMETER PreAction -A scriptblock to be executed before the 'Action' scriptblock. -Note: This parameter is ignored if the 'Action' scriptblock is not defined. -Optional - -.PARAMETER PostAction -A scriptblock to be executed after the 'Action' scriptblock. -Note: This parameter is ignored if the 'Action' scriptblock is not defined. -Optional - -.PARAMETER Precondition -A scriptblock that is executed to determine if the task is executed or skipped. -This scriptblock should return $true or $false -Optional - -.PARAMETER Postcondition -A scriptblock that is executed to determine if the task completed its job correctly. -An exception is thrown if the scriptblock returns $false. -Optional - -.PARAMETER ContinueOnError -If this switch parameter is set then the task will not cause the build to fail when an exception is thrown - -.PARAMETER Depends -An array of tasks that this task depends on. They will be executed before the current task is executed. - -.PARAMETER Description -A description of the task. - -.EXAMPLE -A sample build script is shown below: - -task default -depends Test - -task Test -depends Compile, Clean { - "This is a test" -} - -task Compile -depends Clean { - "Compile" -} - -task Clean { - "Clean" -} - -The 'default' task is required and should not contain an 'Action' parameter. -It uses the 'depends' parameter to specify that 'Test' is a dependency - -The 'Test' task uses the 'depends' parameter to specify that 'Compile' and 'Clean' are dependencies -The 'Compile' task depends on the 'Clean' task. - -Note: -The 'Action' parameter is defaulted to the script block following the 'Clean' task. - -The equivalent 'Test' task is shown below: - -task Test -depends Compile, Clean -Action { - $testMessage -} - -The output for the above sample build script is shown below: -Executing task, Clean... -Clean -Executing task, Compile... -Compile -Executing task, Test... -This is a test - -Build Succeeded! - ----------------------------------------------------------------------- -Build Time Report ----------------------------------------------------------------------- -Name Duration ----- -------- -Clean 00:00:00.0065614 -Compile 00:00:00.0133268 -Test 00:00:00.0225964 -Total: 00:00:00.0782496 - -.LINK -Invoke-psake -Properties -Include -FormatTaskName -TaskSetup -TaskTearDown -Assert -#> -[CmdletBinding( - SupportsShouldProcess=$False, - SupportsTransactions=$False, - ConfirmImpact="None", - DefaultParameterSetName="")] - param( - [Parameter(Position=0,Mandatory=1)] - [string]$name = $null, - [Parameter(Position=1,Mandatory=0)] - [scriptblock]$action = $null, - [Parameter(Position=2,Mandatory=0)] - [scriptblock]$preaction = $null, - [Parameter(Position=3,Mandatory=0)] - [scriptblock]$postaction = $null, - [Parameter(Position=4,Mandatory=0)] - [scriptblock]$precondition = $null, - [Parameter(Position=5,Mandatory=0)] - [scriptblock]$postcondition = $null, - [Parameter(Position=6,Mandatory=0)] - [switch]$continueOnError = $false, - [Parameter(Position=7,Mandatory=0)] - [string[]]$depends = @(), - [Parameter(Position=8,Mandatory=0)] - [string]$description = $null + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)] [string]$taskName ) - if ($name.ToLower() -eq 'default') - { - Assert ($action -eq $null) "Error: 'default' task cannot specify an action" - } + Assert $taskName ($msgs.error_invalid_task_name) - $newTask = @{ - Name = $name - DependsOn = $depends - PreAction = $preaction - Action = $action - PostAction = $postaction - Precondition = $precondition - Postcondition = $postcondition - ContinueOnError = $continueOnError - Description = $description - Duration = 0 - } + $taskKey = $taskName.ToLower() - $taskKey = $name.ToLower() - - Assert (!$script:context.Peek().tasks.ContainsKey($taskKey)) "Error: Task, $name, has already been defined." - - $script:context.Peek().tasks.$taskKey = $newTask -} - -function Properties -{ -<# -.SYNOPSIS -Define a scriptblock that contains assignments to variables that will be available to all tasks in the build script - -.DESCRIPTION -A build script may declare a "Properies" function which allows you to define -variables that will be available to all the "Task" functions in the build script. - -.PARAMETER properties -The script block containing all the variable assignment statements -Required - -.EXAMPLE -A sample build script is shown below: - -Properties { - $build_dir = "c:\build" - $connection_string = "datasource=localhost;initial catalog=northwind;integrated security=sspi" -} - -Task default -depends Test - -Task Test -depends Compile, Clean { -} - -Task Compile -depends Clean { -} - -Task Clean { -} - -.LINK -Invoke-psake -Task -Include -FormatTaskName -TaskSetup -TaskTearDown -Assert - -.NOTES -You can have more than 1 "Properties" function defined in the script -#> -[CmdletBinding( - SupportsShouldProcess=$False, - SupportsTransactions=$False, - ConfirmImpact="None", - DefaultParameterSetName="")] - param( - [Parameter(Position=0,Mandatory=1)] - [scriptblock]$properties - ) - $script:context.Peek().properties += $properties -} - -function Include -{ -<# -.SYNOPSIS -Include the functions or code of another powershell script file into the current build script's scope - -.DESCRIPTION -A build script may declare an "includes" function which allows you to define -a file containing powershell code to be included and added to the scope of -the currently running build script. - -.PARAMETER fileNamePathToInclude -A string containing the path and name of the powershell file to include -Required - -.EXAMPLE -A sample build script is shown below: - -Include ".\build_utils.ps1" - -Task default -depends Test - -Task Test -depends Compile, Clean { -} - -Task Compile -depends Clean { -} - -Task Clean { -} - - -.LINK -Invoke-psake -Task -Properties -FormatTaskName -TaskSetup -TaskTearDown -Assert - -.NOTES -You can have more than 1 "Include" function defined in the script -#> -[CmdletBinding( - SupportsShouldProcess=$False, - SupportsTransactions=$False, - ConfirmImpact="None", - DefaultParameterSetName="")] - param( - [Parameter(Position=0,Mandatory=1)] - [string]$fileNamePathToInclude - ) - Assert (test-path $fileNamePathToInclude) "Error: Unable to include $fileNamePathToInclude. File not found." - $script:context.Peek().includes.Enqueue((Resolve-Path $fileNamePathToInclude)); -} - -function FormatTaskName -{ -<# -.SYNOPSIS -Allows you to define a format mask that will be used when psake displays -the task name - -.DESCRIPTION -Allows you to define a format mask that will be used when psake displays -the task name. The default is "Executing task, {0}..." - -.PARAMETER format -A string containing the format mask to use, it should contain a placeholder ({0}) -that will be used to substitute the task name. -Required - -.EXAMPLE -A sample build script is shown below: - -FormatTaskName "[Task: {0}]" - -Task default -depends Test - -Task Test -depends Compile, Clean { -} - -Task Compile -depends Clean { -} - -Task Clean { -} - -You should get the following output: ------------------------------------- - -[Task: Clean] -[Task: Compile] -[Task: Test] - -Build Succeeded - ----------------------------------------------------------------------- -Build Time Report ----------------------------------------------------------------------- -Name Duration ----- -------- -Clean 00:00:00.0043477 -Compile 00:00:00.0102130 -Test 00:00:00.0182858 -Total: 00:00:00.0698071 - -.LINK -Invoke-psake -Include -Task -Properties -TaskSetup -TaskTearDown -Assert -#> -[CmdletBinding( - SupportsShouldProcess=$False, - SupportsTransactions=$False, - ConfirmImpact="None", - DefaultParameterSetName="")] - param( - [Parameter(Position=0,Mandatory=1)] - [string]$format - ) - $script:context.Peek().formatTaskNameString = $format -} - -function TaskSetup -{ -<# -.SYNOPSIS -Adds a scriptblock that will be executed before each task - -.DESCRIPTION -This function will accept a scriptblock that will be executed before each -task in the build script. - -.PARAMETER include -A scriptblock to execute -Required - -.EXAMPLE -A sample build script is shown below: - -Task default -depends Test - -Task Test -depends Compile, Clean { -} - -Task Compile -depends Clean { -} - -Task Clean { -} - -TaskSetup { - "Running 'TaskSetup' for task $script:context.Peek().currentTaskName" -} - -You should get the following output: ------------------------------------- - -Running 'TaskSetup' for task Clean -Executing task, Clean... -Running 'TaskSetup' for task Compile -Executing task, Compile... -Running 'TaskSetup' for task Test -Executing task, Test... - -Build Succeeded - ----------------------------------------------------------------------- -Build Time Report ----------------------------------------------------------------------- -Name Duration ----- -------- -Clean 00:00:00.0054018 -Compile 00:00:00.0123085 -Test 00:00:00.0236915 -Total: 00:00:00.0739437 - -.LINK -Invoke-psake -Include -Task -Properties -FormatTaskName -TaskTearDown -Assert -#> -[CmdletBinding( - SupportsShouldProcess=$False, - SupportsTransactions=$False, - ConfirmImpact="None", - DefaultParameterSetName="")] - param( - [Parameter(Position=0,Mandatory=1)] - [scriptblock]$setup - ) - $script:context.Peek().taskSetupScriptBlock = $setup -} - -function TaskTearDown -{ -<# -.SYNOPSIS -Adds a scriptblock that will be executed after each task - -.DESCRIPTION -This function will accept a scriptblock that will be executed after each -task in the build script. - -.PARAMETER include -A scriptblock to execute -Required - -.EXAMPLE -A sample build script is shown below: - -Task default -depends Test - -Task Test -depends Compile, Clean { -} - -Task Compile -depends Clean { -} - -Task Clean { -} - -TaskTearDown { - "Running 'TaskTearDown' for task $script:context.Peek().currentTaskName" -} - -You should get the following output: ------------------------------------- - -Executing task, Clean... -Running 'TaskTearDown' for task Clean -Executing task, Compile... -Running 'TaskTearDown' for task Compile -Executing task, Test... -Running 'TaskTearDown' for task Test - -Build Succeeded - ----------------------------------------------------------------------- -Build Time Report ----------------------------------------------------------------------- -Name Duration ----- -------- -Clean 00:00:00.0064555 -Compile 00:00:00.0218902 -Test 00:00:00.0309151 -Total: 00:00:00.0858301 - -.LINK -Invoke-psake -Include -Task -Properties -FormatTaskName -TaskSetup -Assert -#> -[CmdletBinding( - SupportsShouldProcess=$False, - SupportsTransactions=$False, - ConfirmImpact="None", - DefaultParameterSetName="")] - param( - [Parameter(Position=0,Mandatory=1)] - [scriptblock]$teardown) - $script:context.Peek().taskTearDownScriptBlock = $teardown -} - -function Invoke-psake -{ -<# -.SYNOPSIS -Runs a psake build script. - -.DESCRIPTION -This function runs a psake build script - -.PARAMETER BuildFile -The psake build script to execute (default: default.ps1). - -.PARAMETER TaskList -A comma-separated list of task names to execute - -.PARAMETER Framework -The version of the .NET framework you want to build. You can append x86 or x64 to force a specific framework. If not specified, x86 or x64 will be detected based on the bitness of the PowerShell process. -Possible values: '1.0', '1.1', '2.0', '2.0x86', '2.0x64', '3.0', '3.0x86', '3.0x64', '3.5', '3.5x86', '3.5x64', '4.0', '4.0x86', '4.0x64' -Default = '3.5' - -.PARAMETER Docs -Prints a list of tasks and their descriptions - -.PARAMETER Parameters -A hashtable containing parameters to be passed into the current build script. These parameters will be processed before the 'Properties' function of the script is processed. This means you can access parameters from within the 'Properties' function! - -.PARAMETER Properties -A hashtable containing properties to be passed into the current build script. These properties will override matching properties that are found in the 'Properties' function of the script. - -.EXAMPLE -Invoke-psake - -Runs the 'default' task in the 'default.ps1' build script in the current directory - -.EXAMPLE -Invoke-psake '.\build.ps1' - -Runs the 'default' task in the '.build.ps1' build script - -.EXAMPLE -Invoke-psake '.\build.ps1' Tests,Package - -Runs the 'Tests' and 'Package' tasks in the '.build.ps1' build script - -.EXAMPLE -Invoke-psake Tests - -If you have your Tasks in the .\default.ps1. This example will run the 'Tests' tasks in the 'default.ps1' build script. - -.EXAMPLE -Invoke-psake 'Tests, Package' - -If you have your Tasks in the .\default.ps1. This example will run the 'Tests' and 'Package' tasks in the 'default.ps1' build script. -NOTE: the quotes around the list of tasks to execute. - -.EXAMPLE -Invoke-psake '.\build.ps1' -docs - -Prints a report of all the tasks and their descriptions and exits - -.EXAMPLE -Invoke-psake .\parameters.ps1 -parameters @{"p1"="v1";"p2"="v2"} - -Runs the build script called 'parameters.ps1' and passes in parameters 'p1' and 'p2' with values 'v1' and 'v2' - -.EXAMPLE -Invoke-psake .\properties.ps1 -properties @{"x"="1";"y"="2"} - -Runs the build script called 'properties.ps1' and passes in parameters 'x' and 'y' with values '1' and '2' - -.OUTPUTS - If there is an exception and '$psake.use_exit_on_error' -eq $true - then runs exit(1) to set the DOS lastexitcode variable - otherwise set the '$psake.build_success variable' to $true or $false depending - on whether an exception was thrown - -.NOTES -When the psake module is loaded a variabled called $psake is created it is a hashtable -containing some variables that can be used to configure psake: - -$psake.use_exit_on_error = $false # determines if psake uses the "exit()" function when an exception occurs -$psake.log_error = $false # determines if the exception details are written to a file -$psake.build_success = $false # indicates that the current build was successful -$psake.version = "4.00" # contains the current version of psake -$psake.build_script_file = $null # contains a System.IO.FileInfo for the current build file -$psake.framework_version = "" # contains the framework version # for the current build - -$psake.use_exit_on_error and $psake.log_error are boolean variables that can be set before you call Invoke-Psake. - -You should see the following when you display the contents of the $psake variable right after importing psake - -PS projects:\psake> Import-Module .\psake.psm1 -PS projects:\psake> $psake - -Name Value ----- ----- -version 4.00 -build_script_file -use_exit_on_error False -build_success False -log_error False -framework_version - -After a build is executed the following $psake values are updated (build_script_file, build_success, and framework_version) - -PS projects:\psake> Invoke-psake .\examples\default.ps1 -Executing task: Clean -Executed Clean! -Executing task: Compile -Executed Compile! -Executing task: Test -Executed Test! - -Build Succeeded! - ----------------------------------------------------------------------- -Build Time Report ----------------------------------------------------------------------- -Name Duration ----- -------- -Clean 00:00:00.0798486 -Compile 00:00:00.0869948 -Test 00:00:00.0958225 -Total: 00:00:00.2712414 - -PS projects:\psake> $psake - -Name Value ----- ----- -version 4.00 -build_script_file C:\Users\Jorge\Documents\Projects\psake\examples\default.ps1 -use_exit_on_error False -build_success True -log_error False -framework_version 3.5 - -.LINK -Task -Include -Properties -FormatTaskName -TaskSetup -TaskTearDown -Assert -#> -[CmdletBinding( - SupportsShouldProcess=$False, - SupportsTransactions=$False, - ConfirmImpact="None", - DefaultParameterSetName="")] - - param( - [Parameter(Position=0,Mandatory=0)] - [string]$buildFile = $script:psake.default_build_file_name, - [Parameter(Position=1,Mandatory=0)] - [string[]]$taskList = @(), - [Parameter(Position=2,Mandatory=0)] - [string]$framework = '3.5', - [Parameter(Position=3,Mandatory=0)] - [switch]$docs = $false, - [Parameter(Position=4,Mandatory=0)] - [System.Collections.Hashtable]$parameters = @{}, - [Parameter(Position=5, Mandatory=0)] - [System.Collections.Hashtable]$properties = @{} - ) - - Begin - { - $script:psake.build_success = $false - $script:psake.framework_version = $framework - - if ($script:context -eq $null) - { - $script:context = New-Object System.Collections.Stack + if ($currentContext.aliases.Contains($taskKey)) { + $taskName = $currentContext.aliases.$taskKey.Name + $taskKey = $taskName.ToLower() } - $script:context.push(@{ - "formatTaskNameString" = "Executing task: {0}"; - "taskSetupScriptBlock" = $null; - "taskTearDownScriptBlock" = $null; - "executedTasks" = New-Object System.Collections.Stack; - "callStack" = New-Object System.Collections.Stack; - "originalEnvPath" = $env:path; - "originalDirectory" = Get-Location; - "originalErrorActionPreference" = $global:ErrorActionPreference; - "tasks" = @{}; - "properties" = @(); - "includes" = New-Object System.Collections.Queue; - }) - } + $currentContext = $psake.context.Peek() - Process - { - try - { - $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() + Assert ($currentContext.tasks.Contains($taskKey)) ($msgs.error_task_name_does_not_exist -f $taskName) - <# - If the default.ps1 file exists and the given "buildfile" isn't found assume that the given - $buildFile is actually the target Tasks to execute in the default.ps1 script. - #> - if((Test-Path $script:psake.default_build_file_name ) -and !(test-path $buildFile)) { - $list = New-Object System.Collections.ArrayList - foreach($t in $buildFile.Split(',')) { - $t1 = $t.Trim() - if($t1 -ne $null -or $t1 -ne "") { - $list.Add($t1) + if ($currentContext.executedTasks.Contains($taskKey)) { return } + + Assert (!$currentContext.callStack.Contains($taskKey)) ($msgs.error_circular_reference -f $taskName) + + $currentContext.callStack.Push($taskKey) + + $task = $currentContext.tasks.$taskKey + + $precondition_is_valid = & $task.Precondition + + if (!$precondition_is_valid) { + WriteColoredOutput ($msgs.precondition_was_false -f $taskName) -foregroundcolor Cyan + } else { + if ($taskKey -ne 'default') { + + if ($task.PreAction -or $task.PostAction) { + Assert ($task.Action -ne $null) ($msgs.error_missing_action_parameter -f $taskName) + } + + if ($task.Action) { + try { + foreach($childTask in $task.DependsOn) { + Invoke-Task $childTask + } + + $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() + $currentContext.currentTaskName = $taskName + + & $currentContext.taskSetupScriptBlock + + if ($task.PreAction) { + & $task.PreAction + } + + if ($currentContext.config.taskNameFormat -is [ScriptBlock]) { + & $currentContext.config.taskNameFormat $taskName + } else { + WriteColoredOutput ($currentContext.config.taskNameFormat -f $taskName) -foregroundcolor Cyan + } + + foreach ($variable in $task.requiredVariables) { + Assert ((test-path "variable:$variable") -and ((get-variable $variable).Value -ne $null)) ($msgs.required_variable_not_set -f $variable, $taskName) + } + + & $task.Action + + if ($task.PostAction) { + & $task.PostAction + } + + & $currentContext.taskTearDownScriptBlock + $task.Duration = $stopwatch.Elapsed + } catch { + if ($task.ContinueOnError) { + "-"*70 + WriteColoredOutput ($msgs.continue_on_error -f $taskName,$_) -foregroundcolor Yellow + "-"*70 + $task.Duration = $stopwatch.Elapsed + } else { + throw $_ + } + } + } else { + # no action was specified but we still execute all the dependencies + foreach($childTask in $task.DependsOn) { + Invoke-Task $childTask + } + } + } else { + foreach($childTask in $task.DependsOn) { + Invoke-Task $childTask + } } - } - $taskList = $list.ToArray() - $buildFile = $script:psake.default_build_file_name + + Assert (& $task.Postcondition) ($msgs.postcondition_failed -f $taskName) } - # Execute the build file to set up the tasks and defaults - Assert (test-path $buildFile) "Error: Could not find the build file, $buildFile." + $poppedTaskKey = $currentContext.callStack.Pop() + Assert ($poppedTaskKey -eq $taskKey) ($msgs.error_corrupt_callstack -f $taskKey,$poppedTaskKey) - $script:psake.build_script_file = dir $buildFile - set-location $script:psake.build_script_file.Directory - . $script:psake.build_script_file.FullName - - if ($docs) - { - Write-Documentation - Cleanup-Environment - return - } - - Configure-BuildEnvironment - - # N.B. The initial dot (.) indicates that variables initialized/modified - # in the propertyBlock are available in the parent scope. - while ($script:context.Peek().includes.Count -gt 0) - { - $includeBlock = $script:context.Peek().includes.Dequeue() - . $includeBlock - } - - foreach($key in $parameters.keys) - { - if (test-path "variable:\$key") - { - set-item -path "variable:\$key" -value $parameters.$key | out-null - } - else - { - new-item -path "variable:\$key" -value $parameters.$key | out-null - } - } - - foreach($propertyBlock in $script:context.Peek().properties) - { - . $propertyBlock - } - - foreach($key in $properties.keys) - { - if (test-path "variable:\$key") - { - set-item -path "variable:\$key" -value $properties.$key | out-null - } - } - - # Execute the list of tasks or the default task - if($taskList.Length -ne 0) - { - foreach($task in $taskList) - { - ExecuteTask $task - } - } - elseif ($script:context.Peek().tasks.default -ne $null) - { - ExecuteTask default - } - else - { - throw 'Error: default task required' - } - - $stopwatch.Stop() - - "`nBuild Succeeded!`n" - - Write-TaskTimeSummary - - $script:psake.build_success = $true - } - catch - { - #Append detailed exception and script variables to error log file - if ($script:psake.log_error) - { - $errorLogFile = "psake-error-log-{0}.log" -f ([DateTime]::Now.ToString("yyyyMMdd")) - "-" * 70 >> $errorLogFile - "{0}: An Error Occurred. See Error Details Below: " -f [DateTime]::Now >>$errorLogFile - "-" * 70 >> $errorLogFile - Resolve-Error $_ >> $errorLogFile - "-" * 70 >> $errorLogFile - "Script Variables" >> $errorLogFile - "-" * 70 >> $errorLogFile - Get-Variable -scope script >> $errorLogFile - } - - $buildFileName = Split-Path $buildFile -leaf - if (test-path $buildFile) { $buildFileName = $script:psake.build_script_file.Name } - Write-Host -foregroundcolor Red ($buildFileName + ":" + $_) - - if ($script:psake.use_exit_on_error) - { - exit(1) - } - else - { - $script:psake.build_success = $false - } - } - } #Process - - End - { - # Clear out any global variables - Cleanup-Environment - [void]$script:context.Pop() - } + $currentContext.executedTasks.Push($taskKey) } -Export-ModuleMember -Function "Invoke-psake","Task","Properties","Include","FormatTaskName","TaskSetup","TaskTearDown","Assert","Exec" \ No newline at end of file +# .ExternalHelp psake.psm1-help.xml +function Exec +{ + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)][scriptblock]$cmd, + [Parameter(Position=1,Mandatory=0)][string]$errorMessage = ($msgs.error_bad_command -f $cmd), + [Parameter(Position=2,Mandatory=0)][int]$maxRetries = 0, + [Parameter(Position=3,Mandatory=0)][string]$retryTriggerErrorPattern = $null + ) + + $tryCount = 1 + + do { + try { + $global:lastexitcode = 0 + & $cmd + if ($lastexitcode -ne 0) { + throw ("Exec: " + $errorMessage) + } + break + } + catch [Exception] + { + if ($tryCount -gt $maxRetries) { + throw $_ + } + + if ($retryTriggerErrorPattern -ne $null) { + $isMatch = [regex]::IsMatch($_.Exception.Message, $retryTriggerErrorPattern) + + if ($isMatch -eq $false) { + throw $_ + } + } + + Write-Host "Try $tryCount failed, retrying again in 1 second..." + + $tryCount++ + + [System.Threading.Thread]::Sleep([System.TimeSpan]::FromSeconds(1)) + } + } + while ($true) +} + +# .ExternalHelp psake.psm1-help.xml +function Assert +{ + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)]$conditionToCheck, + [Parameter(Position=1,Mandatory=1)]$failureMessage + ) + if (!$conditionToCheck) { + throw ("Assert: " + $failureMessage) + } +} + +# .ExternalHelp psake.psm1-help.xml +function Task +{ + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)][string]$name = $null, + [Parameter(Position=1,Mandatory=0)][scriptblock]$action = $null, + [Parameter(Position=2,Mandatory=0)][scriptblock]$preaction = $null, + [Parameter(Position=3,Mandatory=0)][scriptblock]$postaction = $null, + [Parameter(Position=4,Mandatory=0)][scriptblock]$precondition = {$true}, + [Parameter(Position=5,Mandatory=0)][scriptblock]$postcondition = {$true}, + [Parameter(Position=6,Mandatory=0)][switch]$continueOnError = $false, + [Parameter(Position=7,Mandatory=0)][string[]]$depends = @(), + [Parameter(Position=8,Mandatory=0)][string[]]$requiredVariables = @(), + [Parameter(Position=9,Mandatory=0)][string]$description = $null, + [Parameter(Position=10,Mandatory=0)][string]$alias = $null, + [Parameter(Position=11,Mandatory=0)][string]$maxRetries = 0, + [Parameter(Position=12,Mandatory=0)][string]$retryTriggerErrorPattern = $null + ) + if ($name -eq 'default') { + Assert (!$action) ($msgs.error_default_task_cannot_have_action) + } + + $newTask = @{ + Name = $name + DependsOn = $depends + PreAction = $preaction + Action = $action + PostAction = $postaction + Precondition = $precondition + Postcondition = $postcondition + ContinueOnError = $continueOnError + Description = $description + Duration = [System.TimeSpan]::Zero + RequiredVariables = $requiredVariables + Alias = $alias + MaxRetries = $maxRetries + RetryTriggerErrorPattern = $retryTriggerErrorPattern + } + + $taskKey = $name.ToLower() + + $currentContext = $psake.context.Peek() + + Assert (!$currentContext.tasks.ContainsKey($taskKey)) ($msgs.error_duplicate_task_name -f $name) + + $currentContext.tasks.$taskKey = $newTask + + if($alias) + { + $aliasKey = $alias.ToLower() + + Assert (!$currentContext.aliases.ContainsKey($aliasKey)) ($msgs.error_duplicate_alias_name -f $alias) + + $currentContext.aliases.$aliasKey = $newTask + } +} + +# .ExternalHelp psake.psm1-help.xml +function Properties { + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)][scriptblock]$properties + ) + $psake.context.Peek().properties += $properties +} + +# .ExternalHelp psake.psm1-help.xml +function Include { + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)][string]$fileNamePathToInclude + ) + Assert (test-path $fileNamePathToInclude -pathType Leaf) ($msgs.error_invalid_include_path -f $fileNamePathToInclude) + $psake.context.Peek().includes.Enqueue((Resolve-Path $fileNamePathToInclude)); +} + +# .ExternalHelp psake.psm1-help.xml +function FormatTaskName { + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)]$format + ) + $psake.context.Peek().config.taskNameFormat = $format +} + +# .ExternalHelp psake.psm1-help.xml +function TaskSetup { + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)][scriptblock]$setup + ) + $psake.context.Peek().taskSetupScriptBlock = $setup +} + +# .ExternalHelp psake.psm1-help.xml +function TaskTearDown { + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)][scriptblock]$teardown + ) + $psake.context.Peek().taskTearDownScriptBlock = $teardown +} + +# .ExternalHelp psake.psm1-help.xml +function Framework { + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)][string]$framework + ) + $psake.context.Peek().config.framework = $framework + ConfigureBuildEnvironment +} + +# .ExternalHelp psake.psm1-help.xml +function Invoke-psake { + [CmdletBinding()] + param( + [Parameter(Position = 0, Mandatory = 0)][string] $buildFile, + [Parameter(Position = 1, Mandatory = 0)][string[]] $taskList = @(), + [Parameter(Position = 2, Mandatory = 0)][string] $framework, + [Parameter(Position = 3, Mandatory = 0)][switch] $docs = $false, + [Parameter(Position = 4, Mandatory = 0)][hashtable] $parameters = @{}, + [Parameter(Position = 5, Mandatory = 0)][hashtable] $properties = @{}, + [Parameter(Position = 6, Mandatory = 0)][alias("init")][scriptblock] $initialization = {}, + [Parameter(Position = 7, Mandatory = 0)][switch] $nologo = $false, + [Parameter(Position = 8, Mandatory = 0)][switch] $detailedDocs = $false + ) + try { + if (-not $nologo) { + "psake version {0}`nCopyright (c) 2010-2014 James Kovacs & Contributors`n" -f $psake.version + } + + if (!$buildFile) { + $buildFile = $psake.config_default.buildFileName + } + elseif (!(test-path $buildFile -pathType Leaf) -and (test-path $psake.config_default.buildFileName -pathType Leaf)) { + # If the $config.buildFileName file exists and the given "buildfile" isn 't found assume that the given + # $buildFile is actually the target Tasks to execute in the $config.buildFileName script. + $taskList = $buildFile.Split(', ') + $buildFile = $psake.config_default.buildFileName + } + + # Execute the build file to set up the tasks and defaults + Assert (test-path $buildFile -pathType Leaf) ($msgs.error_build_file_not_found -f $buildFile) + + $psake.build_script_file = get-item $buildFile + $psake.build_script_dir = $psake.build_script_file.DirectoryName + $psake.build_success = $false + + $psake.context.push(@{ + "taskSetupScriptBlock" = {}; + "taskTearDownScriptBlock" = {}; + "executedTasks" = new-object System.Collections.Stack; + "callStack" = new-object System.Collections.Stack; + "originalEnvPath" = $env:path; + "originalDirectory" = get-location; + "originalErrorActionPreference" = $global:ErrorActionPreference; + "tasks" = @{}; + "aliases" = @{}; + "properties" = @(); + "includes" = new-object System.Collections.Queue; + "config" = CreateConfigurationForNewContext $buildFile $framework + }) + + LoadConfiguration $psake.build_script_dir + + $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() + + set-location $psake.build_script_dir + + LoadModules + + $frameworkOldValue = $framework + . $psake.build_script_file.FullName + + $currentContext = $psake.context.Peek() + + if ($framework -ne $frameworkOldValue) { + writecoloredoutput $msgs.warning_deprecated_framework_variable -foregroundcolor Yellow + $currentContext.config.framework = $framework + } + + ConfigureBuildEnvironment + + while ($currentContext.includes.Count -gt 0) { + $includeFilename = $currentContext.includes.Dequeue() + . $includeFilename + } + + if ($docs -or $detailedDocs) { + WriteDocumentation($detailedDocs) + CleanupEnvironment + return + } + + foreach ($key in $parameters.keys) { + if (test-path "variable:\$key") { + set-item -path "variable:\$key" -value $parameters.$key -WhatIf:$false -Confirm:$false | out-null + } else { + new-item -path "variable:\$key" -value $parameters.$key -WhatIf:$false -Confirm:$false | out-null + } + } + + # The initial dot (.) indicates that variables initialized/modified in the propertyBlock are available in the parent scope. + foreach ($propertyBlock in $currentContext.properties) { + . $propertyBlock + } + + foreach ($key in $properties.keys) { + if (test-path "variable:\$key") { + set-item -path "variable:\$key" -value $properties.$key -WhatIf:$false -Confirm:$false | out-null + } + } + + # Simple dot sourcing will not work. We have to force the script block into our + # module's scope in order to initialize variables properly. + . $MyInvocation.MyCommand.Module $initialization + + # Execute the list of tasks or the default task + if ($taskList) { + foreach ($task in $taskList) { + invoke-task $task + } + } elseif ($currentContext.tasks.default) { + invoke-task default + } else { + throw $msgs.error_no_default_task + } + + WriteColoredOutput ("`n" + $msgs.build_success + "`n") -foregroundcolor Green + + WriteTaskTimeSummary $stopwatch.Elapsed + + $psake.build_success = $true + } catch { + $currentConfig = GetCurrentConfigurationOrDefault + if ($currentConfig.verboseError) { + $error_message = "{0}: An Error Occurred. See Error Details Below: `n" -f (Get-Date) + $error_message += ("-" * 70) + "`n" + $error_message += "Error: {0}`n" -f (ResolveError $_ -Short) + $error_message += ("-" * 70) + "`n" + $error_message += ResolveError $_ + $error_message += ("-" * 70) + "`n" + $error_message += "Script Variables" + "`n" + $error_message += ("-" * 70) + "`n" + $error_message += get-variable -scope script | format-table | out-string + } else { + # ($_ | Out-String) gets error messages with source information included. + $error_message = "Error: {0}: `n{1}" -f (Get-Date), (ResolveError $_ -Short) + } + + $psake.build_success = $false + + # if we are running in a nested scope (i.e. running a psake script from a psake script) then we need to re-throw the exception + # so that the parent script will fail otherwise the parent script will report a successful build + $inNestedScope = ($psake.context.count -gt 1) + if ( $inNestedScope ) { + throw $_ + } else { + if (!$psake.run_by_psake_build_tester) { + WriteColoredOutput $error_message -foregroundcolor Red + } + } + } finally { + CleanupEnvironment + } +} + +#-- Private Module Functions --# +function WriteColoredOutput { + param( + [string] $message, + [System.ConsoleColor] $foregroundcolor + ) + + $currentConfig = GetCurrentConfigurationOrDefault + if ($currentConfig.coloredOutput -eq $true) { + if (($Host.UI -ne $null) -and ($Host.UI.RawUI -ne $null) -and ($Host.UI.RawUI.ForegroundColor -ne $null)) { + $previousColor = $Host.UI.RawUI.ForegroundColor + $Host.UI.RawUI.ForegroundColor = $foregroundcolor + } + } + + $message + + if ($previousColor -ne $null) { + $Host.UI.RawUI.ForegroundColor = $previousColor + } +} + +function LoadModules { + $currentConfig = $psake.context.peek().config + if ($currentConfig.modules) { + + $scope = $currentConfig.moduleScope + + $global = [string]::Equals($scope, "global", [StringComparison]::CurrentCultureIgnoreCase) + + $currentConfig.modules | foreach { + resolve-path $_ | foreach { + "Loading module: $_" + $module = import-module $_ -passthru -DisableNameChecking -global:$global + if (!$module) { + throw ($msgs.error_loading_module -f $_.Name) + } + } + } + "" + } +} + +function LoadConfiguration { + param( + [string] $configdir = $PSScriptRoot + ) + + $psakeConfigFilePath = (join-path $configdir "psake-config.ps1") + + if (test-path $psakeConfigFilePath -pathType Leaf) { + try { + $config = GetCurrentConfigurationOrDefault + . $psakeConfigFilePath + } catch { + throw "Error Loading Configuration from psake-config.ps1: " + $_ + } + } +} + +function GetCurrentConfigurationOrDefault() { + if ($psake.context.count -gt 0) { + return $psake.context.peek().config + } else { + return $psake.config_default + } +} + +function CreateConfigurationForNewContext { + param( + [string] $buildFile, + [string] $framework + ) + + $previousConfig = GetCurrentConfigurationOrDefault + + $config = new-object psobject -property @{ + buildFileName = $previousConfig.buildFileName; + framework = $previousConfig.framework; + taskNameFormat = $previousConfig.taskNameFormat; + verboseError = $previousConfig.verboseError; + coloredOutput = $previousConfig.coloredOutput; + modules = $previousConfig.modules; + moduleScope = $previousConfig.moduleScope; + } + + if ($framework) { + $config.framework = $framework; + } + + if ($buildFile) { + $config.buildFileName = $buildFile; + } + + return $config +} + +function ConfigureBuildEnvironment { + $framework = $psake.context.peek().config.framework + if ($framework -cmatch '^((?:\d+\.\d+)(?:\.\d+){0,1})(x86|x64){0,1}$') { + $versionPart = $matches[1] + $bitnessPart = $matches[2] + } else { + throw ($msgs.error_invalid_framework -f $framework) + } + $versions = $null + $buildToolsVersions = $null + switch ($versionPart) { + '1.0' { + $versions = @('v1.0.3705') + } + '1.1' { + $versions = @('v1.1.4322') + } + '2.0' { + $versions = @('v2.0.50727') + } + '3.0' { + $versions = @('v2.0.50727') + } + '3.5' { + $versions = @('v3.5', 'v2.0.50727') + } + '4.0' { + $versions = @('v4.0.30319') + } + '4.5.1' { + $versions = @('v4.0.30319') + $buildToolsVersions = @('14.0', '12.0') + } + default { + throw ($msgs.error_unknown_framework -f $versionPart, $framework) + } + } + + $bitness = 'Framework' + if ($versionPart -ne '1.0' -and $versionPart -ne '1.1') { + switch ($bitnessPart) { + 'x86' { + $bitness = 'Framework' + $buildToolsKey = 'MSBuildToolsPath32' + } + 'x64' { + $bitness = 'Framework64' + $buildToolsKey = 'MSBuildToolsPath' + } + { [string]::IsNullOrEmpty($_) } { + $ptrSize = [System.IntPtr]::Size + switch ($ptrSize) { + 4 { + $bitness = 'Framework' + $buildToolsKey = 'MSBuildToolsPath32' + } + 8 { + $bitness = 'Framework64' + $buildToolsKey = 'MSBuildToolsPath' + } + default { + throw ($msgs.error_unknown_pointersize -f $ptrSize) + } + } + } + default { + throw ($msgs.error_unknown_bitnesspart -f $bitnessPart, $framework) + } + } + } + $frameworkDirs = @() + if ($buildToolsVersions -ne $null) { + foreach($ver in $buildToolsVersions) { + if (Test-Path "HKLM:\SOFTWARE\Microsoft\MSBuild\ToolsVersions\$ver") { + $frameworkDirs += (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\MSBuild\ToolsVersions\$ver" -Name $buildToolsKey).$buildToolsKey + } + } + } + $frameworkDirs = $frameworkDirs + @($versions | foreach { "$env:windir\Microsoft.NET\$bitness\$_\" }) + + for ($i = 0; $i -lt $frameworkDirs.Count; $i++) { + $dir = $frameworkDirs[$i] + if ($dir -Match "\$\(Registry:HKEY_LOCAL_MACHINE(.*?)@(.*)\)") { + $key = "HKLM:" + $matches[1] + $name = $matches[2] + $dir = (Get-ItemProperty -Path $key -Name $name).$name + $frameworkDirs[$i] = $dir + } + } + + $frameworkDirs | foreach { Assert (test-path $_ -pathType Container) ($msgs.error_no_framework_install_dir_found -f $_)} + + $env:path = ($frameworkDirs -join ";") + ";$env:path" + # if any error occurs in a PS function then "stop" processing immediately + # this does not effect any external programs that return a non-zero exit code + $global:ErrorActionPreference = "Stop" +} + +function CleanupEnvironment { + if ($psake.context.Count -gt 0) { + $currentContext = $psake.context.Peek() + $env:path = $currentContext.originalEnvPath + Set-Location $currentContext.originalDirectory + $global:ErrorActionPreference = $currentContext.originalErrorActionPreference + [void] $psake.context.Pop() + } +} + +function SelectObjectWithDefault +{ + [CmdletBinding()] + param( + [Parameter(ValueFromPipeline=$true)] + [PSObject] + $InputObject, + [string] + $Name, + $Value + ) + + process { + if ($_ -eq $null) { $Value } + elseif ($_ | Get-Member -Name $Name) { + $_.$Name + } + elseif (($_ -is [Hashtable]) -and ($_.Keys -contains $Name)) { + $_.$Name + } + else { $Value } + } +} + +# borrowed from Jeffrey Snover http://blogs.msdn.com/powershell/archive/2006/12/07/resolve-error.aspx +# modified to better handle SQL errors +function ResolveError +{ + [CmdletBinding()] + param( + [Parameter(ValueFromPipeline=$true)] + $ErrorRecord=$Error[0], + [Switch] + $Short + ) + + process { + if ($_ -eq $null) { $_ = $ErrorRecord } + $ex = $_.Exception + + if (-not $Short) { + $error_message = "`nErrorRecord:{0}ErrorRecord.InvocationInfo:{1}Exception:`n{2}" + $formatted_errorRecord = $_ | format-list * -force | out-string + $formatted_invocationInfo = $_.InvocationInfo | format-list * -force | out-string + $formatted_exception = '' + + $i = 0 + while ($ex -ne $null) { + $i++ + $formatted_exception += ("$i" * 70) + "`n" + + ($ex | format-list * -force | out-string) + "`n" + $ex = $ex | SelectObjectWithDefault -Name 'InnerException' -Value $null + } + + return $error_message -f $formatted_errorRecord, $formatted_invocationInfo, $formatted_exception + } + + $lastException = @() + while ($ex -ne $null) { + $lastMessage = $ex | SelectObjectWithDefault -Name 'Message' -Value '' + $lastException += ($lastMessage -replace "`n", '') + if ($ex -is [Data.SqlClient.SqlException]) { + $lastException += "(Line [$($ex.LineNumber)] " + + "Procedure [$($ex.Procedure)] Class [$($ex.Class)] " + + " Number [$($ex.Number)] State [$($ex.State)] )" + } + $ex = $ex | SelectObjectWithDefault -Name 'InnerException' -Value $null + } + $shortException = $lastException -join ' --> ' + + $header = $null + $current = $_ + $header = (($_.InvocationInfo | + SelectObjectWithDefault -Name 'PositionMessage' -Value '') -replace "`n", ' '), + ($_ | SelectObjectWithDefault -Name 'Message' -Value ''), + ($_ | SelectObjectWithDefault -Name 'Exception' -Value '') | + ? { -not [String]::IsNullOrEmpty($_) } | + Select -First 1 + + $delimiter = '' + if ((-not [String]::IsNullOrEmpty($header)) -and + (-not [String]::IsNullOrEmpty($shortException))) + { $delimiter = ' [<<==>>] ' } + + return "$($header)$($delimiter)Exception: $($shortException)" + } +} + +function WriteDocumentation($showDetailed) { + $currentContext = $psake.context.Peek() + + if ($currentContext.tasks.default) { + $defaultTaskDependencies = $currentContext.tasks.default.DependsOn + } else { + $defaultTaskDependencies = @() + } + + $docs = $currentContext.tasks.Keys | foreach-object { + if ($_ -eq "default") { + return + } + + $task = $currentContext.tasks.$_ + new-object PSObject -property @{ + Name = $task.Name; + Alias = $task.Alias; + Description = $task.Description; + "Depends On" = $task.DependsOn -join ", " + Default = if ($defaultTaskDependencies -contains $task.Name) { $true } + } + } + if ($showDetailed) { + $docs | sort 'Name' | format-list -property Name,Alias,Description,"Depends On",Default + } else { + $docs | sort 'Name' | format-table -autoSize -wrap -property Name,Alias,"Depends On",Default,Description + } + +} + +function WriteTaskTimeSummary($invokePsakeDuration) { + if ($psake.context.count -gt 0) { + "-" * 70 + "Build Time Report" + "-" * 70 + $list = @() + $currentContext = $psake.context.Peek() + while ($currentContext.executedTasks.Count -gt 0) { + $taskKey = $currentContext.executedTasks.Pop() + $task = $currentContext.tasks.$taskKey + if ($taskKey -eq "default") { + continue + } + $list += new-object PSObject -property @{ + Name = $task.Name; + Duration = $task.Duration + } + } + [Array]::Reverse($list) + $list += new-object PSObject -property @{ + Name = "Total:"; + Duration = $invokePsakeDuration + } + # using "out-string | where-object" to filter out the blank line that format-table prepends + $list | format-table -autoSize -property Name,Duration | out-string -stream | where-object { $_ } + } +} + +DATA msgs { +convertfrom-stringdata @' + error_invalid_task_name = Task name should not be null or empty string. + error_task_name_does_not_exist = Task {0} does not exist. + error_circular_reference = Circular reference found for task {0}. + error_missing_action_parameter = Action parameter must be specified when using PreAction or PostAction parameters for task {0}. + error_corrupt_callstack = Call stack was corrupt. Expected {0}, but got {1}. + error_invalid_framework = Invalid .NET Framework version, {0} specified. + error_unknown_framework = Unknown .NET Framework version, {0} specified in {1}. + error_unknown_pointersize = Unknown pointer size ({0}) returned from System.IntPtr. + error_unknown_bitnesspart = Unknown .NET Framework bitness, {0}, specified in {1}. + error_no_framework_install_dir_found = No .NET Framework installation directory found at {0}. + error_bad_command = Error executing command {0}. + error_default_task_cannot_have_action = 'default' task cannot specify an action. + error_duplicate_task_name = Task {0} has already been defined. + error_duplicate_alias_name = Alias {0} has already been defined. + error_invalid_include_path = Unable to include {0}. File not found. + error_build_file_not_found = Could not find the build file {0}. + error_no_default_task = 'default' task required. + error_loading_module = Error loading module {0}. + warning_deprecated_framework_variable = Warning: Using global variable $framework to set .NET framework version used is deprecated. Instead use Framework function or configuration file psake-config.ps1. + required_variable_not_set = Variable {0} must be set to run task {1}. + postcondition_failed = Postcondition failed for task {0}. + precondition_was_false = Precondition was false, not executing task {0}. + continue_on_error = Error in task {0}. {1} + build_success = Build Succeeded! +'@ +} + +import-localizeddata -bindingvariable msgs -erroraction silentlycontinue + +$script:psake = @{} +$psake.version = "4.4.1" # contains the current version of psake +$psake.context = new-object system.collections.stack # holds onto the current state of all variables +$psake.run_by_psake_build_tester = $false # indicates that build is being run by psake-BuildTester +$psake.config_default = new-object psobject -property @{ + buildFileName = "default.ps1"; + framework = "4.0"; + taskNameFormat = "Executing {0}"; + verboseError = $false; + coloredOutput = $true; + modules = $null; + moduleScope = ""; +} # contains default configuration, can be overriden in psake-config.ps1 in directory with psake.psm1 or in directory with current build script + +$psake.build_success = $false # indicates that the current build was successful +$psake.build_script_file = $null # contains a System.IO.FileInfo for the current build script +$psake.build_script_dir = "" # contains a string with fully-qualified path to current build script + +LoadConfiguration + +export-modulemember -function Invoke-psake, Invoke-Task, Task, Properties, Include, FormatTaskName, TaskSetup, TaskTearDown, Framework, Assert, Exec -variable psake \ No newline at end of file