##// END OF EJS Templates
JsonXmlReader performance tuning...
cin -
r229:5f7a3e1d32b9 v2
parent child
Show More
@@ -0,0 +1,6
1 <?xml version="1.0" encoding="utf-8" ?>
2 <configuration>
3 <startup>
4 <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
5 </startup>
6 </configuration> No newline at end of file
@@ -0,0 +1,68
1 <?xml version="1.0" encoding="utf-8"?>
2 <Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
4 <PropertyGroup>
5 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
6 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
7 <ProjectGuid>{100DFEB0-75BE-436F-ADDF-1F46EF433F46}</ProjectGuid>
8 <OutputType>Exe</OutputType>
9 <AppDesignerFolder>Properties</AppDesignerFolder>
10 <RootNamespace>Implab.Playground</RootNamespace>
11 <AssemblyName>Implab.Playground</AssemblyName>
12 <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
13 <FileAlignment>512</FileAlignment>
14 <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
15 </PropertyGroup>
16 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
17 <PlatformTarget>AnyCPU</PlatformTarget>
18 <DebugSymbols>true</DebugSymbols>
19 <DebugType>full</DebugType>
20 <Optimize>false</Optimize>
21 <OutputPath>bin\Debug\</OutputPath>
22 <DefineConstants>DEBUG;TRACE</DefineConstants>
23 <ErrorReport>prompt</ErrorReport>
24 <WarningLevel>4</WarningLevel>
25 </PropertyGroup>
26 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
27 <PlatformTarget>AnyCPU</PlatformTarget>
28 <DebugType>pdbonly</DebugType>
29 <Optimize>true</Optimize>
30 <OutputPath>bin\Release\</OutputPath>
31 <DefineConstants>TRACE</DefineConstants>
32 <ErrorReport>prompt</ErrorReport>
33 <WarningLevel>4</WarningLevel>
34 <Prefer32Bit>true</Prefer32Bit>
35 <DebugSymbols>true</DebugSymbols>
36 </PropertyGroup>
37 <ItemGroup>
38 <Reference Include="System" />
39 <Reference Include="System.Core" />
40 <Reference Include="System.Xml.Linq" />
41 <Reference Include="System.Data.DataSetExtensions" />
42 <Reference Include="Microsoft.CSharp" />
43 <Reference Include="System.Data" />
44 <Reference Include="System.Net.Http" />
45 <Reference Include="System.Xml" />
46 </ItemGroup>
47 <ItemGroup>
48 <Compile Include="Program.cs" />
49 <Compile Include="Properties\AssemblyInfo.cs" />
50 </ItemGroup>
51 <ItemGroup>
52 <None Include="App.config" />
53 </ItemGroup>
54 <ItemGroup>
55 <ProjectReference Include="..\Implab\Implab.csproj">
56 <Project>{f550f1f8-8746-4ad0-9614-855f4c4b7f05}</Project>
57 <Name>Implab</Name>
58 </ProjectReference>
59 </ItemGroup>
60 <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
61 <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
62 Other similar extension points exist, see Microsoft.Common.targets.
63 <Target Name="BeforeBuild">
64 </Target>
65 <Target Name="AfterBuild">
66 </Target>
67 -->
68 </Project> No newline at end of file
@@ -0,0 +1,42
1 using Implab.Formats.Json;
2 using Implab.Xml;
3 using System;
4 using System.Collections.Generic;
5 using System.IO;
6 using System.Linq;
7 using System.Text;
8 using System.Threading.Tasks;
9 using System.Xml;
10 using System.Xml.Serialization;
11
12 namespace Implab.Playground {
13 public class Program {
14
15 [XmlRoot(Namespace = "XmlSimpleData")]
16 public class XmlSimpleModel {
17 [XmlElement]
18 public string Name { get; set; }
19
20 [XmlElement]
21 public int Order { get; set; }
22
23 [XmlElement]
24 public string[] Items { get; set; }
25
26 }
27
28 static void Main(string[] args) {
29 var model = new XmlSimpleModel {
30 Name = "Tablet",
31 Order = 10,
32 Items = new string[] { "z1", "z2", "z3" }
33 };
34
35 var doc = SerializationHelpers.SerializeAsXmlDocument(model);
36
37 var m2 = SerializationHelpers.DeserializeFromXmlNode<XmlSimpleModel>(doc.DocumentElement);
38
39 Console.WriteLine("done");
40 }
41 }
42 }
@@ -0,0 +1,36
1 using System.Reflection;
2 using System.Runtime.CompilerServices;
3 using System.Runtime.InteropServices;
4
5 // General Information about an assembly is controlled through the following
6 // set of attributes. Change these attribute values to modify the information
7 // associated with an assembly.
8 [assembly: AssemblyTitle("Implab.Playground")]
9 [assembly: AssemblyDescription("")]
10 [assembly: AssemblyConfiguration("")]
11 [assembly: AssemblyCompany("")]
12 [assembly: AssemblyProduct("Implab.Playground")]
13 [assembly: AssemblyCopyright("Copyright © 2017")]
14 [assembly: AssemblyTrademark("")]
15 [assembly: AssemblyCulture("")]
16
17 // Setting ComVisible to false makes the types in this assembly not visible
18 // to COM components. If you need to access a type in this assembly from
19 // COM, set the ComVisible attribute to true on that type.
20 [assembly: ComVisible(false)]
21
22 // The following GUID is for the ID of the typelib if this project is exposed to COM
23 [assembly: Guid("100dfeb0-75be-436f-addf-1f46ef433f46")]
24
25 // Version information for an assembly consists of the following four values:
26 //
27 // Major Version
28 // Minor Version
29 // Build Number
30 // Revision
31 //
32 // You can specify all the values or you can default the Build and Revision Numbers
33 // by using the '*' as shown below:
34 // [assembly: AssemblyVersion("1.0.*")]
35 [assembly: AssemblyVersion("1.0.0.0")]
36 [assembly: AssemblyFileVersion("1.0.0.0")]
@@ -0,0 +1,75
1 <?xml version="1.0" encoding="UTF-8"?>
2 <VSPerformanceSession Version="1.00">
3 <Options>
4 <Solution>Implab.sln</Solution>
5 <CollectionMethod>Sampling</CollectionMethod>
6 <AllocationMethod>None</AllocationMethod>
7 <AddReport>true</AddReport>
8 <ResourceBasedAnalysisSelected>true</ResourceBasedAnalysisSelected>
9 <UniqueReport>Timestamp</UniqueReport>
10 <SamplingMethod>Cycles</SamplingMethod>
11 <CycleCount>50000</CycleCount>
12 <PageFaultCount>10</PageFaultCount>
13 <SysCallCount>10</SysCallCount>
14 <SamplingCounter Name="" ReloadValue="00000000000f4240" DisplayName="" />
15 <RelocateBinaries>false</RelocateBinaries>
16 <HardwareCounters EnableHWCounters="false" />
17 <EtwSettings />
18 <PdhSettings>
19 <PdhCountersEnabled>false</PdhCountersEnabled>
20 <PdhCountersRate>500</PdhCountersRate>
21 <PdhCounters>
22 <PdhCounter>\Память\Обмен страниц/с</PdhCounter>
23 <PdhCounter>\Процессор(_Total)\% загруженности процессора</PdhCounter>
24 <PdhCounter>\Физический диск(_Total)\Средняя длина очереди диска</PdhCounter>
25 </PdhCounters>
26 </PdhSettings>
27 </Options>
28 <ExcludeSmallFuncs>true</ExcludeSmallFuncs>
29 <InteractionProfilingEnabled>false</InteractionProfilingEnabled>
30 <JScriptProfilingEnabled>false</JScriptProfilingEnabled>
31 <PreinstrumentEvent>
32 <InstrEventExclude>false</InstrEventExclude>
33 </PreinstrumentEvent>
34 <PostinstrumentEvent>
35 <InstrEventExclude>false</InstrEventExclude>
36 </PostinstrumentEvent>
37 <Binaries>
38 <ProjBinary>
39 <Path>Implab.Playground\obj\Debug\Implab.Playground.exe</Path>
40 <ArgumentTimestamp>01/01/0001 00:00:00</ArgumentTimestamp>
41 <Instrument>true</Instrument>
42 <Sample>true</Sample>
43 <ExternalWebsite>false</ExternalWebsite>
44 <InteractionProfilingEnabled>false</InteractionProfilingEnabled>
45 <IsLocalJavascript>false</IsLocalJavascript>
46 <IsWindowsStoreApp>false</IsWindowsStoreApp>
47 <IsWWA>false</IsWWA>
48 <LaunchProject>true</LaunchProject>
49 <OverrideProjectSettings>false</OverrideProjectSettings>
50 <LaunchMethod>Executable</LaunchMethod>
51 <ExecutablePath>Implab.Playground\bin\Release\Implab.Playground.exe</ExecutablePath>
52 <StartupDirectory>Implab.Playground\bin\Release\</StartupDirectory>
53 <Arguments>
54 </Arguments>
55 <NetAppHost>IIS</NetAppHost>
56 <NetBrowser>InternetExplorer</NetBrowser>
57 <ExcludeSmallFuncs>true</ExcludeSmallFuncs>
58 <JScriptProfilingEnabled>false</JScriptProfilingEnabled>
59 <PreinstrumentEvent>
60 <InstrEventExclude>false</InstrEventExclude>
61 </PreinstrumentEvent>
62 <PostinstrumentEvent>
63 <InstrEventExclude>false</InstrEventExclude>
64 </PostinstrumentEvent>
65 <ProjRef>{100DFEB0-75BE-436F-ADDF-1F46EF433F46}|Implab.Playground\Implab.Playground.csproj</ProjRef>
66 <ProjPath>Implab.Playground\Implab.Playground.csproj</ProjPath>
67 <ProjName>Implab.Playground</ProjName>
68 </ProjBinary>
69 </Binaries>
70 <Launches>
71 <ProjBinary>
72 <Path>:PB:{100DFEB0-75BE-436F-ADDF-1F46EF433F46}|Implab.Playground\Implab.Playground.csproj</Path>
73 </ProjBinary>
74 </Launches>
75 </VSPerformanceSession> No newline at end of file
@@ -0,0 +1,45
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6 using System.Xml;
7 using System.Xml.Linq;
8
9 namespace Implab.Xml {
10 public static class SerializationHelpers {
11 public static string SerializeAsString<T>(T obj) {
12 return SerializersPool<T>.Instance.SerializeAsString(obj);
13 }
14
15 public static void Serialize<T>(XmlWriter writer, T obj) {
16 SerializersPool<T>.Instance.Serialize(writer, obj);
17 }
18
19 public static XmlDocument SerializeAsXmlDocument<T>(T obj) {
20 var doc = new XmlDocument();
21 using (var writer = doc.CreateNavigator().AppendChild()) {
22 SerializersPool<T>.Instance.Serialize(writer, obj);
23 }
24 return doc;
25 }
26
27 public static XDocument SerializeAsXDocument<T>(T obj) {
28 var doc = new XDocument();
29 using (var writer = doc.CreateWriter()) {
30 SerializersPool<T>.Instance.Serialize(writer, obj);
31 }
32 return doc;
33 }
34
35 public static T DeserializeFromString<T>(string data) {
36 return SerializersPool<T>.Instance.DeserializeFromString(data);
37 }
38
39 public static T DeserializeFromXmlNode<T>(XmlNode node) {
40 Safe.ArgumentNotNull(node, nameof(node));
41 using (var reader = node.CreateNavigator().ReadSubtree())
42 return SerializersPool<T>.Instance.Deserialize(reader);
43 }
44 }
45 }
@@ -0,0 +1,76
1 using Implab.Components;
2 using System;
3 using System.Collections.Generic;
4 using System.IO;
5 using System.Linq;
6 using System.Text;
7 using System.Threading.Tasks;
8 using System.Xml;
9 using System.Xml.Serialization;
10
11 namespace Implab.Xml {
12 public class SerializersPool<T> : ObjectPool<XmlSerializer> {
13
14 static readonly SerializersPool<T> _instance = new SerializersPool<T>();
15
16 public static SerializersPool<T> Instance {
17 get { return _instance; }
18 }
19
20 #region implemented abstract members of ObjectPool
21 protected override XmlSerializer CreateInstance() {
22 return new XmlSerializer(typeof(T));
23 }
24 #endregion
25
26 public T DeserializeFromString(string data) {
27 using (var reader = new StringReader(data)) {
28 return Deserialize(reader);
29 }
30 }
31
32 public T Deserialize(TextReader reader) {
33 var sr = Allocate();
34 try {
35 return (T)sr.Deserialize(reader);
36 } finally {
37 Release(sr);
38 }
39 }
40
41 public T Deserialize(XmlReader reader) {
42 var sr = Allocate();
43 try {
44 return (T)sr.Deserialize(reader);
45 } finally {
46 Release(sr);
47 }
48 }
49
50 public string SerializeAsString(T data) {
51 using (var writer = new StringWriter()) {
52 Serialize(writer, data);
53 return writer.ToString();
54 }
55 }
56
57 public void Serialize(TextWriter writer, T data) {
58 var sr = Allocate();
59 try {
60 sr.Serialize(writer, data);
61 } finally {
62 Release(sr);
63 }
64 }
65
66 public void Serialize(XmlWriter writer, T data) {
67 var sr = Allocate();
68 try {
69 sr.Serialize(writer, data);
70 } finally {
71 Release(sr);
72 }
73 }
74
75 }
76 }
@@ -1,23 +1,25
1 1 syntax: glob
2 2 Implab.Test/bin/
3 3 *.user
4 4 Implab.Test/obj/
5 5 *.userprefs
6 6 Implab/bin/
7 7 Implab/obj/
8 8 TestResults/
9 9 Implab.Fx/obj/
10 10 Implab.Fx/bin/
11 11 Implab.Fx.Test/bin/
12 12 Implab.Fx.Test/obj/
13 13 _ReSharper.Implab/
14 14 Implab.Diagnostics.Interactive/bin/
15 15 Implab.Diagnostics.Interactive/obj/
16 16 MonoPlay/bin/
17 17 MonoPlay/obj/
18 18 Implab.Test/Implab.Format.Test/bin/
19 19 Implab.Test/Implab.Format.Test/obj/
20 20 *.suo
21 21 Implab.Format.Test/bin/
22 22 Implab.Format.Test/obj/
23 23 packages/
24 Implab.Playground/obj/
25 Implab.Playground/bin/
@@ -1,146 +1,147
1 1 using NUnit.Framework;
2 2 using System;
3 3 using Implab.Automaton;
4 4 using Implab.Xml;
5 5 using System.Xml;
6 6 using Implab.Formats;
7 7 using Implab.Formats.Json;
8 using System.IO;
8 9
9 10 namespace Implab.Format.Test {
10 11 [TestFixture]
11 12 public class JsonTests {
12 13
13 14 [Test]
14 15 public void TestScannerValidTokens() {
15 16 using (var scanner = JsonStringScanner.Create(@"9123, -123, 0, 0.1, -0.2, -0.1e3, 1.3E-3, ""some \t\n\u0020 text"", literal []{}:")) {
16 17
17 18 Tuple<JsonTokenType, object>[] expexted = {
18 new Tuple<JsonTokenType,object>(JsonTokenType.Number, 9123d),
19 new Tuple<JsonTokenType,object>(JsonTokenType.Number, "9123"),
19 20 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
20 new Tuple<JsonTokenType,object>(JsonTokenType.Number, -123d),
21 new Tuple<JsonTokenType,object>(JsonTokenType.Number, "-123"),
21 22 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
22 new Tuple<JsonTokenType,object>(JsonTokenType.Number, 0d),
23 new Tuple<JsonTokenType,object>(JsonTokenType.Number, "0"),
23 24 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
24 new Tuple<JsonTokenType,object>(JsonTokenType.Number, 0.1d),
25 new Tuple<JsonTokenType,object>(JsonTokenType.Number, "0.1"),
25 26 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
26 new Tuple<JsonTokenType,object>(JsonTokenType.Number, -0.2d),
27 new Tuple<JsonTokenType,object>(JsonTokenType.Number, "-0.2"),
27 28 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
28 new Tuple<JsonTokenType,object>(JsonTokenType.Number, -0.1e3d),
29 new Tuple<JsonTokenType,object>(JsonTokenType.Number, "-0.1e3"),
29 30 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
30 new Tuple<JsonTokenType,object>(JsonTokenType.Number, 1.3E-3d),
31 new Tuple<JsonTokenType,object>(JsonTokenType.Number, "1.3E-3"),
31 32 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
32 33 new Tuple<JsonTokenType,object>(JsonTokenType.String, "some \t\n text"),
33 34 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
34 35 new Tuple<JsonTokenType,object>(JsonTokenType.Literal, "literal"),
35 36 new Tuple<JsonTokenType,object>(JsonTokenType.BeginArray, null),
36 37 new Tuple<JsonTokenType,object>(JsonTokenType.EndArray, null),
37 38 new Tuple<JsonTokenType,object>(JsonTokenType.BeginObject, null),
38 39 new Tuple<JsonTokenType,object>(JsonTokenType.EndObject, null),
39 40 new Tuple<JsonTokenType,object>(JsonTokenType.NameSeparator, null)
40 41 };
41 42
42 object value;
43 string value;
43 44 JsonTokenType tokenType;
44 45 for (var i = 0; i < expexted.Length; i++) {
45 46
46 47 Assert.IsTrue(scanner.ReadToken(out value, out tokenType));
47 48 Assert.AreEqual(expexted[i].Item1, tokenType);
48 49 Assert.AreEqual(expexted[i].Item2, value);
49 50 }
50 51
51 52 Assert.IsFalse(scanner.ReadToken(out value, out tokenType));
52 53 }
53 54 }
54 55
55 56 [Test]
56 57 public void TestScannerBadTokens() {
57 58 var bad = new[] {
58 59 " 1",
59 60 " literal",
60 61 " \"",
61 62 "\"unclosed string",
62 63 "1.bad",
63 64 "001", // should be read as three numbers
64 65 "--10",
65 66 "+10",
66 67 "1.0.0",
67 68 "1e1.0",
68 69 "l1teral0",
69 70 ".123",
70 71 "-.123"
71 72 };
72 73
73 74 foreach (var json in bad) {
74 75 using (var scanner = JsonStringScanner.Create(json)) {
75 76 try {
76 object value;
77 string value;
77 78 JsonTokenType token;
78 79 scanner.ReadToken(out value, out token);
79 80 if (!Object.Equals(value, json)) {
80 81 Console.WriteLine("'{0}' is read as {1}", json, value is String ? String.Format("'{0}'", value) : value);
81 82 continue;
82 83 }
83 84 Assert.Fail("Token '{0}' shouldn't pass", json);
84 85 } catch (ParserException e) {
85 86 Console.WriteLine(e.Message);
86 87 }
87 88 }
88 89 }
89 90 }
90 91
91 92 [Test]
92 93 public void JsonXmlReaderSimpleTest() {
93 94 var json = "\"some text\"";
94 95 //Console.WriteLine($"JSON: {json}");
95 96 //Console.WriteLine("XML");
96 97 /*using (var xmlReader = new JsonXmlReader(new JSONParser(json), new JsonXmlReaderOptions { NamespaceUri = "JsonXmlReaderSimpleTest", RootName = "string", NodesPrefix = "json" })) {
97 98 Assert.AreEqual(xmlReader.ReadState, System.Xml.ReadState.Initial);
98 99
99 100 AssertRead(xmlReader, XmlNodeType.XmlDeclaration);
100 101 AssertRead(xmlReader, XmlNodeType.Element);
101 102 AssertRead(xmlReader, XmlNodeType.Text);
102 103 AssertRead(xmlReader, XmlNodeType.EndElement);
103 104 Assert.IsFalse(xmlReader.Read());
104 105 }*/
105 106
106 107 //DumpJsonParse("\"text value\"");
107 108 //DumpJsonParse("null");
108 109 //DumpJsonParse("true");
109 110 //DumpJsonParse("{}");
110 111 //DumpJsonParse("[]");
111 112 DumpJsonParse("{\"one\":1, \"two\":2}");
112 DumpJsonParse("[1,2,3]");
113 DumpJsonParse("[1,\"\",2,3]");
113 114 DumpJsonParse("[{\"info\": [7,8,9]}]");
114 DumpJsonFlatParse("[1,2,[3,4],{\"info\": [5,6]},{\"num\": [7,8,null]}, null,[null]]");
115 DumpJsonFlatParse("[1,2,\"\",[3,4],{\"info\": [5,6]},{\"num\": [7,8,null]}, null,[null]]");
115 116 }
116 117
117 118 void AssertRead(XmlReader reader, XmlNodeType expected) {
118 119 Assert.IsTrue(reader.Read());
119 120 Console.WriteLine($"{new string(' ', reader.Depth*2)}{reader}");
120 121 Assert.AreEqual(expected, reader.NodeType);
121 122 }
122 123
123 124 void DumpJsonParse(string json) {
124 125 Console.WriteLine($"JSON: {json}");
125 126 Console.WriteLine("XML");
126 using (var xmlReader = new JsonXmlReader(new JsonParser(json), new JsonXmlReaderOptions { NamespaceUri = "JsonXmlReaderSimpleTest", NodesPrefix = "json" })) {
127 using (var xmlReader = new JsonXmlReader(JsonReader.ParseString(json), new JsonXmlReaderOptions { NamespaceUri = "JsonXmlReaderSimpleTest", NodesPrefix = "json" })) {
127 128 while (xmlReader.Read())
128 129 Console.WriteLine($"{new string(' ', xmlReader.Depth * 2)}{xmlReader}");
129 130 }
130 131 }
131 132
132 133 void DumpJsonFlatParse(string json) {
133 134 Console.WriteLine($"JSON: {json}");
134 135 Console.WriteLine("XML");
135 136 using (var xmlWriter = XmlWriter.Create(Console.Out, new XmlWriterSettings {
136 137 Indent = true,
137 138 CloseOutput = false,
138 139 ConformanceLevel = ConformanceLevel.Document
139 140 }))
140 using (var xmlReader = new JsonXmlReader(new JsonParser(json), new JsonXmlReaderOptions { NamespaceUri = "JsonXmlReaderSimpleTest", NodesPrefix = "", FlattenArrays = true })) {
141 using (var xmlReader = new JsonXmlReader(JsonReader.ParseString(json), new JsonXmlReaderOptions { NamespaceUri = "JsonXmlReaderSimpleTest", NodesPrefix = "", FlattenArrays = true })) {
141 142 xmlWriter.WriteNode(xmlReader, false);
142 143 }
143 144 }
144 145 }
145 146 }
146 147
@@ -1,260 +1,263
1 1 
2 2 Microsoft Visual Studio Solution File, Format Version 12.00
3 3 # Visual Studio 14
4 4 VisualStudioVersion = 14.0.25420.1
5 5 MinimumVisualStudioVersion = 10.0.40219.1
6 6 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab", "Implab\Implab.csproj", "{F550F1F8-8746-4AD0-9614-855F4C4B7F05}"
7 7 EndProject
8 8 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CE8D8D18-437A-445C-B662-4C2CE79A76F6}"
9 9 ProjectSection(SolutionItems) = preProject
10 10 Implab.vsmdi = Implab.vsmdi
11 11 Local.testsettings = Local.testsettings
12 12 TraceAndTestImpact.testsettings = TraceAndTestImpact.testsettings
13 13 EndProjectSection
14 14 EndProject
15 15 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Test", "Implab.Test\Implab.Test.csproj", "{63F92C0C-61BF-48C0-A377-8D67C3C661D0}"
16 16 EndProject
17 17 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Fx", "Implab.Fx\Implab.Fx.csproj", "{06E706F8-6881-43EB-927E-FFC503AF6ABC}"
18 18 EndProject
19 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Fx.Test", "Implab.Fx.Test\Implab.Fx.Test.csproj", "{2F31E405-E267-4195-A05D-574093C21209}"
20 EndProject
21 19 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Format.Test", "Implab.Format.Test\Implab.Format.Test.csproj", "{4D364996-7ECD-4193-8F90-F223FFEA49DA}"
22 20 EndProject
21 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Playground", "Implab.Playground\Implab.Playground.csproj", "{100DFEB0-75BE-436F-ADDF-1F46EF433F46}"
22 EndProject
23 23 Global
24 GlobalSection(Performance) = preSolution
25 HasPerformanceSessions = true
26 EndGlobalSection
24 27 GlobalSection(SolutionConfigurationPlatforms) = preSolution
25 28 Debug 4.5|Any CPU = Debug 4.5|Any CPU
26 29 Debug|Any CPU = Debug|Any CPU
27 30 Release 4.5|Any CPU = Release 4.5|Any CPU
28 31 Release|Any CPU = Release|Any CPU
29 32 EndGlobalSection
30 33 GlobalSection(ProjectConfigurationPlatforms) = postSolution
31 34 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU
32 35 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU
33 36 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
34 37 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.Build.0 = Debug|Any CPU
35 38 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU
36 39 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU
37 40 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.ActiveCfg = Release|Any CPU
38 41 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.Build.0 = Release|Any CPU
39 42 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU
40 43 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU
41 44 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
42 45 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
43 46 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU
44 47 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU
45 48 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
46 49 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.Build.0 = Release|Any CPU
47 50 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU
48 51 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU
49 52 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
50 53 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug|Any CPU.Build.0 = Debug|Any CPU
51 54 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU
52 55 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU
53 56 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release|Any CPU.ActiveCfg = Release|Any CPU
54 57 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release|Any CPU.Build.0 = Release|Any CPU
55 {2F31E405-E267-4195-A05D-574093C21209}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU
56 {2F31E405-E267-4195-A05D-574093C21209}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU
57 {2F31E405-E267-4195-A05D-574093C21209}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
58 {2F31E405-E267-4195-A05D-574093C21209}.Debug|Any CPU.Build.0 = Debug|Any CPU
59 {2F31E405-E267-4195-A05D-574093C21209}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU
60 {2F31E405-E267-4195-A05D-574093C21209}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU
61 {2F31E405-E267-4195-A05D-574093C21209}.Release|Any CPU.ActiveCfg = Release|Any CPU
62 {2F31E405-E267-4195-A05D-574093C21209}.Release|Any CPU.Build.0 = Release|Any CPU
63 58 {4D364996-7ECD-4193-8F90-F223FFEA49DA}.Debug 4.5|Any CPU.ActiveCfg = Debug|Any CPU
64 59 {4D364996-7ECD-4193-8F90-F223FFEA49DA}.Debug 4.5|Any CPU.Build.0 = Debug|Any CPU
65 60 {4D364996-7ECD-4193-8F90-F223FFEA49DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
66 61 {4D364996-7ECD-4193-8F90-F223FFEA49DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
67 62 {4D364996-7ECD-4193-8F90-F223FFEA49DA}.Release 4.5|Any CPU.ActiveCfg = Release|Any CPU
68 63 {4D364996-7ECD-4193-8F90-F223FFEA49DA}.Release 4.5|Any CPU.Build.0 = Release|Any CPU
69 64 {4D364996-7ECD-4193-8F90-F223FFEA49DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
70 65 {4D364996-7ECD-4193-8F90-F223FFEA49DA}.Release|Any CPU.Build.0 = Release|Any CPU
66 {100DFEB0-75BE-436F-ADDF-1F46EF433F46}.Debug 4.5|Any CPU.ActiveCfg = Debug|Any CPU
67 {100DFEB0-75BE-436F-ADDF-1F46EF433F46}.Debug 4.5|Any CPU.Build.0 = Debug|Any CPU
68 {100DFEB0-75BE-436F-ADDF-1F46EF433F46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
69 {100DFEB0-75BE-436F-ADDF-1F46EF433F46}.Debug|Any CPU.Build.0 = Debug|Any CPU
70 {100DFEB0-75BE-436F-ADDF-1F46EF433F46}.Release 4.5|Any CPU.ActiveCfg = Release|Any CPU
71 {100DFEB0-75BE-436F-ADDF-1F46EF433F46}.Release 4.5|Any CPU.Build.0 = Release|Any CPU
72 {100DFEB0-75BE-436F-ADDF-1F46EF433F46}.Release|Any CPU.ActiveCfg = Release|Any CPU
73 {100DFEB0-75BE-436F-ADDF-1F46EF433F46}.Release|Any CPU.Build.0 = Release|Any CPU
71 74 EndGlobalSection
72 75 GlobalSection(SolutionProperties) = preSolution
73 76 HideSolutionNode = FALSE
74 77 EndGlobalSection
75 78 GlobalSection(MonoDevelopProperties) = preSolution
76 79 StartupItem = Implab\Implab.csproj
77 80 Policies = $0
78 81 $0.CSharpFormattingPolicy = $1
79 82 $1.IndentSwitchBody = True
80 83 $1.NamespaceBraceStyle = EndOfLine
81 84 $1.ClassBraceStyle = EndOfLine
82 85 $1.InterfaceBraceStyle = EndOfLine
83 86 $1.StructBraceStyle = EndOfLine
84 87 $1.EnumBraceStyle = EndOfLine
85 88 $1.MethodBraceStyle = EndOfLine
86 89 $1.ConstructorBraceStyle = EndOfLine
87 90 $1.DestructorBraceStyle = EndOfLine
88 91 $1.BeforeMethodDeclarationParentheses = False
89 92 $1.BeforeMethodCallParentheses = False
90 93 $1.BeforeConstructorDeclarationParentheses = False
91 94 $1.NewLineBeforeConstructorInitializerColon = NewLine
92 95 $1.NewLineAfterConstructorInitializerColon = SameLine
93 96 $1.BeforeIndexerDeclarationBracket = False
94 97 $1.BeforeDelegateDeclarationParentheses = False
95 98 $1.NewParentheses = False
96 99 $1.SpacesBeforeBrackets = False
97 100 $1.inheritsSet = Mono
98 101 $1.inheritsScope = text/x-csharp
99 102 $1.scope = text/x-csharp
100 103 $0.TextStylePolicy = $6
101 104 $2.FileWidth = 120
102 105 $2.EolMarker = Unix
103 106 $2.inheritsSet = VisualStudio
104 107 $2.inheritsScope = text/plain
105 108 $2.scope = text/x-csharp
106 109 $0.DotNetNamingPolicy = $3
107 110 $3.DirectoryNamespaceAssociation = PrefixedHierarchical
108 111 $3.ResourceNamePolicy = MSBuild
109 112 $4.FileWidth = 120
110 113 $4.TabsToSpaces = False
111 114 $4.inheritsSet = VisualStudio
112 115 $4.inheritsScope = text/plain
113 116 $4.scope = application/xml
114 117 $0.XmlFormattingPolicy = $5
115 118 $5.inheritsSet = Mono
116 119 $5.inheritsScope = application/xml
117 120 $5.scope = application/xml
118 121 $6.FileWidth = 120
119 122 $6.TabsToSpaces = False
120 123 $6.inheritsSet = VisualStudio
121 124 $6.inheritsScope = text/plain
122 125 $6.scope = text/plain
123 126 $0.NameConventionPolicy = $7
124 127 $7.Rules = $8
125 128 $8.NamingRule = $34
126 129 $9.Name = Namespaces
127 130 $9.AffectedEntity = Namespace
128 131 $9.VisibilityMask = VisibilityMask
129 132 $9.NamingStyle = PascalCase
130 133 $9.IncludeInstanceMembers = True
131 134 $9.IncludeStaticEntities = True
132 135 $10.Name = Types
133 136 $10.AffectedEntity = Class, Struct, Enum, Delegate
134 137 $10.VisibilityMask = VisibilityMask
135 138 $10.NamingStyle = PascalCase
136 139 $10.IncludeInstanceMembers = True
137 140 $10.IncludeStaticEntities = True
138 141 $11.Name = Interfaces
139 142 $11.RequiredPrefixes = $12
140 143 $12.String = I
141 144 $11.AffectedEntity = Interface
142 145 $11.VisibilityMask = VisibilityMask
143 146 $11.NamingStyle = PascalCase
144 147 $11.IncludeInstanceMembers = True
145 148 $11.IncludeStaticEntities = True
146 149 $13.Name = Attributes
147 150 $13.RequiredSuffixes = $14
148 151 $14.String = Attribute
149 152 $13.AffectedEntity = CustomAttributes
150 153 $13.VisibilityMask = VisibilityMask
151 154 $13.NamingStyle = PascalCase
152 155 $13.IncludeInstanceMembers = True
153 156 $13.IncludeStaticEntities = True
154 157 $15.Name = Event Arguments
155 158 $15.RequiredSuffixes = $16
156 159 $16.String = EventArgs
157 160 $15.AffectedEntity = CustomEventArgs
158 161 $15.VisibilityMask = VisibilityMask
159 162 $15.NamingStyle = PascalCase
160 163 $15.IncludeInstanceMembers = True
161 164 $15.IncludeStaticEntities = True
162 165 $17.Name = Exceptions
163 166 $17.RequiredSuffixes = $18
164 167 $18.String = Exception
165 168 $17.AffectedEntity = CustomExceptions
166 169 $17.VisibilityMask = VisibilityMask
167 170 $17.NamingStyle = PascalCase
168 171 $17.IncludeInstanceMembers = True
169 172 $17.IncludeStaticEntities = True
170 173 $19.Name = Methods
171 174 $19.AffectedEntity = Methods
172 175 $19.VisibilityMask = VisibilityMask
173 176 $19.NamingStyle = PascalCase
174 177 $19.IncludeInstanceMembers = True
175 178 $19.IncludeStaticEntities = True
176 179 $20.Name = Static Readonly Fields
177 180 $20.AffectedEntity = ReadonlyField
178 181 $20.VisibilityMask = Internal, Protected, Public
179 182 $20.NamingStyle = CamelCase
180 183 $20.IncludeInstanceMembers = False
181 184 $20.IncludeStaticEntities = True
182 185 $21.Name = Fields (Non Private)
183 186 $21.AffectedEntity = Field
184 187 $21.VisibilityMask = Internal, Public
185 188 $21.NamingStyle = CamelCase
186 189 $21.IncludeInstanceMembers = True
187 190 $21.IncludeStaticEntities = True
188 191 $22.Name = ReadOnly Fields (Non Private)
189 192 $22.AffectedEntity = ReadonlyField
190 193 $22.VisibilityMask = Internal, Public
191 194 $22.NamingStyle = CamelCase
192 195 $22.IncludeInstanceMembers = True
193 196 $22.IncludeStaticEntities = False
194 197 $23.Name = Fields (Private)
195 198 $23.RequiredPrefixes = $24
196 199 $24.String = m_
197 200 $23.AffectedEntity = Field, ReadonlyField
198 201 $23.VisibilityMask = Private, Protected
199 202 $23.NamingStyle = CamelCase
200 203 $23.IncludeInstanceMembers = True
201 204 $23.IncludeStaticEntities = False
202 205 $25.Name = Static Fields (Private)
203 206 $25.RequiredPrefixes = $26
204 207 $26.String = _
205 208 $25.AffectedEntity = Field
206 209 $25.VisibilityMask = Private
207 210 $25.NamingStyle = CamelCase
208 211 $25.IncludeInstanceMembers = False
209 212 $25.IncludeStaticEntities = True
210 213 $27.Name = ReadOnly Fields (Private)
211 214 $27.RequiredPrefixes = $28
212 215 $28.String = m_
213 216 $27.AffectedEntity = ReadonlyField
214 217 $27.VisibilityMask = Private, Protected
215 218 $27.NamingStyle = CamelCase
216 219 $27.IncludeInstanceMembers = True
217 220 $27.IncludeStaticEntities = False
218 221 $29.Name = Constant Fields
219 222 $29.AffectedEntity = ConstantField
220 223 $29.VisibilityMask = VisibilityMask
221 224 $29.NamingStyle = AllUpper
222 225 $29.IncludeInstanceMembers = True
223 226 $29.IncludeStaticEntities = True
224 227 $30.Name = Properties
225 228 $30.AffectedEntity = Property
226 229 $30.VisibilityMask = VisibilityMask
227 230 $30.NamingStyle = PascalCase
228 231 $30.IncludeInstanceMembers = True
229 232 $30.IncludeStaticEntities = True
230 233 $31.Name = Events
231 234 $31.AffectedEntity = Event
232 235 $31.VisibilityMask = VisibilityMask
233 236 $31.NamingStyle = PascalCase
234 237 $31.IncludeInstanceMembers = True
235 238 $31.IncludeStaticEntities = True
236 239 $32.Name = Enum Members
237 240 $32.AffectedEntity = EnumMember
238 241 $32.VisibilityMask = VisibilityMask
239 242 $32.NamingStyle = PascalCase
240 243 $32.IncludeInstanceMembers = True
241 244 $32.IncludeStaticEntities = True
242 245 $33.Name = Parameters
243 246 $33.AffectedEntity = Parameter, LocalVariable
244 247 $33.VisibilityMask = VisibilityMask
245 248 $33.NamingStyle = CamelCase
246 249 $33.IncludeInstanceMembers = True
247 250 $33.IncludeStaticEntities = True
248 251 $34.Name = Type Parameters
249 252 $34.RequiredPrefixes = $35
250 253 $35.String = T
251 254 $34.AffectedEntity = TypeParameter
252 255 $34.VisibilityMask = VisibilityMask
253 256 $34.NamingStyle = PascalCase
254 257 $34.IncludeInstanceMembers = True
255 258 $34.IncludeStaticEntities = True
256 259 EndGlobalSection
257 260 GlobalSection(TestCaseManagementSettings) = postSolution
258 261 CategoryFile = Implab.vsmdi
259 262 EndGlobalSection
260 263 EndGlobal
@@ -1,348 +1,348
1 1 using Implab;
2 2 using System;
3 3 using System.Collections.Generic;
4 4 using System.Linq;
5 5 using System.Diagnostics;
6 6 using System.IO;
7 7 using System.CodeDom.Compiler;
8 8 using System.CodeDom;
9 9
10 10 namespace Implab.Automaton {
11 11 public class DFATable : IDFATableBuilder {
12 12 int m_stateCount;
13 13 int m_symbolCount;
14 14 int m_initialState;
15 15
16 16 readonly HashSet<int> m_finalStates = new HashSet<int>();
17 17 readonly HashSet<AutomatonTransition> m_transitions = new HashSet<AutomatonTransition>();
18 18
19 19
20 20 #region IDFADefinition implementation
21 21
22 22 public bool IsFinalState(int s) {
23 23 Safe.ArgumentInRange(s, 0, m_stateCount, "s");
24 24
25 25 return m_finalStates.Contains(s);
26 26 }
27 27
28 28 public IEnumerable<int> FinalStates {
29 29 get {
30 30 return m_finalStates;
31 31 }
32 32 }
33 33
34 34 public int StateCount {
35 35 get { return m_stateCount; }
36 36 }
37 37
38 38 public int AlphabetSize {
39 39 get { return m_symbolCount; }
40 40 }
41 41
42 42 public int InitialState {
43 43 get { return m_initialState; }
44 44 }
45 45
46 46 #endregion
47 47
48 48 public void SetInitialState(int s) {
49 49 Safe.ArgumentAssert(s >= 0, "s");
50 50 m_stateCount = Math.Max(m_stateCount, s + 1);
51 51 m_initialState = s;
52 52 }
53 53
54 54 public void MarkFinalState(int state) {
55 55 m_stateCount = Math.Max(m_stateCount, state + 1);
56 56 m_finalStates.Add(state);
57 57 }
58 58
59 59 public void Add(AutomatonTransition item) {
60 60 Safe.ArgumentAssert(item.s1 >= 0, "item");
61 61 Safe.ArgumentAssert(item.s2 >= 0, "item");
62 62 Safe.ArgumentAssert(item.edge >= 0, "item");
63 63
64 64 m_stateCount = Math.Max(m_stateCount, Math.Max(item.s1, item.s2) + 1);
65 65 m_symbolCount = Math.Max(m_symbolCount, item.edge + 1);
66 66
67 67 m_transitions.Add(item);
68 68 }
69 69
70 70 public void Clear() {
71 71 m_stateCount = 0;
72 72 m_symbolCount = 0;
73 73 m_finalStates.Clear();
74 74 m_transitions.Clear();
75 75 }
76 76
77 77 public bool Contains(AutomatonTransition item) {
78 78 return m_transitions.Contains(item);
79 79 }
80 80
81 81 public void CopyTo(AutomatonTransition[] array, int arrayIndex) {
82 82 m_transitions.CopyTo(array, arrayIndex);
83 83 }
84 84
85 85 public bool Remove(AutomatonTransition item) {
86 86 return m_transitions.Remove(item);
87 87 }
88 88
89 89 public int Count {
90 90 get {
91 91 return m_transitions.Count;
92 92 }
93 93 }
94 94
95 95 public bool IsReadOnly {
96 96 get {
97 97 return false;
98 98 }
99 99 }
100 100
101 101 public IEnumerator<AutomatonTransition> GetEnumerator() {
102 102 return m_transitions.GetEnumerator();
103 103 }
104 104
105 105 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
106 106 return GetEnumerator();
107 107 }
108 108
109 109 public void AddSymbol(int symbol) {
110 110 Safe.ArgumentAssert(symbol >= 0, "symbol");
111 111 m_symbolCount = Math.Max(symbol + 1, m_symbolCount);
112 112 }
113 113
114 114 public int[,] CreateTransitionTable() {
115 115 var table = new int[StateCount,AlphabetSize];
116 116
117 117 for (int i = 0; i < StateCount; i++)
118 118 for (int j = 0; j < AlphabetSize; j++)
119 119 table[i, j] = AutomatonConst.UNREACHABLE_STATE;
120 120
121 121 foreach (var t in this)
122 table[t.s1,t.edge] = t.s2;
122 table[t.s1,t.edge] = (byte)t.s2;
123 123
124 124 return table;
125 125 }
126 126
127 127 public bool[] CreateFinalStateTable() {
128 128 var table = new bool[StateCount];
129 129
130 130 foreach (var s in FinalStates)
131 131 table[s] = true;
132 132
133 133 return table;
134 134 }
135 135
136 136 /// <summary>Формирует множества конечных состояний перед началом работы алгоритма минимизации.</summary>
137 137 /// <remarks>
138 138 /// В процессе построения минимального автомата требуется разделить множество состояний,
139 139 /// на два подмножества - конечные состояния и все остальные, после чего эти подмножества
140 140 /// будут резделены на более мелкие. Иногда требуется гарантировать различия конечных сосотяний,
141 141 /// для этого необходимо переопределить даннцю фукнцию, для получения множеств конечных состояний.
142 142 /// </remarks>
143 143 /// <returns>The final states.</returns>
144 144 protected virtual IEnumerable<HashSet<int>> SplitFinalStates(IEnumerable<int> states) {
145 145 return new [] { new HashSet<int>(states) };
146 146 }
147 147
148 148 protected void Optimize(
149 149 IDFATableBuilder optimalDFA,
150 150 IDictionary<int,int> alphabetMap,
151 151 IDictionary<int,int> stateMap
152 152 ) {
153 153 Safe.ArgumentNotNull(optimalDFA, "dfa");
154 154 Safe.ArgumentNotNull(alphabetMap, "alphabetMap");
155 155 Safe.ArgumentNotNull(stateMap, "stateMap");
156 156
157 157
158 158 var setComparer = new CustomEqualityComparer<HashSet<int>>(
159 159 (x, y) => x.SetEquals(y),
160 160 s => s.Sum(x => x.GetHashCode())
161 161 );
162 162
163 163 var optimalStates = new HashSet<HashSet<int>>(setComparer);
164 164 var queue = new HashSet<HashSet<int>>(setComparer);
165 165
166 166 optimalStates.Add(new HashSet<int>(FinalStates));
167 167
168 168 var state = new HashSet<int>(
169 169 Enumerable
170 170 .Range(0, m_stateCount)
171 171 .Where(i => !m_finalStates.Contains(i))
172 172 );
173 173
174 174 optimalStates.Add(state);
175 175 queue.Add(state);
176 176
177 177 var rmap = m_transitions
178 178 .GroupBy(t => t.s2)
179 179 .ToDictionary(
180 180 g => g.Key, // s2
181 181 g => g.ToLookup(t => t.edge, t => t.s1)//.ToDictionary(p => p.Key)
182 182 );
183 183
184 184 while (queue.Count > 0) {
185 185 var stateA = queue.First();
186 186 queue.Remove(stateA);
187 187
188 188 for (int c = 0; c < m_symbolCount; c++) {
189 189 var stateX = new HashSet<int>();
190 190 foreach(var a in stateA.Where(rmap.ContainsKey))
191 191 stateX.UnionWith(rmap[a][c]); // all states from wich the symbol 'c' leads to the state 'a'
192 192
193 193 var tmp = optimalStates.ToArray();
194 194 foreach (var stateY in tmp) {
195 195 var stateR1 = new HashSet<int>(stateY);
196 196 var stateR2 = new HashSet<int>(stateY);
197 197
198 198 stateR1.IntersectWith(stateX);
199 199 stateR2.ExceptWith(stateX);
200 200
201 201 if (stateR1.Count > 0 && stateR2.Count > 0) {
202 202
203 203
204 204 optimalStates.Remove(stateY);
205 205 optimalStates.Add(stateR1);
206 206 optimalStates.Add(stateR2);
207 207
208 208 if (queue.Contains(stateY)) {
209 209 queue.Remove(stateY);
210 210 queue.Add(stateR1);
211 211 queue.Add(stateR2);
212 212 } else {
213 213 queue.Add(stateR1.Count <= stateR2.Count ? stateR1 : stateR2);
214 214 }
215 215 }
216 216 }
217 217 }
218 218 }
219 219
220 220 // дополнительно разбиваем конечные состояния
221 221 foreach (var final in optimalStates.Where(s => s.Overlaps(m_finalStates)).ToArray()) {
222 222 optimalStates.Remove(final);
223 223 foreach (var split in SplitFinalStates(final))
224 224 optimalStates.Add(split);
225 225 }
226 226
227 227
228 228 // карта получения оптимального состояния по соотвествующему ему простому состоянию
229 229 var nextState = 0;
230 230 foreach (var item in optimalStates) {
231 231 var id = nextState++;
232 232 foreach (var s in item)
233 233 stateMap[s] = id;
234 234 }
235 235
236 236 // получаем минимальный алфавит
237 237 // входные символы не различимы, если Move(s,a1) == Move(s,a2), для любого s
238 238 // для этого используем алгоритм кластеризации, сначала
239 239 // считаем, что все символы не различимы
240 240
241 241 var minClasses = new HashSet<HashSet<int>>(setComparer);
242 242 var alphaQueue = new Queue<HashSet<int>>();
243 243 alphaQueue.Enqueue(new HashSet<int>(Enumerable.Range(0,AlphabetSize)));
244 244
245 245 // для всех состояний, будем проверять каждый класс на различимость,
246 246 // т.е. символы различимы, если они приводят к разным состояниям
247 247 for (int s = 0 ; s < optimalStates.Count; s++) {
248 248 var newQueue = new Queue<HashSet<int>>();
249 249
250 250 foreach (var A in alphaQueue) {
251 251 // классы из одного символа делить бесполезно, переводим их сразу в
252 252 // результирующий алфавит
253 253 if (A.Count == 1) {
254 254 minClasses.Add(A);
255 255 continue;
256 256 }
257 257
258 258 // различаем классы символов, которые переводят в различные оптимальные состояния
259 259 // optimalState -> alphaClass
260 260 var classes = new Dictionary<int, HashSet<int>>();
261 261
262 262 foreach (var term in A) {
263 263 // ищем все переходы класса по символу term
264 264 var s2 = m_transitions.Where(t => stateMap[t.s1] == s && t.edge == term).Select(t => stateMap[t.s2]).DefaultIfEmpty(-1).First();
265 265
266 266 HashSet<int> a2;
267 267 if (!classes.TryGetValue(s2, out a2)) {
268 268 a2 = new HashSet<int>();
269 269 newQueue.Enqueue(a2);
270 270 classes[s2] = a2;
271 271 }
272 272 a2.Add(term);
273 273 }
274 274 }
275 275
276 276 if (newQueue.Count == 0)
277 277 break;
278 278 alphaQueue = newQueue;
279 279 }
280 280
281 281 // после окончания работы алгоритма в очереди останутся минимальные различимые классы
282 282 // входных символов
283 283 foreach (var A in alphaQueue)
284 284 minClasses.Add(A);
285 285
286 286 // построение отображения алфавитов входных символов.
287 287 // поскольку символ DFAConst.UNCLASSIFIED_INPUT может иметь
288 288 // специальное значение, тогда сохраним минимальный класс,
289 289 // содержащий этот символ на томже месте.
290 290
291 291 var nextCls = 0;
292 292 foreach (var item in minClasses) {
293 293 if (nextCls == AutomatonConst.UNCLASSIFIED_INPUT)
294 294 nextCls++;
295 295
296 296 // сохраняем DFAConst.UNCLASSIFIED_INPUT
297 297 var cls = item.Contains(AutomatonConst.UNCLASSIFIED_INPUT) ? AutomatonConst.UNCLASSIFIED_INPUT : nextCls++;
298 298 optimalDFA.AddSymbol(cls);
299 299
300 300 foreach (var a in item)
301 301 alphabetMap[a] = cls;
302 302 }
303 303
304 304 // построение автомата
305 305 optimalDFA.SetInitialState(stateMap[m_initialState]);
306 306
307 307 foreach (var sf in m_finalStates.Select(s => stateMap[s]).Distinct())
308 308 optimalDFA.MarkFinalState(sf);
309 309
310 310 foreach (var t in m_transitions.Select(t => new AutomatonTransition(stateMap[t.s1],stateMap[t.s2],alphabetMap[t.edge])).Distinct())
311 311 optimalDFA.Add(t);
312 312 }
313 313
314 314 protected string PrintDFA<TInput, TState>(IAlphabet<TInput> inputAlphabet, IAlphabet<TState> stateAlphabet) {
315 315 Safe.ArgumentNotNull(inputAlphabet, "inputAlphabet");
316 316 Safe.ArgumentNotNull(stateAlphabet, "stateAlphabet");
317 317
318 318 var data = new List<string>();
319 319
320 320 data.Add("digraph dfa {");
321 321
322 322 foreach (var final in m_finalStates)
323 323 data.Add(String.Format("{0} [shape=box];",String.Join("", stateAlphabet.GetSymbols(final))));
324 324
325 325 foreach (var t in m_transitions)
326 326 data.Add(String.Format(
327 327 "{0} -> {2} [label={1}];",
328 328 String.Join("", stateAlphabet.GetSymbols(t.s1)),
329 329 ToLiteral(ToLiteral(String.Join("", t.edge == AutomatonConst.UNCLASSIFIED_INPUT ? new [] { "@" } : inputAlphabet.GetSymbols(t.edge).Select(x => x.ToString())))),
330 330 String.Join("", stateAlphabet.GetSymbols(t.s2))
331 331 ));
332 332 data.Add("}");
333 333 return String.Join("\n", data);
334 334 }
335 335
336 336 static string ToLiteral(string input)
337 337 {
338 338 using (var writer = new StringWriter())
339 339 {
340 340 using (var provider = CodeDomProvider.CreateProvider("CSharp"))
341 341 {
342 342 provider.GenerateCodeFromExpression(new CodePrimitiveExpression(input), writer, null);
343 343 return writer.ToString();
344 344 }
345 345 }
346 346 }
347 347 }
348 348 }
@@ -1,84 +1,84
1 1 using Implab.Automaton;
2 2 using System;
3 3 using System.Collections.Generic;
4 4 using System.Linq;
5 using System.Runtime.CompilerServices;
5 6 using System.Text;
6 7 using System.Threading.Tasks;
7 8
8 9 namespace Implab.Formats {
9 10 public class InputScanner<TTag> {
10 11 readonly TTag[] m_tags;
11 12 readonly int m_initialState;
12 13 readonly int[,] m_dfa;
13 14 readonly CharMap m_alphabet;
14 15 readonly bool[] m_final;
15 16
16 17 int m_position;
17 18 int m_state;
18 19
19 20 public InputScanner(int[,] dfaTable, bool[] finalStates, TTag[] tags, int initialState, CharMap alphabet) {
20 21 Safe.ArgumentNotNull(dfaTable, nameof(dfaTable));
21 22 Safe.ArgumentNotNull(finalStates, nameof(finalStates));
22 23 Safe.ArgumentNotNull(tags, nameof(tags));
23 24 Safe.ArgumentNotNull(alphabet, nameof(alphabet));
24 25
25 26 m_dfa = dfaTable;
26 27 m_final = finalStates;
27 28 m_tags = tags;
28 29 m_initialState = initialState;
29 30 m_alphabet = alphabet;
30 31 }
31 32
32 33 public TTag Tag {
34 [MethodImpl(MethodImplOptions.AggressiveInlining)]
33 35 get {
34 36 return m_tags[m_state];
35 37 }
36 38 }
37 39
38 40 public int Position {
41 [MethodImpl(MethodImplOptions.AggressiveInlining)]
39 42 get {
40 43 return m_position;
41 44 }
42 45 }
43 46
44 47 public bool IsFinal {
48 [MethodImpl(MethodImplOptions.AggressiveInlining)]
45 49 get {
46 50 return m_final[m_state];
47 51 }
48 52 }
49 53
50 public void Reset() {
54 [MethodImpl(MethodImplOptions.AggressiveInlining)]
55 public void ResetState() {
51 56 m_state = m_initialState;
52 57 }
53 58
54 59 public InputScanner<TTag> Clone() {
55 60 var clone = new InputScanner<TTag>(m_dfa, m_final, m_tags, m_initialState, m_alphabet);
56 61 clone.m_state = m_state;
57 62 clone.m_position = m_position;
58 63 return clone;
59 64 }
60 65
61 public bool Scan(char[] data, int offset, int length) {
62 if (length <= 0) {
63 m_position = offset;
64 return false; // EOF
65 }
66
67 var max = offset + length;
66 //[MethodImpl(MethodImplOptions.AggressiveInlining)]
67 public bool Scan(char[] data, int offset, int max) {
68 68 var next = m_state;
69 69
70 70 while(offset < max) {
71 71 next = m_dfa[next, m_alphabet.Translate(data[offset])];
72 72 if (next == AutomatonConst.UNREACHABLE_STATE) {
73 73 // scanner stops on the next position after last recognized symbol
74 74 m_position = offset;
75 75 return false;
76 76 }
77 77 m_state = next;
78 78 offset++;
79 79 }
80 80 m_position = offset;
81 81 return true;
82 82 }
83 83 }
84 84 }
@@ -1,294 +1,318
1 1 using System;
2 2 using System.Diagnostics;
3 3 using System.IO;
4 4 using Implab.Automaton;
5 5 using Implab.Automaton.RegularExpressions;
6 6 using System.Linq;
7 7 using Implab.Components;
8 8 using System.Collections.Generic;
9 using System.Text;
10 using System.Globalization;
9 11
10 12 namespace Implab.Formats.Json {
11 13 /// <summary>
12 14 /// Pull парсер JSON данных.
13 15 /// </summary>
14 16 /// <remarks>
15 17 /// Следует отметить отдельную интерпретацию свойства <see cref="Level"/>,
16 18 /// оно означает текущий уровень вложенности объектов, однако закрывающий
17 19 /// элемент объекта и массива имеет уровень меньше, чем сам объект.
18 20 /// <code>
19 21 /// { // Level = 1
20 22 /// "name" : "Peter", // Level = 1
21 23 /// "address" : { // Level = 2
22 24 /// city : "Stern" // Level = 2
23 25 /// } // Level = 1
24 26 /// } // Level = 0
25 27 /// </code>
26 28 /// </remarks>
27 public class JsonParser : Disposable {
29 public class JsonReader : Disposable {
28 30
29 31 enum MemberContext {
30 32 MemberName,
31 33 MemberValue
32 34 }
33 35
34 36 #region Parser rules
35 37 struct ParserContext {
36 38 readonly int[,] m_dfa;
37 39 int m_state;
38 40
39 41 readonly JsonElementContext m_elementContext;
40 42
41 43 public ParserContext(int[,] dfa, int state, JsonElementContext context) {
42 44 m_dfa = dfa;
43 45 m_state = state;
44 46 m_elementContext = context;
45 47 }
46 48
47 49 public bool Move(JsonTokenType token) {
48 50 var next = m_dfa[m_state, (int)token];
49 51 if (next == AutomatonConst.UNREACHABLE_STATE)
50 52 return false;
51 53 m_state = next;
52 54 return true;
53 55 }
54 56
55 57 public JsonElementContext ElementContext {
56 58 get { return m_elementContext; }
57 59 }
58 60 }
59 61
60 62 static readonly ParserContext _jsonContext;
61 63 static readonly ParserContext _objectContext;
62 64 static readonly ParserContext _arrayContext;
63 65
64 static JsonParser() {
66 static JsonReader() {
65 67
66 68 var valueExpression = MakeToken(JsonTokenType.BeginArray, JsonTokenType.BeginObject, JsonTokenType.Literal, JsonTokenType.Number, JsonTokenType.String);
67 69 var memberExpression = MakeToken(JsonTokenType.String).Cat(MakeToken(JsonTokenType.NameSeparator)).Cat(valueExpression);
68 70
69 71 var objectExpression = memberExpression
70 72 .Cat(
71 73 MakeToken(JsonTokenType.ValueSeparator)
72 74 .Cat(memberExpression)
73 75 .EClosure()
74 76 )
75 77 .Optional()
76 78 .Cat(MakeToken(JsonTokenType.EndObject))
77 79 .End();
78 80
79 81 var arrayExpression = valueExpression
80 82 .Cat(
81 83 MakeToken(JsonTokenType.ValueSeparator)
82 84 .Cat(valueExpression)
83 85 .EClosure()
84 86 )
85 87 .Optional()
86 88 .Cat(MakeToken(JsonTokenType.EndArray))
87 89 .End();
88 90
89 91 var jsonExpression = valueExpression.End();
90 92
91 93 _jsonContext = CreateParserContext(jsonExpression, JsonElementContext.None);
92 94 _objectContext = CreateParserContext(objectExpression, JsonElementContext.Object);
93 95 _arrayContext = CreateParserContext(arrayExpression, JsonElementContext.Array);
94 96 }
95 97
96 98 static Token MakeToken(params JsonTokenType[] input) {
97 99 return Token.New( input.Select(t => (int)t).ToArray() );
98 100 }
99 101
100 102 static ParserContext CreateParserContext(Token expr, JsonElementContext context) {
101 103
102 104 var dfa = new DFATable();
103 105 var builder = new RegularExpressionVisitor(dfa);
104 106 expr.Accept(builder);
105 107 builder.BuildDFA();
106 108
107 109 return new ParserContext(dfa.CreateTransitionTable(), dfa.InitialState, context);
108 110 }
109 111
110 112 #endregion
111 113
112 114 readonly JsonScanner m_scanner;
113 115 // json starts from the value context and may content even a single literal
114 116 MemberContext m_memberContext = MemberContext.MemberValue;
115 117
116 118 JsonElementType m_elementType;
117 119 object m_elementValue;
118 120 string m_memberName = String.Empty;
119 121
120 122 Stack<ParserContext> m_stack = new Stack<ParserContext>();
121 123 ParserContext m_context = _jsonContext;
122 124
123 125 /// <summary>
124 126 /// Создает новый парсер на основе строки, содержащей JSON
125 127 /// </summary>
126 128 /// <param name="text"></param>
127 public JsonParser(string text) {
128 Safe.ArgumentNotEmpty(text, "text");
129 m_scanner = JsonStringScanner.Create(text);
130 }
131
132 /// <summary>
133 /// Создает новый экземпляр парсера, на основе текстового потока.
134 /// </summary>
135 /// <param name="reader">Текстовый поток.</param>
136 public JsonParser(TextReader reader) {
137 Safe.ArgumentNotNull(reader, "reader");
138 m_scanner = JsonTextScanner.Create(reader);
129 JsonReader(JsonScanner scanner) {
130 m_scanner = scanner;
139 131 }
140 132
141 133 public int Level {
142 134 get { return m_stack.Count; }
143 135 }
144 136
145 137 /// <summary>
146 138 /// Тип текущего элемента на котором стоит парсер.
147 139 /// </summary>
148 140 public JsonElementType ElementType {
149 141 get { return m_elementType; }
150 142 }
151 143
152 144 /// <summary>
153 145 /// Имя элемента - имя свойства родительского контейнера. Для элементов массивов и корневого всегда
154 146 /// пустая строка.
155 147 /// </summary>
156 148 public string ElementName {
157 149 get { return m_memberName; }
158 150 }
159 151
160 152 /// <summary>
161 153 /// Значение элемента. Только для элементов типа <see cref="JsonElementType.Value"/>, для остальных <c>null</c>
162 154 /// </summary>
163 155 public object ElementValue {
164 156 get { return m_elementValue; }
165 157 }
166 158
167 159 /// <summary>
168 160 /// Читает слеюудущий объект из потока
169 161 /// </summary>
170 162 /// <returns><c>true</c> - операция чтения прошла успешно, <c>false</c> - конец данных</returns>
171 163 public bool Read() {
172 object tokenValue;
164 string tokenValue;
173 165 JsonTokenType tokenType;
174 166
175 167 m_memberName = String.Empty;
176 168
177 169 while (m_scanner.ReadToken(out tokenValue, out tokenType)) {
178 170 if(!m_context.Move(tokenType))
179 171 UnexpectedToken(tokenValue, tokenType);
180 172
181 173 switch (tokenType) {
182 174 case JsonTokenType.BeginObject:
183 175 m_stack.Push(m_context);
184 176 m_context = _objectContext;
185 177
186 178 m_elementValue = null;
187 179 m_memberContext = MemberContext.MemberName;
188 180 m_elementType = JsonElementType.BeginObject;
189 181 return true;
190 182 case JsonTokenType.EndObject:
191 183 if (m_stack.Count == 0)
192 184 UnexpectedToken(tokenValue, tokenType);
193 185 m_context = m_stack.Pop();
194 186
195 187 m_elementValue = null;
196 188 m_elementType = JsonElementType.EndObject;
197 189 return true;
198 190 case JsonTokenType.BeginArray:
199 191 m_stack.Push(m_context);
200 192 m_context = _arrayContext;
201 193
202 194 m_elementValue = null;
203 195 m_memberContext = MemberContext.MemberValue;
204 196 m_elementType = JsonElementType.BeginArray;
205 197 return true;
206 198 case JsonTokenType.EndArray:
207 199 if (m_stack.Count == 0)
208 200 UnexpectedToken(tokenValue, tokenType);
209 201 m_context = m_stack.Pop();
210 202
211 203 m_elementValue = null;
212 204 m_elementType = JsonElementType.EndArray;
213 205 return true;
214 206 case JsonTokenType.String:
215 207 if (m_memberContext == MemberContext.MemberName) {
216 m_memberName = (string)tokenValue;
208 m_memberName = tokenValue;
217 209 break;
218 210 }
219 211 m_elementType = JsonElementType.Value;
220 212 m_elementValue = tokenValue;
221 213 return true;
222 214 case JsonTokenType.Number:
223 215 m_elementType = JsonElementType.Value;
224 m_elementValue = tokenValue;
216 m_elementValue = double.Parse(tokenValue, CultureInfo.InvariantCulture);
225 217 return true;
226 218 case JsonTokenType.Literal:
227 219 m_elementType = JsonElementType.Value;
228 m_elementValue = ParseLiteral((string)tokenValue);
220 m_elementValue = ParseLiteral(tokenValue);
229 221 return true;
230 222 case JsonTokenType.NameSeparator:
231 223 m_memberContext = MemberContext.MemberValue;
232 224 break;
233 225 case JsonTokenType.ValueSeparator:
234 226 m_memberContext = m_context.ElementContext == JsonElementContext.Object ? MemberContext.MemberName : MemberContext.MemberValue;
235 227 break;
236 228 default:
237 229 UnexpectedToken(tokenValue, tokenType);
238 230 break;
239 231 }
240 232 }
241 233 if (m_context.ElementContext != JsonElementContext.None)
242 234 throw new ParserException("Unexpedted end of data");
243 235
244 EOF = true;
236 Eof = true;
245 237
246 238 return false;
247 239 }
248 240
249 241 object ParseLiteral(string literal) {
250 242 switch (literal) {
251 243 case "null":
252 244 return null;
253 245 case "false":
254 246 return false;
255 247 case "true":
256 248 return true;
257 249 default:
258 250 UnexpectedToken(literal, JsonTokenType.Literal);
259 251 return null; // avoid compliler error
260 252 }
261 253 }
262 254
263 255 void UnexpectedToken(object value, JsonTokenType tokenType) {
264 256 throw new ParserException(String.Format("Unexpected token {0}: '{1}'", tokenType, value));
265 257 }
266 258
267 259
268 260 /// <summary>
269 261 /// Признак конца потока
270 262 /// </summary>
271 public bool EOF {
263 public bool Eof {
272 264 get;
273 265 private set;
274 266 }
275 267
276 268 protected override void Dispose(bool disposing) {
277 269 if (disposing)
278 270 m_scanner.Dispose();
279 271 }
280 272
281 273 /// <summary>
282 274 /// Переходит в конец текущего объекта.
283 275 /// </summary>
284 276 public void SeekElementEnd() {
285 277 var level = Level - 1;
286 278
287 279 Debug.Assert(level >= 0);
288 280
289 281 while (Level != level)
290 282 Read();
291 283 }
284
285 public static JsonReader Create(string file, Encoding encoding) {
286 return new JsonReader(JsonTextScanner.Create(file, encoding));
287 }
288
289 public static JsonReader Create(string file) {
290 return new JsonReader(JsonTextScanner.Create(file));
291 }
292
293 public static JsonReader Create(Stream stream, Encoding encoding) {
294 return new JsonReader(JsonTextScanner.Create(stream, encoding));
295 }
296
297 public static JsonReader Create(Stream stream) {
298 return new JsonReader(JsonTextScanner.Create(stream));
299 }
300
301 public static JsonReader Create(TextReader reader) {
302 return new JsonReader(JsonTextScanner.Create(reader));
303 }
304
305 public static JsonReader ParseString(string data) {
306 return new JsonReader(JsonStringScanner.Create(data));
307 }
308
309 public static JsonReader ParseString(string data, int offset, int length) {
310 return new JsonReader(JsonStringScanner.Create(data, offset, length));
311 }
312
313 public static JsonReader ParseString(char[] data, int offset, int lenght) {
314 return new JsonReader(JsonStringScanner.Create(data, offset, lenght));
315 }
292 316 }
293 317
294 318 }
@@ -1,134 +1,190
1 1 using System;
2 2 using System.Globalization;
3 3 using Implab.Automaton;
4 4 using System.Text;
5 5 using Implab.Components;
6 6 using System.IO;
7 7
8 8 namespace Implab.Formats.Json {
9 9 /// <summary>
10 10 /// Сканнер (лексер), разбивающий поток символов на токены JSON.
11 11 /// </summary>
12 12 public abstract class JsonScanner : Disposable {
13 13 readonly InputScanner<JsonGrammar.TokenType> m_jsonContext = JsonGrammar.CreateJsonExpressionScanner();
14 14 readonly InputScanner<JsonGrammar.TokenType> m_stringContext = JsonGrammar.CreateStringExpressionScanner();
15 15
16 16 readonly char[] m_unescapeBuf = new char[4];
17 17 readonly char[] m_buffer;
18 18 int m_length;
19 19 int m_pos;
20 20 readonly StringBuilder m_tokenBuilder = new StringBuilder();
21 21
22 22 protected JsonScanner(char[] buffer, int pos, int length) {
23 23 m_buffer = buffer;
24 24 m_pos = pos;
25 25 m_length = length;
26 26 }
27 27
28 bool Read(InputScanner<JsonGrammar.TokenType> scanner, out JsonGrammar.TokenType tokenType) {
29 scanner.Reset();
28 bool ReadChunk(InputScanner<JsonGrammar.TokenType> scanner, out JsonGrammar.TokenType tokenType) {
29 scanner.ResetState();
30
31 while(scanner.Scan(m_buffer, m_pos, m_length)) {
32 // scanner requests new data
30 33
31 if (m_pos == m_length) {
34 if (m_pos != m_length) // capture results for the future
35 m_tokenBuilder.Append(m_buffer, m_pos, m_length - m_pos);
36
37 // read next data
38 m_length = Read(m_buffer, 0, m_buffer.Length);
39
40 if (m_length == 0) {
41 // no data is read
42 if (scanner.Position == m_pos) {
43 // scanned hasn't moved, that's the end
32 44 m_pos = 0;
33 m_length = Read(m_buffer, 0, m_buffer.Length);
34 if (m_length == 0) {
35 45 tokenType = JsonGrammar.TokenType.None;
36 return false; // EOF
46 return false;
47 }
48
49 if (scanner.IsFinal) {
50 m_pos = 0;
51 tokenType = scanner.Tag;
52 return true;
53 } else {
54 throw new ParserException("Unexpected EOF");
37 55 }
38 56 }
39 57
40 while(scanner.Scan(m_buffer, m_pos, m_length - m_pos)) {
41 m_tokenBuilder.Append(m_buffer, m_pos, m_length - m_pos);
42 58 m_pos = 0;
43 m_length = Read(m_buffer, 0, m_buffer.Length);
44 59 }
45 60 var scannerPos = scanner.Position;
61
62 // scanner stops as scannerPos
63 if (!scanner.IsFinal)
64 throw new ParserException($"Unexpected character '{m_buffer[scannerPos + 1]}'");
65
66 tokenType = scanner.Tag;
67 if (scannerPos != m_pos && tokenType == JsonGrammar.TokenType.Number || tokenType == JsonGrammar.TokenType.Literal)
68 m_tokenBuilder.Append(m_buffer, m_pos, scannerPos - m_pos);
69
70 m_pos = scannerPos;
71 return true;
72 }
73
74 bool ReadStringChunk(InputScanner<JsonGrammar.TokenType> scanner, out JsonGrammar.TokenType tokenType) {
75 scanner.ResetState();
76
77 while (scanner.Scan(m_buffer, m_pos, m_length)) {
78 // scanner requests new data
79
80 if (m_pos != m_length) // capture results for the future
81 m_tokenBuilder.Append(m_buffer, m_pos, m_length - m_pos);
82
83 // read next data
84 m_length = Read(m_buffer, 0, m_buffer.Length);
85
86 if (m_length == 0) {
87 // no data is read
88 if (scanner.Position == m_pos) {
89 // scanned hasn't moved, that's the end
90 m_pos = 0;
91 tokenType = JsonGrammar.TokenType.None;
92 return false;
93 }
94
95 if (scanner.IsFinal) {
96 m_pos = 0;
97 tokenType = scanner.Tag;
98 return true;
99 } else {
100 throw new ParserException("Unexpected EOF");
101 }
102 }
103
104 m_pos = 0;
105 }
106 var scannerPos = scanner.Position;
107
108 // scanner stops as scannerPos
109 if (!scanner.IsFinal)
110 throw new ParserException($"Unexpected character '{m_buffer[scannerPos + 1]}'");
111
46 112 if (scannerPos != m_pos) {
47 113 m_tokenBuilder.Append(m_buffer, m_pos, scannerPos - m_pos);
48 114 m_pos = scannerPos;
49 115 }
50
51 if (!scanner.IsFinal) {
52 if (m_length == 0) {
53 // unexpected EOF
54 throw new ParserException("Unexpected EOF");
55 } else {
56 // unecpected character
57 throw new ParserException($"Unexpected character '{m_buffer[m_pos + 1]}'");
58 }
59 }
60 116 tokenType = scanner.Tag;
61 117 return true;
62 118 }
63 119
64 120 protected abstract int Read(char[] buffer, int offset, int size);
65 121
66 122
67 123 /// <summary>
68 124 /// Читает следующий лексический элемент из входных данных.
69 125 /// </summary>
70 126 /// <param name="tokenValue">Возвращает значение прочитанного токена.</param>
71 127 /// <param name="tokenType">Возвращает тип прочитанного токена.</param>
72 128 /// <returns><c>true</c> - чтение произведено успешно. <c>false</c> - достигнут конец входных данных</returns>
73 129 /// <remarks>В случе если токен не распознается, возникает исключение. Значения токенов обрабатываются, т.е.
74 130 /// в строках обрабатываются экранированные символы, числа становтся типа double.</remarks>
75 public bool ReadToken(out object tokenValue, out JsonTokenType tokenType) {
131 public bool ReadToken(out string tokenValue, out JsonTokenType tokenType) {
76 132 JsonGrammar.TokenType tag;
77 133 m_tokenBuilder.Clear();
78 while (Read(m_jsonContext, out tag)) {
134 while (ReadChunk(m_jsonContext, out tag)) {
79 135 switch (tag) {
80 136 case JsonGrammar.TokenType.StringBound:
81 137 tokenValue = ReadString();
82 138 tokenType = JsonTokenType.String;
83 139 break;
84 140 case JsonGrammar.TokenType.Number:
85 tokenValue = Double.Parse(m_tokenBuilder.ToString(), CultureInfo.InvariantCulture);
141 tokenValue = m_tokenBuilder.ToString();
86 142 tokenType = JsonTokenType.Number;
87 143 break;
88 144 case JsonGrammar.TokenType.Literal:
89 145 tokenType = JsonTokenType.Literal;
90 146 tokenValue = m_tokenBuilder.ToString();
91 147 break;
92 148 case JsonGrammar.TokenType.Whitespace:
93 149 m_tokenBuilder.Clear();
94 150 continue;
95 151 default:
96 152 tokenType = (JsonTokenType)tag;
97 153 tokenValue = null;
98 154 break;
99 155 }
100 156 return true;
101 157 }
102 158 tokenValue = null;
103 159 tokenType = JsonTokenType.None;
104 160 return false;
105 161 }
106 162
107 163 string ReadString() {
108 164 JsonGrammar.TokenType tag;
109 165 m_tokenBuilder.Clear();
110 166
111 while (Read(m_stringContext, out tag)) {
167 while (ReadStringChunk(m_stringContext, out tag)) {
112 168 switch (tag) {
113 169 case JsonGrammar.TokenType.StringBound:
114 170 m_tokenBuilder.Length--;
115 171 return m_tokenBuilder.ToString();
116 172 case JsonGrammar.TokenType.UnescapedChar:
117 173 break;
118 174 case JsonGrammar.TokenType.EscapedUnicode: // \xXXXX - unicode escape sequence
119 175 m_tokenBuilder.CopyTo(m_tokenBuilder.Length - 4, m_unescapeBuf, 0, 4);
120 176 m_tokenBuilder.Length -= 6;
121 177 m_tokenBuilder.Append(StringTranslator.TranslateHexUnicode(m_unescapeBuf, 0));
122 178 break;
123 179 case JsonGrammar.TokenType.EscapedChar: // \t - escape sequence
124 180 var ch = m_tokenBuilder[m_tokenBuilder.Length-1];
125 181 m_tokenBuilder.Length -= 2;
126 182 m_tokenBuilder.Append(StringTranslator.TranslateEscapedChar(ch));
127 183 break;
128 184 }
129 185 }
130 186
131 187 throw new ParserException("Unexpected end of data");
132 188 }
133 189 }
134 190 }
@@ -1,49 +1,49
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.IO;
4 4 using System.Linq;
5 5 using System.Text;
6 6 using System.Threading.Tasks;
7 7
8 8 namespace Implab.Formats.Json {
9 9 public class JsonTextScanner : JsonScanner {
10 const int _bufferSize = 4096;
10 const int _bufferSize = 16*4096;
11 11 readonly TextReader m_reader;
12 12
13 13 JsonTextScanner(TextReader reader, char[] buffer) : base(buffer, 0, 0) {
14 14 m_reader = reader;
15 15 }
16 16
17 17 protected override int Read(char[] buffer, int offset, int size) {
18 18 return m_reader.Read(buffer, offset, size);
19 19 }
20 20
21 21 public static JsonTextScanner Create(string file, Encoding encoding) {
22 22 return new JsonTextScanner(new StreamReader(file, encoding), new char[_bufferSize]);
23 23 }
24 24
25 25 public static JsonTextScanner Create(string file) {
26 26 return new JsonTextScanner(new StreamReader(file), new char[_bufferSize]);
27 27 }
28 28
29 29 public static JsonTextScanner Create(Stream stream, Encoding encoding) {
30 30 return new JsonTextScanner(new StreamReader(stream, encoding), new char[_bufferSize]);
31 31 }
32 32
33 33 public static JsonTextScanner Create(Stream stream) {
34 34 return new JsonTextScanner(new StreamReader(stream), new char[_bufferSize]);
35 35 }
36 36
37 37 public static JsonTextScanner Create(TextReader reader) {
38 38 Safe.ArgumentNotNull(reader, nameof(reader));
39 39 return new JsonTextScanner(reader, new char[_bufferSize]);
40 40 }
41 41
42 42 protected override void Dispose(bool disposing) {
43 43 if (disposing)
44 44 Safe.Dispose(m_reader);
45 45
46 46 base.Dispose(disposing);
47 47 }
48 48 }
49 49 }
@@ -1,279 +1,282
1 1 <?xml version="1.0" encoding="utf-8"?>
2 2 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 3 <PropertyGroup>
4 4 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5 5 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
6 6 <ProjectGuid>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</ProjectGuid>
7 7 <OutputType>Library</OutputType>
8 8 <RootNamespace>Implab</RootNamespace>
9 9 <AssemblyName>Implab</AssemblyName>
10 10 <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
11 11 </PropertyGroup>
12 12 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
13 13 <DebugSymbols>true</DebugSymbols>
14 14 <DebugType>full</DebugType>
15 15 <Optimize>false</Optimize>
16 16 <OutputPath>bin\Debug</OutputPath>
17 17 <DefineConstants>TRACE;DEBUG;</DefineConstants>
18 18 <ErrorReport>prompt</ErrorReport>
19 19 <WarningLevel>4</WarningLevel>
20 20 <ConsolePause>false</ConsolePause>
21 21 <RunCodeAnalysis>true</RunCodeAnalysis>
22 22 </PropertyGroup>
23 23 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
24 24 <DebugType>full</DebugType>
25 25 <Optimize>true</Optimize>
26 26 <OutputPath>bin\Release</OutputPath>
27 27 <ErrorReport>prompt</ErrorReport>
28 28 <WarningLevel>4</WarningLevel>
29 29 <ConsolePause>false</ConsolePause>
30 30 </PropertyGroup>
31 31 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug 4.5|AnyCPU' ">
32 32 <DebugSymbols>true</DebugSymbols>
33 33 <DebugType>full</DebugType>
34 34 <Optimize>false</Optimize>
35 35 <OutputPath>bin\Debug</OutputPath>
36 36 <DefineConstants>TRACE;DEBUG;NET_4_5</DefineConstants>
37 37 <ErrorReport>prompt</ErrorReport>
38 38 <WarningLevel>4</WarningLevel>
39 39 <RunCodeAnalysis>true</RunCodeAnalysis>
40 40 <ConsolePause>false</ConsolePause>
41 41 </PropertyGroup>
42 42 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.5|AnyCPU' ">
43 43 <Optimize>true</Optimize>
44 44 <OutputPath>bin\Release</OutputPath>
45 45 <ErrorReport>prompt</ErrorReport>
46 46 <WarningLevel>4</WarningLevel>
47 47 <ConsolePause>false</ConsolePause>
48 48 <DefineConstants>NET_4_5</DefineConstants>
49 49 </PropertyGroup>
50 50 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'DebugMono|AnyCPU' ">
51 51 <DebugSymbols>true</DebugSymbols>
52 52 <DebugType>full</DebugType>
53 53 <Optimize>false</Optimize>
54 54 <OutputPath>bin\Debug</OutputPath>
55 55 <DefineConstants>TRACE;DEBUG;NET_4_5;MONO</DefineConstants>
56 56 <ErrorReport>prompt</ErrorReport>
57 57 <WarningLevel>4</WarningLevel>
58 58 <RunCodeAnalysis>true</RunCodeAnalysis>
59 59 <ConsolePause>false</ConsolePause>
60 60 </PropertyGroup>
61 61 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseMono|AnyCPU' ">
62 62 <Optimize>true</Optimize>
63 63 <OutputPath>bin\Release</OutputPath>
64 64 <DefineConstants>NET_4_5;MONO;</DefineConstants>
65 65 <ErrorReport>prompt</ErrorReport>
66 66 <WarningLevel>4</WarningLevel>
67 67 <ConsolePause>false</ConsolePause>
68 68 </PropertyGroup>
69 69 <ItemGroup>
70 70 <Reference Include="System" />
71 71 <Reference Include="System.Xml" />
72 72 <Reference Include="mscorlib" />
73 <Reference Include="System.Xml.Linq" />
73 74 </ItemGroup>
74 75 <ItemGroup>
75 76 <Compile Include="Components\StateChangeEventArgs.cs" />
76 77 <Compile Include="CustomEqualityComparer.cs" />
77 78 <Compile Include="Diagnostics\ConsoleTraceListener.cs" />
78 79 <Compile Include="Diagnostics\LogChannel.cs" />
79 80 <Compile Include="Diagnostics\LogicalOperation.cs" />
80 81 <Compile Include="Diagnostics\TextFileListener.cs" />
81 82 <Compile Include="Diagnostics\Trace.cs" />
82 83 <Compile Include="Diagnostics\TraceLog.cs" />
83 84 <Compile Include="Diagnostics\TraceEvent.cs" />
84 85 <Compile Include="Diagnostics\TraceEventType.cs" />
85 86 <Compile Include="Diagnostics\TraceSourceAttribute.cs" />
86 87 <Compile Include="Formats\CharMap.cs" />
87 88 <Compile Include="Formats\InputScanner.cs" />
88 89 <Compile Include="Formats\Json\JsonStringScanner.cs" />
89 90 <Compile Include="Formats\Json\JsonTextScanner.cs" />
90 91 <Compile Include="ICancellable.cs" />
91 92 <Compile Include="IProgressHandler.cs" />
92 93 <Compile Include="IProgressNotifier.cs" />
93 94 <Compile Include="IPromiseT.cs" />
94 95 <Compile Include="IPromise.cs" />
95 96 <Compile Include="IServiceLocator.cs" />
96 97 <Compile Include="ITaskController.cs" />
97 98 <Compile Include="Parallels\DispatchPool.cs" />
98 99 <Compile Include="Parallels\ArrayTraits.cs" />
99 100 <Compile Include="Parallels\MTQueue.cs" />
100 101 <Compile Include="Parallels\WorkerPool.cs" />
101 102 <Compile Include="ProgressInitEventArgs.cs" />
102 103 <Compile Include="Properties\AssemblyInfo.cs" />
103 104 <Compile Include="Parallels\AsyncPool.cs" />
104 105 <Compile Include="Safe.cs" />
105 106 <Compile Include="SyncContextPromise.cs" />
106 107 <Compile Include="ValueEventArgs.cs" />
107 108 <Compile Include="PromiseExtensions.cs" />
108 109 <Compile Include="SyncContextPromiseT.cs" />
109 110 <Compile Include="Diagnostics\OperationContext.cs" />
110 111 <Compile Include="Diagnostics\TraceContext.cs" />
111 112 <Compile Include="Diagnostics\LogEventArgs.cs" />
112 113 <Compile Include="Diagnostics\LogEventArgsT.cs" />
113 114 <Compile Include="Diagnostics\Extensions.cs" />
114 115 <Compile Include="PromiseEventType.cs" />
115 116 <Compile Include="Parallels\AsyncQueue.cs" />
116 117 <Compile Include="PromiseT.cs" />
117 118 <Compile Include="IDeferred.cs" />
118 119 <Compile Include="IDeferredT.cs" />
119 120 <Compile Include="Promise.cs" />
120 121 <Compile Include="PromiseTransientException.cs" />
121 122 <Compile Include="Parallels\Signal.cs" />
122 123 <Compile Include="Parallels\SharedLock.cs" />
123 124 <Compile Include="Diagnostics\ILogWriter.cs" />
124 125 <Compile Include="Diagnostics\ListenerBase.cs" />
125 126 <Compile Include="Parallels\BlockingQueue.cs" />
126 127 <Compile Include="AbstractEvent.cs" />
127 128 <Compile Include="AbstractPromise.cs" />
128 129 <Compile Include="AbstractPromiseT.cs" />
129 130 <Compile Include="FuncTask.cs" />
130 131 <Compile Include="FuncTaskBase.cs" />
131 132 <Compile Include="FuncTaskT.cs" />
132 133 <Compile Include="ActionChainTaskBase.cs" />
133 134 <Compile Include="ActionChainTask.cs" />
134 135 <Compile Include="ActionChainTaskT.cs" />
135 136 <Compile Include="FuncChainTaskBase.cs" />
136 137 <Compile Include="FuncChainTask.cs" />
137 138 <Compile Include="FuncChainTaskT.cs" />
138 139 <Compile Include="ActionTaskBase.cs" />
139 140 <Compile Include="ActionTask.cs" />
140 141 <Compile Include="ActionTaskT.cs" />
141 142 <Compile Include="ICancellationToken.cs" />
142 143 <Compile Include="SuccessPromise.cs" />
143 144 <Compile Include="SuccessPromiseT.cs" />
144 145 <Compile Include="PromiseAwaiterT.cs" />
145 146 <Compile Include="PromiseAwaiter.cs" />
146 147 <Compile Include="Components\ComponentContainer.cs" />
147 148 <Compile Include="Components\Disposable.cs" />
148 149 <Compile Include="Components\DisposablePool.cs" />
149 150 <Compile Include="Components\ObjectPool.cs" />
150 151 <Compile Include="Components\ServiceLocator.cs" />
151 152 <Compile Include="Components\IInitializable.cs" />
152 153 <Compile Include="TaskController.cs" />
153 154 <Compile Include="Components\App.cs" />
154 155 <Compile Include="Components\IRunnable.cs" />
155 156 <Compile Include="Components\ExecutionState.cs" />
156 157 <Compile Include="Components\RunnableComponent.cs" />
157 158 <Compile Include="Components\IFactory.cs" />
158 159 <Compile Include="Automaton\IAlphabet.cs" />
159 160 <Compile Include="Automaton\ParserException.cs" />
160 161 <Compile Include="Automaton\IndexedAlphabetBase.cs" />
161 162 <Compile Include="Automaton\IAlphabetBuilder.cs" />
162 163 <Compile Include="Automaton\RegularExpressions\AltToken.cs" />
163 164 <Compile Include="Automaton\RegularExpressions\BinaryToken.cs" />
164 165 <Compile Include="Automaton\RegularExpressions\CatToken.cs" />
165 166 <Compile Include="Automaton\RegularExpressions\StarToken.cs" />
166 167 <Compile Include="Automaton\RegularExpressions\SymbolToken.cs" />
167 168 <Compile Include="Automaton\RegularExpressions\EmptyToken.cs" />
168 169 <Compile Include="Automaton\RegularExpressions\Token.cs" />
169 170 <Compile Include="Automaton\RegularExpressions\IVisitor.cs" />
170 171 <Compile Include="Automaton\AutomatonTransition.cs" />
171 172 <Compile Include="Formats\Json\JsonElementContext.cs" />
172 173 <Compile Include="Formats\Json\JsonElementType.cs" />
173 174 <Compile Include="Formats\Json\JsonGrammar.cs" />
174 <Compile Include="Formats\Json\JsonParser.cs" />
175 <Compile Include="Formats\Json\JsonReader.cs" />
175 176 <Compile Include="Formats\Json\JsonScanner.cs" />
176 177 <Compile Include="Formats\Json\JsonTokenType.cs" />
177 178 <Compile Include="Formats\Json\JsonWriter.cs" />
178 179 <Compile Include="Formats\Json\StringTranslator.cs" />
179 180 <Compile Include="Automaton\MapAlphabet.cs" />
180 181 <Compile Include="Formats\CharAlphabet.cs" />
181 182 <Compile Include="Formats\ByteAlphabet.cs" />
182 183 <Compile Include="Automaton\IDFATable.cs" />
183 184 <Compile Include="Automaton\IDFATableBuilder.cs" />
184 185 <Compile Include="Automaton\DFATable.cs" />
185 186 <Compile Include="Automaton\RegularExpressions\RegularExpressionVisitor.cs" />
186 187 <Compile Include="Automaton\RegularExpressions\ITaggedDFABuilder.cs" />
187 188 <Compile Include="Formats\Grammar.cs" />
188 189 <Compile Include="Automaton\RegularExpressions\EndTokenT.cs" />
189 190 <Compile Include="Automaton\RegularExpressions\EndToken.cs" />
190 191 <Compile Include="Automaton\RegularExpressions\RegularExpressionVisitorT.cs" />
191 192 <Compile Include="Automaton\AutomatonConst.cs" />
192 193 <Compile Include="Automaton\RegularExpressions\RegularDFA.cs" />
193 194 <Compile Include="Components\LazyAndWeak.cs" />
194 195 <Compile Include="AbstractTask.cs" />
195 196 <Compile Include="AbstractTaskT.cs" />
196 197 <Compile Include="FailedPromise.cs" />
197 198 <Compile Include="FailedPromiseT.cs" />
198 199 <Compile Include="Components\PollingComponent.cs" />
199 200 <Compile Include="Xml\JsonXmlReader.cs" />
200 201 <Compile Include="Xml\JsonXmlReaderOptions.cs" />
201 202 <Compile Include="Xml\JsonXmlReaderPosition.cs" />
203 <Compile Include="Xml\SerializationHelpers.cs" />
204 <Compile Include="Xml\SerializersPool.cs" />
202 205 <Compile Include="Xml\XmlSimpleAttribute.cs" />
203 206 <Compile Include="Xml\XmlNameContext.cs" />
204 207 </ItemGroup>
205 208 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
206 209 <ItemGroup />
207 210 <ProjectExtensions>
208 211 <MonoDevelop>
209 212 <Properties>
210 213 <Policies>
211 214 <CSharpFormattingPolicy IndentBlock="True" IndentBraces="False" IndentSwitchSection="False" IndentSwitchCaseSection="True" LabelPositioning="OneLess" NewLinesForBracesInProperties="False" NewLinesForBracesInAccessors="False" NewLinesForBracesInAnonymousMethods="False" NewLinesForBracesInControlBlocks="False" NewLinesForBracesInAnonymousTypes="False" NewLinesForBracesInObjectCollectionArrayInitializers="False" NewLinesForBracesInLambdaExpressionBody="False" NewLineForElse="False" NewLineForCatch="False" NewLineForFinally="False" NewLineForMembersInObjectInit="False" NewLineForMembersInAnonymousTypes="False" NewLineForClausesInQuery="False" SpaceWithinMethodDeclarationParenthesis="False" SpaceBetweenEmptyMethodDeclarationParentheses="False" SpaceWithinMethodCallParentheses="False" SpaceBetweenEmptyMethodCallParentheses="False" SpaceAfterControlFlowStatementKeyword="True" SpaceWithinExpressionParentheses="False" SpaceWithinCastParentheses="False" SpaceWithinOtherParentheses="False" SpaceAfterCast="False" SpacesIgnoreAroundVariableDeclaration="False" SpaceBetweenEmptySquareBrackets="False" SpaceWithinSquareBrackets="False" SpaceAfterColonInBaseTypeDeclaration="True" SpaceAfterComma="True" SpaceAfterDot="False" SpaceAfterSemicolonsInForStatement="True" SpaceBeforeComma="False" SpaceBeforeDot="False" SpaceBeforeSemicolonsInForStatement="False" SpacingAroundBinaryOperator="Single" WrappingPreserveSingleLine="True" WrappingKeepStatementsOnSingleLine="True" PlaceSystemDirectiveFirst="True" NewLinesForBracesInTypes="True" NewLinesForBracesInMethods="True" SpacingAfterMethodDeclarationName="True" SpaceAfterMethodCallName="True" SpaceBeforeOpenSquareBracket="True" SpaceBeforeColonInBaseTypeDeclaration="True" scope="text/x-csharp" />
212 215 <TextStylePolicy FileWidth="120" TabWidth="4" IndentWidth="4" RemoveTrailingWhitespace="True" NoTabsAfterNonTabs="False" TabsToSpaces="True" EolMarker="Unix" scope="text/x-csharp" />
213 216 <DotNetNamingPolicy DirectoryNamespaceAssociation="PrefixedHierarchical" ResourceNamePolicy="MSBuild" />
214 217 <TextStylePolicy FileWidth="120" TabWidth="4" TabsToSpaces="False" IndentWidth="4" RemoveTrailingWhitespace="True" NoTabsAfterNonTabs="False" EolMarker="Native" scope="application/xml" />
215 218 <XmlFormattingPolicy scope="application/xml">
216 219 <DefaultFormat OmitXmlDeclaration="False" NewLineChars="&#xA;" IndentContent="True" ContentIndentString=" " AttributesInNewLine="False" MaxAttributesPerLine="10" AttributesIndentString=" " WrapAttributes="False" AlignAttributes="False" AlignAttributeValues="False" QuoteChar="&quot;" SpacesBeforeAssignment="0" SpacesAfterAssignment="0" EmptyLinesBeforeStart="0" EmptyLinesAfterStart="0" EmptyLinesBeforeEnd="0" EmptyLinesAfterEnd="0" />
217 220 </XmlFormattingPolicy>
218 221 <TextStylePolicy FileWidth="120" TabWidth="4" TabsToSpaces="False" IndentWidth="4" RemoveTrailingWhitespace="True" NoTabsAfterNonTabs="False" EolMarker="Native" scope="text/plain" />
219 222 <NameConventionPolicy>
220 223 <Rules>
221 224 <NamingRule Name="Namespaces" AffectedEntity="Namespace" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
222 225 <NamingRule Name="Types" AffectedEntity="Class, Struct, Enum, Delegate" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
223 226 <NamingRule Name="Interfaces" AffectedEntity="Interface" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
224 227 <RequiredPrefixes>
225 228 <String>I</String>
226 229 </RequiredPrefixes>
227 230 </NamingRule>
228 231 <NamingRule Name="Attributes" AffectedEntity="CustomAttributes" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
229 232 <RequiredSuffixes>
230 233 <String>Attribute</String>
231 234 </RequiredSuffixes>
232 235 </NamingRule>
233 236 <NamingRule Name="Event Arguments" AffectedEntity="CustomEventArgs" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
234 237 <RequiredSuffixes>
235 238 <String>EventArgs</String>
236 239 </RequiredSuffixes>
237 240 </NamingRule>
238 241 <NamingRule Name="Exceptions" AffectedEntity="CustomExceptions" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
239 242 <RequiredSuffixes>
240 243 <String>Exception</String>
241 244 </RequiredSuffixes>
242 245 </NamingRule>
243 246 <NamingRule Name="Methods" AffectedEntity="Methods" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
244 247 <NamingRule Name="Static Readonly Fields" AffectedEntity="ReadonlyField" VisibilityMask="Internal, Protected, Public" NamingStyle="CamelCase" IncludeInstanceMembers="False" IncludeStaticEntities="True" />
245 248 <NamingRule Name="Fields (Non Private)" AffectedEntity="Field" VisibilityMask="Internal, Public" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
246 249 <NamingRule Name="ReadOnly Fields (Non Private)" AffectedEntity="ReadonlyField" VisibilityMask="Internal, Public" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False" />
247 250 <NamingRule Name="Fields (Private)" AffectedEntity="Field, ReadonlyField" VisibilityMask="Private, Protected" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False">
248 251 <RequiredPrefixes>
249 252 <String>m_</String>
250 253 </RequiredPrefixes>
251 254 </NamingRule>
252 255 <NamingRule Name="Static Fields (Private)" AffectedEntity="Field" VisibilityMask="Private" NamingStyle="CamelCase" IncludeInstanceMembers="False" IncludeStaticEntities="True">
253 256 <RequiredPrefixes>
254 257 <String>_</String>
255 258 </RequiredPrefixes>
256 259 </NamingRule>
257 260 <NamingRule Name="ReadOnly Fields (Private)" AffectedEntity="ReadonlyField" VisibilityMask="Private, Protected" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False">
258 261 <RequiredPrefixes>
259 262 <String>m_</String>
260 263 </RequiredPrefixes>
261 264 </NamingRule>
262 265 <NamingRule Name="Constant Fields" AffectedEntity="ConstantField" VisibilityMask="VisibilityMask" NamingStyle="AllUpper" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
263 266 <NamingRule Name="Properties" AffectedEntity="Property" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
264 267 <NamingRule Name="Events" AffectedEntity="Event" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
265 268 <NamingRule Name="Enum Members" AffectedEntity="EnumMember" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
266 269 <NamingRule Name="Parameters" AffectedEntity="Parameter, LocalVariable" VisibilityMask="VisibilityMask" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
267 270 <NamingRule Name="Type Parameters" AffectedEntity="TypeParameter" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
268 271 <RequiredPrefixes>
269 272 <String>T</String>
270 273 </RequiredPrefixes>
271 274 </NamingRule>
272 275 </Rules>
273 276 </NameConventionPolicy>
274 277 </Policies>
275 278 </Properties>
276 279 </MonoDevelop>
277 280 </ProjectExtensions>
278 281 <ItemGroup />
279 282 </Project> No newline at end of file
@@ -1,626 +1,626
1 1 using Implab.Formats.Json;
2 2 using System;
3 3 using System.Collections.Generic;
4 4 using System.Globalization;
5 5 using System.Linq;
6 6 using System.Xml;
7 7
8 8 namespace Implab.Xml {
9 9 public class JsonXmlReader : XmlReader {
10 10 struct JsonContext {
11 11 public string localName;
12 12 public bool skip;
13 13 }
14 14
15 JsonParser m_parser;
15 JsonReader m_parser;
16 16 JsonXmlReaderOptions m_options;
17 17 JsonXmlReaderPosition m_position = JsonXmlReaderPosition.Initial;
18 18 XmlNameTable m_nameTable;
19 19
20 20 readonly string m_jsonRootName;
21 21 readonly string m_jsonNamespace;
22 22 readonly string m_jsonPrefix;
23 23 readonly bool m_jsonFlattenArrays;
24 24 readonly string m_jsonArrayItemName;
25 25
26 26 string m_jsonLocalName;
27 27 string m_jsonValueName;
28 28 bool m_jsonSkip; // indicates wheather to generate closing tag for objects or arrays
29 29
30 30 readonly Stack<JsonContext> m_jsonNameStack = new Stack<JsonContext>();
31 31
32 32 XmlQualifiedName m_elementQName;
33 33 string m_elementPrefix;
34 34 int m_elementDepth;
35 35 bool m_elementIsEmpty;
36 36
37 37 XmlQualifiedName m_qName;
38 38 string m_prefix;
39 39 int m_xmlDepth;
40 40
41 41 XmlSimpleAttribute[] m_attributes;
42 42 object m_value;
43 43 bool m_isEmpty;
44 44
45 45 XmlNodeType m_nodeType = XmlNodeType.None;
46 46
47 47 bool m_isAttribute; // indicates that we are reading attribute nodes
48 48 int m_currentAttribute;
49 49 bool m_currentAttributeRead;
50 50
51 51
52 52 XmlNameContext m_context;
53 53
54 54 readonly string m_xmlnsPrefix;
55 55 readonly string m_xmlnsNamespace;
56 56 readonly string m_xsiPrefix;
57 57 readonly string m_xsiNamespace;
58 58
59 59
60 public JsonXmlReader(JsonParser parser, JsonXmlReaderOptions options) {
60 public JsonXmlReader(JsonReader parser, JsonXmlReaderOptions options) {
61 61 Safe.ArgumentNotNull(parser, nameof(parser));
62 62 m_parser = parser;
63 63
64 64 m_options = options ?? new JsonXmlReaderOptions();
65 65
66 66 m_jsonFlattenArrays = m_options.FlattenArrays;
67 67 m_nameTable = m_options.NameTable ?? new NameTable();
68 68
69 69 m_jsonRootName = m_nameTable.Add(string.IsNullOrEmpty(m_options.RootName) ? "data" : m_options.RootName);
70 70 m_jsonArrayItemName = m_nameTable.Add(string.IsNullOrEmpty(m_options.ArrayItemName) ? "item" : m_options.ArrayItemName);
71 71 m_jsonNamespace = m_nameTable.Add(m_options.NamespaceUri ?? string.Empty);
72 72 m_jsonPrefix = m_nameTable.Add(m_options.NodesPrefix ?? string.Empty);
73 73 m_xmlnsPrefix = m_nameTable.Add(XmlNameContext.XmlnsPrefix);
74 74 m_xmlnsNamespace = m_nameTable.Add(XmlNameContext.XmlnsNamespace);
75 75 m_xsiPrefix = m_nameTable.Add(XmlNameContext.XsiPrefix);
76 76 m_xsiNamespace = m_nameTable.Add(XmlNameContext.XsiNamespace);
77 77
78 78 // TODO validate m_jsonRootName, m_jsonArrayItemName
79 79
80 m_context = new XmlNameContext(null);
80 m_context = new XmlNameContext(null, 0);
81 81 }
82 82
83 83 public override int AttributeCount {
84 84 get {
85 85 return m_attributes == null ? 0 : m_attributes.Length;
86 86 }
87 87 }
88 88
89 89 public override string BaseURI {
90 90 get {
91 91 return string.Empty;
92 92 }
93 93 }
94 94
95 95 public override int Depth {
96 96 get {
97 97 return m_xmlDepth;
98 98 }
99 99 }
100 100
101 101 public override bool EOF {
102 102 get {
103 103 return m_position == JsonXmlReaderPosition.Eof;
104 104 }
105 105 }
106 106
107 107 public override bool IsEmptyElement {
108 108 get { return m_isEmpty; }
109 109 }
110 110
111 111
112 112 public override string LocalName {
113 113 get {
114 114 return m_qName.Name;
115 115 }
116 116 }
117 117
118 118 public override string NamespaceURI {
119 119 get {
120 120 return m_qName.Namespace;
121 121 }
122 122 }
123 123
124 124 public override XmlNameTable NameTable {
125 125 get {
126 126 return m_nameTable;
127 127 }
128 128 }
129 129
130 130 public override XmlNodeType NodeType {
131 131 get {
132 132 return m_nodeType;
133 133 }
134 134 }
135 135
136 136 public override string Prefix {
137 137 get {
138 138 return m_prefix;
139 139 }
140 140 }
141 141
142 142 public override ReadState ReadState {
143 143 get {
144 144 switch (m_position) {
145 145 case JsonXmlReaderPosition.Initial:
146 146 return ReadState.Initial;
147 147 case JsonXmlReaderPosition.Eof:
148 148 return ReadState.EndOfFile;
149 149 case JsonXmlReaderPosition.Closed:
150 150 return ReadState.Closed;
151 151 case JsonXmlReaderPosition.Error:
152 152 return ReadState.Error;
153 153 default:
154 154 return ReadState.Interactive;
155 155 };
156 156 }
157 157 }
158 158
159 159 public override string Value {
160 160 get {
161 161 return ConvertValueToString(m_value);
162 162 }
163 163 }
164 164
165 165 static string ConvertValueToString(object value) {
166 166 if (value == null)
167 167 return string.Empty;
168 168
169 169 switch (Convert.GetTypeCode(value)) {
170 170 case TypeCode.Double:
171 171 return ((double)value).ToString(CultureInfo.InvariantCulture);
172 172 case TypeCode.String:
173 173 return (string)value;
174 174 case TypeCode.Boolean:
175 175 return (bool)value ? "true" : "false";
176 176 default:
177 177 return value.ToString();
178 178 }
179 179 }
180 180
181 181 public override string GetAttribute(int i) {
182 182 Safe.ArgumentInRange(i, 0, AttributeCount - 1, nameof(i));
183 183 return ConvertValueToString(m_attributes[i].Value);
184 184 }
185 185
186 186 public override string GetAttribute(string name) {
187 187 if (m_attributes == null)
188 188 return null;
189 189 var qName = m_context.Resolve(name);
190 190 var attr = Array.Find(m_attributes, x => x.QName == qName);
191 191 var value = ConvertValueToString(attr?.Value);
192 192 return value == string.Empty ? null : value;
193 193 }
194 194
195 195 public override string GetAttribute(string name, string namespaceURI) {
196 196 if (m_attributes == null)
197 197 return null;
198 198 var qName = new XmlQualifiedName(name, namespaceURI);
199 199 var attr = Array.Find(m_attributes, x => x.QName == qName);
200 200 var value = ConvertValueToString(attr?.Value);
201 201 return value == string.Empty ? null : value;
202 202 }
203 203
204 204 public override string LookupNamespace(string prefix) {
205 205 return m_context.ResolvePrefix(prefix);
206 206 }
207 207
208 208 public override bool MoveToAttribute(string name) {
209 209 if (m_attributes == null || m_attributes.Length == 0)
210 210 return false;
211 211
212 212 var qName = m_context.Resolve(name);
213 213 var index = Array.FindIndex(m_attributes, x => x.QName == qName);
214 214 if (index >= 0) {
215 215 MoveToAttributeImpl(index);
216 216 return true;
217 217 }
218 218 return false;
219 219 }
220 220
221 221 public override bool MoveToAttribute(string name, string ns) {
222 222 if (m_attributes == null || m_attributes.Length == 0)
223 223 return false;
224 224
225 225 var qName = m_context.Resolve(name);
226 226 var index = Array.FindIndex(m_attributes, x => x.QName == qName);
227 227 if (index >= 0) {
228 228 MoveToAttributeImpl(index);
229 229 return true;
230 230 }
231 231 return false;
232 232 }
233 233
234 234 void MoveToAttributeImpl(int i) {
235 235 if (!m_isAttribute) {
236 236 m_elementQName = m_qName;
237 237 m_elementDepth = m_xmlDepth;
238 238 m_elementPrefix = m_prefix;
239 239 m_elementIsEmpty = m_isEmpty;
240 240 m_isAttribute = true;
241 241 }
242 242
243 243 var attr = m_attributes[i];
244 244
245 245
246 246 m_currentAttribute = i;
247 247 m_currentAttributeRead = false;
248 248 m_nodeType = XmlNodeType.Attribute;
249 249
250 250 m_xmlDepth = m_elementDepth + 1;
251 251 m_qName = attr.QName;
252 252 m_value = attr.Value;
253 253 m_prefix = attr.Prefix;
254 254 }
255 255
256 256 public override bool MoveToElement() {
257 257 if (m_isAttribute) {
258 258 m_value = null;
259 259 m_nodeType = XmlNodeType.Element;
260 260 m_xmlDepth = m_elementDepth;
261 261 m_prefix = m_elementPrefix;
262 262 m_qName = m_elementQName;
263 263 m_isEmpty = m_elementIsEmpty;
264 264 m_isAttribute = false;
265 265 return true;
266 266 }
267 267 return false;
268 268 }
269 269
270 270 public override bool MoveToFirstAttribute() {
271 271 if (m_attributes != null && m_attributes.Length > 0) {
272 272 MoveToAttributeImpl(0);
273 273 return true;
274 274 }
275 275 return false;
276 276 }
277 277
278 278 public override bool MoveToNextAttribute() {
279 279 if (m_isAttribute) {
280 280 var next = m_currentAttribute + 1;
281 281 if (next < AttributeCount) {
282 282 MoveToAttributeImpl(next);
283 283 return true;
284 284 }
285 285 return false;
286 286 } else {
287 287 return MoveToFirstAttribute();
288 288 }
289 289
290 290 }
291 291
292 292 public override bool ReadAttributeValue() {
293 293 if (!m_isAttribute || m_currentAttributeRead)
294 294 return false;
295 295
296 296 ValueNode(m_attributes[m_currentAttribute].Value);
297 297 m_currentAttributeRead = true;
298 298 return true;
299 299 }
300 300
301 301 public override void ResolveEntity() {
302 302 /* do nothing */
303 303 }
304 304
305 305 /// <summary>
306 306 /// Determines do we need to increase depth after the current node
307 307 /// </summary>
308 308 /// <returns></returns>
309 309 public bool IsSibling() {
310 310 switch (m_nodeType) {
311 311 case XmlNodeType.None: // start document
312 312 case XmlNodeType.Attribute: // after attribute only it's content can be iterated with ReadAttributeValue method
313 313 return false;
314 314 case XmlNodeType.Element:
315 315 // if the elemnt is empty the next element will be it's sibling
316 316 return m_isEmpty;
317
318 case XmlNodeType.Document:
319 case XmlNodeType.DocumentFragment:
320 case XmlNodeType.Entity:
321 case XmlNodeType.Text:
322 case XmlNodeType.CDATA:
323 case XmlNodeType.EntityReference:
324 case XmlNodeType.ProcessingInstruction:
325 case XmlNodeType.Comment:
326 case XmlNodeType.DocumentType:
327 case XmlNodeType.Notation:
328 case XmlNodeType.Whitespace:
329 case XmlNodeType.SignificantWhitespace:
330 case XmlNodeType.EndElement:
331 case XmlNodeType.EndEntity:
332 case XmlNodeType.XmlDeclaration:
333 317 default:
334 318 return true;
335 319 }
336 320 }
337 321
338 322 void ValueNode(object value) {
339 323 if (!IsSibling()) // the node is nested
340 324 m_xmlDepth++;
341 325
342 326 m_qName = XmlQualifiedName.Empty;
343 327 m_nodeType = XmlNodeType.Text;
344 328 m_prefix = string.Empty;
345 329 m_value = value;
346 330 m_isEmpty = false;
347 331 m_attributes = null;
348 332 }
349 333
350 334 void ElementNode(string name, string ns, XmlSimpleAttribute[] attrs, bool empty) {
351 335 if (!IsSibling()) // the node is nested
352 336 m_xmlDepth++;
353 337
354 m_context = new XmlNameContext(m_context);
338 var context = m_context;
355 339 List<XmlSimpleAttribute> definedAttrs = null;
356 340
357 341 // define new namespaces
358 342 if (attrs != null) {
359 343 foreach (var attr in attrs) {
360 344 if (attr.QName.Name == "xmlns") {
361 m_context.DefinePrefix(ConvertValueToString(attr.Value), string.Empty);
345 if (context == m_context)
346 context = new XmlNameContext(m_context, m_xmlDepth);
347 context.DefinePrefix(ConvertValueToString(attr.Value), string.Empty);
362 348 } else if (attr.Prefix == m_xmlnsPrefix) {
363 m_context.DefinePrefix(ConvertValueToString(attr.Value), attr.QName.Name);
349 if (context == m_context)
350 context = new XmlNameContext(m_context, m_xmlDepth);
351 context.DefinePrefix(ConvertValueToString(attr.Value), attr.QName.Name);
364 352 } else {
365 353 string attrPrefix;
366 354 if (string.IsNullOrEmpty(attr.QName.Namespace))
367 355 continue;
368 356
369 357 // auto-define prefixes
370 if (!m_context.LookupNamespacePrefix(attr.QName.Namespace, out attrPrefix) || string.IsNullOrEmpty(attrPrefix)) {
358 if (!context.LookupNamespacePrefix(attr.QName.Namespace, out attrPrefix) || string.IsNullOrEmpty(attrPrefix)) {
371 359 // new namespace prefix added
372 attrPrefix = m_context.CreateNamespacePrefix(attr.QName.Namespace);
360 attrPrefix = context.CreateNamespacePrefix(attr.QName.Namespace);
373 361 attr.Prefix = attrPrefix;
374 362
375 363 if (definedAttrs == null)
376 364 definedAttrs = new List<XmlSimpleAttribute>();
377 365
378 366 definedAttrs.Add(new XmlSimpleAttribute(attrPrefix, m_xmlnsNamespace, m_xmlnsPrefix, attr.QName.Namespace));
379 367 }
380 368 }
381 369 }
382 370 }
383 371
384 372 string p;
385 373 // auto-define prefixes
386 if (!m_context.LookupNamespacePrefix(ns, out p)) {
387 p = m_context.CreateNamespacePrefix(ns);
374 if (!context.LookupNamespacePrefix(ns, out p)) {
375 if (context == m_context)
376 context = new XmlNameContext(m_context, m_xmlDepth);
377 p = context.CreateNamespacePrefix(ns);
388 378 if (definedAttrs == null)
389 379 definedAttrs = new List<XmlSimpleAttribute>();
390 380
391 381 definedAttrs.Add(new XmlSimpleAttribute(p, m_xmlnsNamespace, m_xmlnsPrefix, ns));
392 382 }
393 383
394 384 if (definedAttrs != null) {
395 385 if (attrs != null)
396 386 definedAttrs.AddRange(attrs);
397 387 attrs = definedAttrs.ToArray();
398 388 }
399 389
390 if (!empty)
391 m_context = context;
392
400 393 m_nodeType = XmlNodeType.Element;
401 394 m_qName = new XmlQualifiedName(name, ns);
402 395 m_prefix = p;
403 396 m_value = null;
404 397 m_isEmpty = empty;
405 398 m_attributes = attrs;
406 399 }
407 400
408 401 void EndElementNode(string name, string ns) {
409 if (IsSibling()) // closing the element which has children
402 if (IsSibling()) {
403 // closing the element which has children
410 404 m_xmlDepth--;
405 }
411 406
412 407 string p;
413 408 if (!m_context.LookupNamespacePrefix(ns, out p))
414 409 throw new Exception($"Failed to lookup namespace '{ns}'");
415 410
411 if (m_context.Depth == m_xmlDepth)
416 412 m_context = m_context.ParentContext;
413
417 414 m_nodeType = XmlNodeType.EndElement;
418 415 m_prefix = p;
419 416 m_qName = new XmlQualifiedName(name, ns);
420 417 m_value = null;
421 418 m_attributes = null;
422 419 m_isEmpty = false;
423 420 }
424 421
425 422 void XmlDeclaration() {
426 423 if (!IsSibling()) // the node is nested
427 424 m_xmlDepth++;
428 425 m_nodeType = XmlNodeType.XmlDeclaration;
429 426 m_qName = new XmlQualifiedName("xml");
430 427 m_value = "version='1.0'";
431 428 m_prefix = string.Empty;
432 429 m_attributes = null;
433 430 m_isEmpty = false;
434 431 }
435 432
436 433 public override bool Read() {
437 434 try {
438 435 string elementName;
439 436 XmlSimpleAttribute[] elementAttrs = null;
440 437 MoveToElement();
441 438
442 439 switch (m_position) {
443 440 case JsonXmlReaderPosition.Initial:
444 441 m_jsonLocalName = m_jsonRootName;
445 442 m_jsonSkip = false;
446 443 XmlDeclaration();
447 444 m_position = JsonXmlReaderPosition.Declaration;
448 445 return true;
449 446 case JsonXmlReaderPosition.Declaration:
450 447 elementAttrs = new[] {
451 448 new XmlSimpleAttribute(m_xsiPrefix, m_xmlnsNamespace, m_xmlnsPrefix, m_xsiNamespace),
452 449 string.IsNullOrEmpty(m_jsonPrefix) ?
453 450 new XmlSimpleAttribute(m_xmlnsPrefix, string.Empty, string.Empty, m_jsonNamespace) :
454 451 new XmlSimpleAttribute(m_jsonPrefix, m_xmlnsNamespace, m_xmlnsPrefix, m_jsonNamespace)
455 452 };
456 453 break;
457 454 case JsonXmlReaderPosition.ValueElement:
458 455 if (!m_isEmpty) {
456 if (m_parser.ElementValue != null && !m_parser.ElementValue.Equals(string.Empty))
459 457 ValueNode(m_parser.ElementValue);
458 else
459 goto case JsonXmlReaderPosition.ValueContent;
460 460 m_position = JsonXmlReaderPosition.ValueContent;
461 461 return true;
462 462 } else {
463 463 m_position = JsonXmlReaderPosition.ValueEndElement;
464 464 break;
465 465 }
466 466 case JsonXmlReaderPosition.ValueContent:
467 467 EndElementNode(m_jsonValueName, m_jsonNamespace);
468 468 m_position = JsonXmlReaderPosition.ValueEndElement;
469 469 return true;
470 470 case JsonXmlReaderPosition.Eof:
471 471 case JsonXmlReaderPosition.Closed:
472 472 case JsonXmlReaderPosition.Error:
473 473 return false;
474 474 }
475 475
476 476 while (m_parser.Read()) {
477 477 var jsonName = m_nameTable.Add(m_parser.ElementName);
478 478
479 479 switch (m_parser.ElementType) {
480 480 case JsonElementType.BeginObject:
481 481 if (!EnterJsonObject(jsonName, out elementName))
482 482 continue;
483 483
484 484 m_position = JsonXmlReaderPosition.BeginObject;
485 485 ElementNode(elementName, m_jsonNamespace, elementAttrs, false);
486 486 break;
487 487 case JsonElementType.EndObject:
488 488 if (!LeaveJsonScope(out elementName))
489 489 continue;
490 490
491 491 m_position = JsonXmlReaderPosition.EndObject;
492 492 EndElementNode(elementName, m_jsonNamespace);
493 493 break;
494 494 case JsonElementType.BeginArray:
495 495 if (!EnterJsonArray(jsonName, out elementName))
496 496 continue;
497 497
498 498 m_position = JsonXmlReaderPosition.BeginArray;
499 499 ElementNode(elementName, m_jsonNamespace, elementAttrs, false);
500 500 break;
501 501 case JsonElementType.EndArray:
502 502 if (!LeaveJsonScope(out elementName))
503 503 continue;
504 504
505 505 m_position = JsonXmlReaderPosition.EndArray;
506 506 EndElementNode(elementName, m_jsonNamespace);
507 507 break;
508 508 case JsonElementType.Value:
509 509 if (!VisitJsonValue(jsonName, out m_jsonValueName))
510 510 continue;
511 511
512 512 m_position = JsonXmlReaderPosition.ValueElement;
513 513 if (m_parser.ElementValue == null)
514 514 // generate empty element with xsi:nil="true" attribute
515 515 ElementNode(
516 516 m_jsonValueName,
517 517 m_jsonNamespace,
518 518 new[] {
519 519 new XmlSimpleAttribute("nil", m_xsiNamespace, m_xsiPrefix, true)
520 520 },
521 521 true
522 522 );
523 523 else
524 ElementNode(m_jsonValueName, m_jsonNamespace, elementAttrs, m_parser.ElementValue as string == string.Empty);
524 ElementNode(m_jsonValueName, m_jsonNamespace, elementAttrs, m_parser.ElementValue.Equals(string.Empty));
525 525 break;
526 526 default:
527 527 throw new Exception($"Unexpected JSON element {m_parser.ElementType}: {m_parser.ElementName}");
528 528 }
529 529 return true;
530 530 }
531 531
532 532 m_position = JsonXmlReaderPosition.Eof;
533 533 return false;
534 534 } catch {
535 535 m_position = JsonXmlReaderPosition.Error;
536 536 throw;
537 537 }
538 538 }
539 539
540 540 void SaveJsonName() {
541 541 m_jsonNameStack.Push(new JsonContext {
542 542 skip = m_jsonSkip,
543 543 localName = m_jsonLocalName
544 544 });
545 545
546 546 }
547 547
548 548 bool EnterJsonObject(string name, out string elementName) {
549 549 SaveJsonName();
550 550 m_jsonSkip = false;
551 551
552 552 if (string.IsNullOrEmpty(name)) {
553 553 if (m_jsonNameStack.Count != 1 && !m_jsonFlattenArrays)
554 554 m_jsonLocalName = m_jsonArrayItemName;
555 555 } else {
556 556 m_jsonLocalName = name;
557 557 }
558 558
559 559 elementName = m_jsonLocalName;
560 560 return true;
561 561 }
562 562
563 563 /// <summary>
564 564 /// Called when JSON parser visits BeginArray ('[') element.
565 565 /// </summary>
566 566 /// <param name="name">Optional property name if the array is the member of an object</param>
567 567 /// <returns>true if element should be emited, false otherwise</returns>
568 568 bool EnterJsonArray(string name, out string elementName) {
569 569 SaveJsonName();
570 570
571 571 if (string.IsNullOrEmpty(name)) {
572 572 // m_jsonNameStack.Count == 1 means the root node
573 573 if (m_jsonNameStack.Count != 1 && !m_jsonFlattenArrays)
574 574 m_jsonLocalName = m_jsonArrayItemName;
575 575
576 576 m_jsonSkip = false; // we should not flatten arrays inside arrays or in the document root
577 577 } else {
578 578 m_jsonLocalName = name;
579 579 m_jsonSkip = m_jsonFlattenArrays;
580 580 }
581 581 elementName = m_jsonLocalName;
582 582
583 583 return !m_jsonSkip;
584 584 }
585 585
586 586 bool VisitJsonValue(string name, out string elementName) {
587 587 if (string.IsNullOrEmpty(name)) {
588 588 // m_jsonNameStack.Count == 0 means that JSON document consists from simple value
589 589 elementName = (m_jsonNameStack.Count == 0 || m_jsonFlattenArrays) ? m_jsonLocalName : m_jsonArrayItemName;
590 590 } else {
591 591 elementName = name;
592 592 }
593 593 return true;
594 594 }
595 595
596 596 bool LeaveJsonScope(out string elementName) {
597 597 elementName = m_jsonLocalName;
598 598 var skip = m_jsonSkip;
599 599
600 600 var prev = m_jsonNameStack.Pop();
601 601 m_jsonLocalName = prev.localName;
602 602 m_jsonSkip = prev.skip;
603 603
604 604 return !skip;
605 605 }
606 606
607 607 public override string ToString() {
608 608 switch (NodeType) {
609 609 case XmlNodeType.Element:
610 610 return $"<{Name} {string.Join(" ", (m_attributes ?? new XmlSimpleAttribute[0]).Select(x => $"{x.Prefix}{(string.IsNullOrEmpty(x.Prefix) ? "" : ":")}{x.QName.Name}='{ConvertValueToString(x.Value)}'"))} {(IsEmptyElement ? "/" : "")}>";
611 611 case XmlNodeType.Attribute:
612 612 return $"@{Name}";
613 613 case XmlNodeType.Text:
614 614 return $"{Value}";
615 615 case XmlNodeType.CDATA:
616 616 return $"<![CDATA[{Value}]]>";
617 617 case XmlNodeType.EntityReference:
618 618 return $"&{Name};";
619 619 case XmlNodeType.EndElement:
620 620 return $"</{Name}>";
621 621 default:
622 622 return $".{NodeType} {Name} {Value}";
623 623 }
624 624 }
625 625 }
626 626 }
@@ -1,22 +1,22
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.Linq;
4 4 using System.Text;
5 5 using System.Threading.Tasks;
6 6
7 7 namespace Implab.Xml {
8 public enum JsonXmlReaderPosition {
8 enum JsonXmlReaderPosition {
9 9 Initial,
10 10 Declaration,
11 11 BeginArray,
12 12 BeginObject,
13 13 EndArray,
14 14 EndObject,
15 15 ValueElement,
16 16 ValueContent,
17 17 ValueEndElement,
18 18 Eof,
19 19 Closed,
20 20 Error
21 21 }
22 22 }
@@ -1,111 +1,119
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.Linq;
4 4 using System.Text;
5 5 using System.Threading.Tasks;
6 6 using System.Xml;
7 7
8 8 namespace Implab.Xml {
9 public class XmlNameContext {
9 class XmlNameContext {
10 10 public const string XmlnsNamespace = "http://www.w3.org/2000/xmlns/";
11 11 public const string XmlnsPrefix = "xmlns";
12 12 public const string XmlNamespace = "http://www.w3.org/XML/1998/namespace";
13 13 public const string XmlPrefix = "xml";
14 14 public const string XsiNamespace = "http://www.w3.org/2001/XMLSchema-instance";
15 15 public const string XsiPrefix = "xsi";
16 16
17 17 readonly static char[] _qNameDelim = new[] { ':' };
18 18
19 19 Dictionary<string, string> m_ns2prefix;
20 20 Dictionary<string, string> m_prefix2ns;
21 21 int m_nextPrefix = 1;
22 string m_lastNs;
23 string m_lastPrefix;
22 24
23 25 public XmlNameContext ParentContext { get; private set; }
24 26
25 public XmlNameContext(XmlNameContext parent) {
27 public int Depth { get; private set; }
28
29 public XmlNameContext(XmlNameContext parent, int depth) {
26 30 ParentContext = parent;
31 Depth = depth;
32
27 33 if (parent == null) {
28 34 DefinePrefixNoCheck(XmlnsNamespace, XmlnsPrefix);
29 35 DefinePrefixNoCheck(XmlNamespace, XmlPrefix);
30 36 } else {
31 37 m_nextPrefix = parent.m_nextPrefix;
32 38 }
33 39 }
34 40
35 41 public bool LookupNamespacePrefix(string ns, out string prefix) {
36 42 if (ns == null)
37 43 ns = string.Empty;
44 if (ns == m_lastNs) {
45 prefix = m_lastPrefix;
46 return true;
47 }
48
38 49
39 50 prefix = null;
40 51 for (var ctx = this; ctx != null; ctx = ctx.ParentContext) {
41 if (ctx.m_ns2prefix?.TryGetValue(ns, out prefix) == true) {
42 if (ctx != this) // cache for the future use
43 DefinePrefixNoCheck(ns, prefix);
52 if (ctx.m_ns2prefix != null && ctx.m_ns2prefix.TryGetValue(ns, out prefix)) {
53 m_lastNs = ns;
54 m_lastPrefix = prefix;
44 55 return true;
45 56 }
46 57 }
47 58 return false;
48 59 }
49 60
50 61 public string CreateNamespacePrefix(string ns) {
51 62 var prefix = $"p{m_nextPrefix++}";
52 63 DefinePrefixNoCheck(ns, prefix);
53 64 return prefix;
54 65 }
55 66
56 67 void DefinePrefixNoCheck(string ns, string prefix) {
57 68 if (ns == null)
58 69 ns = string.Empty;
59 70 if (prefix == null)
60 71 prefix = string.Empty;
61 72
62 73 if (m_ns2prefix == null)
63 74 m_ns2prefix = new Dictionary<string, string>();
64 75 m_ns2prefix[ns] = prefix;
65 76
66 77 if (m_prefix2ns == null)
67 78 m_prefix2ns = new Dictionary<string, string>();
68 79 m_prefix2ns[prefix] = ns;
69 80 }
70 81
71 82 public void DefinePrefix(string ns, string prefix) {
72 83 // according to https://www.w3.org/TR/xml-names/#ns-decl
73 84
74 85 // It MUST NOT be declared . Other prefixes MUST NOT be bound to this namespace name, and it MUST NOT be declared as the default namespace
75 86 if (ns == XmlnsNamespace)
76 87 throw new Exception($"Attempt to define xmlns:{prefix}='{ns}'");
77 88
78 89 // It MAY, but need not, be declared, and MUST NOT be bound to any other namespace name
79 90 if (ns == XmlNamespace && prefix != XmlPrefix)
80 91 throw new Exception($"Attempt to define xmlns:{prefix}='{ns}'");
81 92
82 93 // add mapping
83 94 DefinePrefixNoCheck(ns, prefix);
84 95 }
85 96
86 97 public string ResolvePrefix(string prefix) {
87 98 if (prefix == null)
88 99 prefix = string.Empty;
89 100 string ns = null;
90 101 for(var ctx = this; ctx != null; ctx = ctx.ParentContext) {
91 if (ctx.m_prefix2ns?.TryGetValue(prefix, out ns) == true) {
92 if (ctx != this) // cache for the future use
93 DefinePrefixNoCheck(ns, prefix);
102 if (ctx.m_prefix2ns != null && ctx.m_prefix2ns.TryGetValue(prefix, out ns) == true)
94 103 return ns;
95 104 }
96 }
97 105 return null;
98 106 }
99 107
100 108 public XmlQualifiedName Resolve(string name) {
101 109 Safe.ArgumentNotEmpty(name, nameof(name));
102 110 var parts = name.Split(_qNameDelim, 2, StringSplitOptions.RemoveEmptyEntries);
103 111
104 112 if (parts.Length == 2) {
105 113 return new XmlQualifiedName(parts[1], ResolvePrefix(parts[0]));
106 114 } else {
107 115 return new XmlQualifiedName(parts[0], ResolvePrefix(string.Empty));
108 116 }
109 117 }
110 118 }
111 119 }
General Comments 3
Under Review
author

Auto status change to "Under Review"

Approved
author

ok, latest stable version should be in default

You need to be logged in to leave comments. Login now