##// END OF EJS Templates
Rewritten JsonScanner, JsonParser, fixed naming style
cin -
r228:6fa235c5a760 v2
parent child
Show More
@@ -0,0 +1,42
1 using Implab.Automaton;
2 using System;
3 using System.Collections.Generic;
4 using System.Linq;
5 using System.Runtime.CompilerServices;
6 using System.Text;
7 using System.Threading.Tasks;
8
9 namespace Implab.Formats {
10 public class CharMap : IAlphabet<char> {
11 readonly char m_min;
12 readonly char m_max;
13 readonly int[] m_map;
14
15 public CharMap(char min, int[] map) {
16 Safe.ArgumentNotNull(map, nameof(map));
17 Count = map.Max()+1;
18 m_min = min;
19 m_map = map;
20 m_max = (char)(min + map.Length);
21 }
22
23 public int Count {
24 get; private set;
25 }
26
27 public bool Contains(char symbol) {
28 return symbol >= m_min && symbol <= m_max && m_map[symbol-m_min] != AutomatonConst.UNCLASSIFIED_INPUT;
29 }
30
31 public IEnumerable<char> GetSymbols(int cls) {
32 for (var i = 0; i < m_map.Length; i++)
33 if (m_map[i] == cls)
34 yield return (char)(i + m_min);
35 }
36
37 [MethodImpl(MethodImplOptions.AggressiveInlining)]
38 public int Translate(char symbol) {
39 return symbol >= m_min && symbol <= m_max ? m_map[symbol-m_min] : AutomatonConst.UNCLASSIFIED_INPUT;
40 }
41 }
42 }
@@ -0,0 +1,84
1 using Implab.Automaton;
2 using System;
3 using System.Collections.Generic;
4 using System.Linq;
5 using System.Text;
6 using System.Threading.Tasks;
7
8 namespace Implab.Formats {
9 public class InputScanner<TTag> {
10 readonly TTag[] m_tags;
11 readonly int m_initialState;
12 readonly int[,] m_dfa;
13 readonly CharMap m_alphabet;
14 readonly bool[] m_final;
15
16 int m_position;
17 int m_state;
18
19 public InputScanner(int[,] dfaTable, bool[] finalStates, TTag[] tags, int initialState, CharMap alphabet) {
20 Safe.ArgumentNotNull(dfaTable, nameof(dfaTable));
21 Safe.ArgumentNotNull(finalStates, nameof(finalStates));
22 Safe.ArgumentNotNull(tags, nameof(tags));
23 Safe.ArgumentNotNull(alphabet, nameof(alphabet));
24
25 m_dfa = dfaTable;
26 m_final = finalStates;
27 m_tags = tags;
28 m_initialState = initialState;
29 m_alphabet = alphabet;
30 }
31
32 public TTag Tag {
33 get {
34 return m_tags[m_state];
35 }
36 }
37
38 public int Position {
39 get {
40 return m_position;
41 }
42 }
43
44 public bool IsFinal {
45 get {
46 return m_final[m_state];
47 }
48 }
49
50 public void Reset() {
51 m_state = m_initialState;
52 }
53
54 public InputScanner<TTag> Clone() {
55 var clone = new InputScanner<TTag>(m_dfa, m_final, m_tags, m_initialState, m_alphabet);
56 clone.m_state = m_state;
57 clone.m_position = m_position;
58 return clone;
59 }
60
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;
68 var next = m_state;
69
70 while(offset < max) {
71 next = m_dfa[next, m_alphabet.Translate(data[offset])];
72 if (next == AutomatonConst.UNREACHABLE_STATE) {
73 // scanner stops on the next position after last recognized symbol
74 m_position = offset;
75 return false;
76 }
77 m_state = next;
78 offset++;
79 }
80 m_position = offset;
81 return true;
82 }
83 }
84 }
@@ -0,0 +1,76
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6
7 namespace Implab.Formats.Json {
8 public class JsonStringScanner : JsonScanner {
9 const int _defaultBuffer = 64;
10
11 readonly string m_data;
12 int m_offset;
13
14 JsonStringScanner(string data, char[] buffer, int pos, int length, int offset) : base(buffer, pos, length) {
15 m_data = data;
16 m_offset = offset;
17 }
18
19 protected override int Read(char[] buffer, int offset, int size) {
20 if (m_data == null)
21 return 0;
22 if (m_offset >= m_data.Length)
23 return 0;
24
25 var count = Math.Min(size, m_data.Length - m_offset);
26
27 m_data.CopyTo(m_offset, buffer, offset, count);
28 m_offset += count;
29
30 return count;
31 }
32
33 public static JsonStringScanner Create(string data) {
34 Safe.ArgumentNotNull(data, nameof(data));
35
36 if (data.Length <= _defaultBuffer)
37 return new JsonStringScanner(null, data.ToCharArray(), 0, data.Length, data.Length);
38
39 var buffer = new char[_defaultBuffer];
40 data.CopyTo(0, buffer, 0, _defaultBuffer);
41 return new JsonStringScanner(data, buffer, 0, _defaultBuffer, _defaultBuffer);
42 }
43
44 public static JsonStringScanner Create(string data, int offset, int length) {
45 Safe.ArgumentNotNull(data, nameof(data));
46 Safe.ArgumentGreaterThan(offset, 0, nameof(offset));
47 Safe.ArgumentGreaterThan(length, 0, nameof(length));
48
49 if (offset + length > data.Length)
50 throw new ArgumentOutOfRangeException("Specified offset and length are out of the string bounds");
51
52 if (length <= _defaultBuffer) {
53 var buffer = new char[length];
54 data.CopyTo(offset, buffer, 0, length);
55
56 return new JsonStringScanner(null, buffer, 0, length, length);
57 } else {
58 var buffer = new char[_defaultBuffer];
59 data.CopyTo(offset, buffer, 0, _defaultBuffer);
60 return new JsonStringScanner(data, buffer, 0, _defaultBuffer, offset + _defaultBuffer);
61 }
62 }
63
64 public static JsonStringScanner Create(char[] data, int offset, int length) {
65 Safe.ArgumentNotNull(data, nameof(data));
66 Safe.ArgumentGreaterThan(offset, 0, nameof(offset));
67 Safe.ArgumentGreaterThan(length, 0, nameof(length));
68
69 if (offset + length > data.Length)
70 throw new ArgumentOutOfRangeException("Specified offset and length are out of the array bounds");
71
72 return new JsonStringScanner(null, data, offset, offset + length, offset + length);
73
74 }
75 }
76 }
@@ -0,0 +1,49
1 using System;
2 using System.Collections.Generic;
3 using System.IO;
4 using System.Linq;
5 using System.Text;
6 using System.Threading.Tasks;
7
8 namespace Implab.Formats.Json {
9 public class JsonTextScanner : JsonScanner {
10 const int _bufferSize = 4096;
11 readonly TextReader m_reader;
12
13 JsonTextScanner(TextReader reader, char[] buffer) : base(buffer, 0, 0) {
14 m_reader = reader;
15 }
16
17 protected override int Read(char[] buffer, int offset, int size) {
18 return m_reader.Read(buffer, offset, size);
19 }
20
21 public static JsonTextScanner Create(string file, Encoding encoding) {
22 return new JsonTextScanner(new StreamReader(file, encoding), new char[_bufferSize]);
23 }
24
25 public static JsonTextScanner Create(string file) {
26 return new JsonTextScanner(new StreamReader(file), new char[_bufferSize]);
27 }
28
29 public static JsonTextScanner Create(Stream stream, Encoding encoding) {
30 return new JsonTextScanner(new StreamReader(stream, encoding), new char[_bufferSize]);
31 }
32
33 public static JsonTextScanner Create(Stream stream) {
34 return new JsonTextScanner(new StreamReader(stream), new char[_bufferSize]);
35 }
36
37 public static JsonTextScanner Create(TextReader reader) {
38 Safe.ArgumentNotNull(reader, nameof(reader));
39 return new JsonTextScanner(reader, new char[_bufferSize]);
40 }
41
42 protected override void Dispose(bool disposing) {
43 if (disposing)
44 Safe.Dispose(m_reader);
45
46 base.Dispose(disposing);
47 }
48 }
49 }
@@ -1,145 +1,146
1 1 using NUnit.Framework;
2 2 using System;
3 using Implab.Formats.JSON;
4 3 using Implab.Automaton;
5 4 using Implab.Xml;
6 5 using System.Xml;
7 using System.Text;
6 using Implab.Formats;
7 using Implab.Formats.Json;
8 8
9 9 namespace Implab.Format.Test {
10 10 [TestFixture]
11 11 public class JsonTests {
12
12 13 [Test]
13 14 public void TestScannerValidTokens() {
14 using (var scanner = new JSONScanner(@"9123, -123, 0, 0.1, -0.2, -0.1e3, 1.3E-3, ""some \t\n\u0020 text"", literal []{}:")) {
15 using (var scanner = JsonStringScanner.Create(@"9123, -123, 0, 0.1, -0.2, -0.1e3, 1.3E-3, ""some \t\n\u0020 text"", literal []{}:")) {
15 16
16 17 Tuple<JsonTokenType, object>[] expexted = {
17 18 new Tuple<JsonTokenType,object>(JsonTokenType.Number, 9123d),
18 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "),
19 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
19 20 new Tuple<JsonTokenType,object>(JsonTokenType.Number, -123d),
20 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "),
21 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
21 22 new Tuple<JsonTokenType,object>(JsonTokenType.Number, 0d),
22 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "),
23 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
23 24 new Tuple<JsonTokenType,object>(JsonTokenType.Number, 0.1d),
24 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "),
25 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
25 26 new Tuple<JsonTokenType,object>(JsonTokenType.Number, -0.2d),
26 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "),
27 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
27 28 new Tuple<JsonTokenType,object>(JsonTokenType.Number, -0.1e3d),
28 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "),
29 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
29 30 new Tuple<JsonTokenType,object>(JsonTokenType.Number, 1.3E-3d),
30 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "),
31 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
31 32 new Tuple<JsonTokenType,object>(JsonTokenType.String, "some \t\n text"),
32 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "),
33 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
33 34 new Tuple<JsonTokenType,object>(JsonTokenType.Literal, "literal"),
34 new Tuple<JsonTokenType,object>(JsonTokenType.BeginArray, " ["),
35 new Tuple<JsonTokenType,object>(JsonTokenType.EndArray, "]"),
36 new Tuple<JsonTokenType,object>(JsonTokenType.BeginObject, "{"),
37 new Tuple<JsonTokenType,object>(JsonTokenType.EndObject, "}"),
38 new Tuple<JsonTokenType,object>(JsonTokenType.NameSeparator, ":")
35 new Tuple<JsonTokenType,object>(JsonTokenType.BeginArray, null),
36 new Tuple<JsonTokenType,object>(JsonTokenType.EndArray, null),
37 new Tuple<JsonTokenType,object>(JsonTokenType.BeginObject, null),
38 new Tuple<JsonTokenType,object>(JsonTokenType.EndObject, null),
39 new Tuple<JsonTokenType,object>(JsonTokenType.NameSeparator, null)
39 40 };
40 41
41 42 object value;
42 43 JsonTokenType tokenType;
43 44 for (var i = 0; i < expexted.Length; i++) {
44 45
45 46 Assert.IsTrue(scanner.ReadToken(out value, out tokenType));
46 47 Assert.AreEqual(expexted[i].Item1, tokenType);
47 48 Assert.AreEqual(expexted[i].Item2, value);
48 49 }
49 50
50 51 Assert.IsFalse(scanner.ReadToken(out value, out tokenType));
51 52 }
52 53 }
53 54
54 55 [Test]
55 56 public void TestScannerBadTokens() {
56 57 var bad = new[] {
57 58 " 1",
58 59 " literal",
59 60 " \"",
60 61 "\"unclosed string",
61 62 "1.bad",
62 63 "001", // should be read as three numbers
63 64 "--10",
64 65 "+10",
65 66 "1.0.0",
66 67 "1e1.0",
67 68 "l1teral0",
68 69 ".123",
69 70 "-.123"
70 71 };
71 72
72 73 foreach (var json in bad) {
73 using (var scanner = new JSONScanner(json)) {
74 using (var scanner = JsonStringScanner.Create(json)) {
74 75 try {
75 76 object value;
76 77 JsonTokenType token;
77 78 scanner.ReadToken(out value, out token);
78 79 if (!Object.Equals(value, json)) {
79 80 Console.WriteLine("'{0}' is read as {1}", json, value is String ? String.Format("'{0}'", value) : value);
80 81 continue;
81 82 }
82 83 Assert.Fail("Token '{0}' shouldn't pass", json);
83 84 } catch (ParserException e) {
84 85 Console.WriteLine(e.Message);
85 86 }
86 87 }
87 88 }
88 89 }
89 90
90 91 [Test]
91 92 public void JsonXmlReaderSimpleTest() {
92 93 var json = "\"some text\"";
93 94 //Console.WriteLine($"JSON: {json}");
94 95 //Console.WriteLine("XML");
95 96 /*using (var xmlReader = new JsonXmlReader(new JSONParser(json), new JsonXmlReaderOptions { NamespaceUri = "JsonXmlReaderSimpleTest", RootName = "string", NodesPrefix = "json" })) {
96 97 Assert.AreEqual(xmlReader.ReadState, System.Xml.ReadState.Initial);
97 98
98 99 AssertRead(xmlReader, XmlNodeType.XmlDeclaration);
99 100 AssertRead(xmlReader, XmlNodeType.Element);
100 101 AssertRead(xmlReader, XmlNodeType.Text);
101 102 AssertRead(xmlReader, XmlNodeType.EndElement);
102 103 Assert.IsFalse(xmlReader.Read());
103 104 }*/
104 105
105 106 //DumpJsonParse("\"text value\"");
106 107 //DumpJsonParse("null");
107 108 //DumpJsonParse("true");
108 109 //DumpJsonParse("{}");
109 110 //DumpJsonParse("[]");
110 111 DumpJsonParse("{\"one\":1, \"two\":2}");
111 112 DumpJsonParse("[1,2,3]");
112 113 DumpJsonParse("[{\"info\": [7,8,9]}]");
113 114 DumpJsonFlatParse("[1,2,[3,4],{\"info\": [5,6]},{\"num\": [7,8,null]}, null,[null]]");
114 115 }
115 116
116 117 void AssertRead(XmlReader reader, XmlNodeType expected) {
117 118 Assert.IsTrue(reader.Read());
118 119 Console.WriteLine($"{new string(' ', reader.Depth*2)}{reader}");
119 120 Assert.AreEqual(expected, reader.NodeType);
120 121 }
121 122
122 123 void DumpJsonParse(string json) {
123 124 Console.WriteLine($"JSON: {json}");
124 125 Console.WriteLine("XML");
125 using (var xmlReader = new JsonXmlReader(new JSONParser(json), new JsonXmlReaderOptions { NamespaceUri = "JsonXmlReaderSimpleTest", NodesPrefix = "json" })) {
126 using (var xmlReader = new JsonXmlReader(new JsonParser(json), new JsonXmlReaderOptions { NamespaceUri = "JsonXmlReaderSimpleTest", NodesPrefix = "json" })) {
126 127 while (xmlReader.Read())
127 128 Console.WriteLine($"{new string(' ', xmlReader.Depth * 2)}{xmlReader}");
128 129 }
129 130 }
130 131
131 132 void DumpJsonFlatParse(string json) {
132 133 Console.WriteLine($"JSON: {json}");
133 134 Console.WriteLine("XML");
134 135 using (var xmlWriter = XmlWriter.Create(Console.Out, new XmlWriterSettings {
135 136 Indent = true,
136 137 CloseOutput = false,
137 138 ConformanceLevel = ConformanceLevel.Document
138 139 }))
139 using (var xmlReader = new JsonXmlReader(new JSONParser(json), new JsonXmlReaderOptions { NamespaceUri = "JsonXmlReaderSimpleTest", NodesPrefix = "", FlattenArrays = true })) {
140 using (var xmlReader = new JsonXmlReader(new JsonParser(json), new JsonXmlReaderOptions { NamespaceUri = "JsonXmlReaderSimpleTest", NodesPrefix = "", FlattenArrays = true })) {
140 141 xmlWriter.WriteNode(xmlReader, false);
141 142 }
142 143 }
143 144 }
144 145 }
145 146
@@ -1,16 +1,36
1 1 using System.Collections.Generic;
2 2 using System.Linq;
3 3 using Implab.Automaton;
4 using System;
4 5
5 6 namespace Implab.Formats {
6 7 public class CharAlphabet: IndexedAlphabetBase<char> {
7 8
8 9 public override int GetSymbolIndex(char symbol) {
9 10 return symbol;
10 11 }
11 12
12 13 public IEnumerable<char> InputSymbols {
13 14 get { return Enumerable.Range(char.MinValue, char.MaxValue).Cast<char>(); }
14 15 }
16
17 public CharMap CreateCharMap() {
18 var map = new Dictionary<int, int>();
19
20 int max = 0, min = char.MaxValue;
21 foreach (var p in Mappings) {
22 var index = GetSymbolIndex(p.Key);
23 max = Math.Max(max, index);
24 min = Math.Min(min, index);
25 map[index] = p.Value;
26 }
27
28 var result = new int[max - min + 1];
29
30 for (int i = 0; i < result.Length; i++)
31 map.TryGetValue(min + i, out result[i]);
32
33 return new CharMap((char)min, result);
34 }
15 35 }
16 36 }
@@ -1,99 +1,73
1 1 using Implab;
2 2 using System;
3 3 using System.Collections.Generic;
4 4 using System.Linq;
5 5 using Implab.Automaton;
6 6 using Implab.Automaton.RegularExpressions;
7 7
8 8 namespace Implab.Formats {
9 9 /// <summary>
10 10 /// Базовый абстрактный класс. Грамматика, позволяет формулировать выражения над алфавитом типа <c>char</c>.
11 11 /// </summary>
12 12 public abstract class Grammar<TSymbol> {
13 13
14 14 protected abstract IAlphabetBuilder<TSymbol> AlphabetBuilder {
15 15 get;
16 16 }
17 17
18 18 protected SymbolToken UnclassifiedToken() {
19 19 return new SymbolToken(AutomatonConst.UNCLASSIFIED_INPUT);
20 20 }
21 21
22 22 protected void DefineAlphabet(IEnumerable<TSymbol> alphabet) {
23 23 Safe.ArgumentNotNull(alphabet, "alphabet");
24 24
25 25 foreach (var ch in alphabet)
26 26 AlphabetBuilder.DefineSymbol(ch);
27 27 }
28 28
29 29 protected Token SymbolToken(TSymbol symbol) {
30 30 return Token.New(TranslateOrAdd(symbol));
31 31 }
32 32
33 33 protected Token SymbolToken(IEnumerable<TSymbol> symbols) {
34 34 Safe.ArgumentNotNull(symbols, "symbols");
35 35
36 36 return Token.New(TranslateOrAdd(symbols).ToArray());
37 37 }
38 38
39 39 protected Token SymbolSetToken(params TSymbol[] set) {
40 40 return SymbolToken(set);
41 41 }
42 42
43 43 int TranslateOrAdd(TSymbol ch) {
44 44 var t = AlphabetBuilder.Translate(ch);
45 45 if (t == AutomatonConst.UNCLASSIFIED_INPUT)
46 46 t = AlphabetBuilder.DefineSymbol(ch);
47 47 return t;
48 48 }
49 49
50 50 IEnumerable<int> TranslateOrAdd(IEnumerable<TSymbol> symbols) {
51 51 return symbols.Distinct().Select(TranslateOrAdd);
52 52 }
53 53
54 54 int TranslateOrDie(TSymbol ch) {
55 55 var t = AlphabetBuilder.Translate(ch);
56 56 if (t == AutomatonConst.UNCLASSIFIED_INPUT)
57 57 throw new ApplicationException(String.Format("Symbol '{0}' is UNCLASSIFIED", ch));
58 58 return t;
59 59 }
60 60
61 61 IEnumerable<int> TranslateOrDie(IEnumerable<TSymbol> symbols) {
62 62 return symbols.Distinct().Select(TranslateOrDie);
63 63 }
64 64
65 65 protected Token SymbolTokenExcept(IEnumerable<TSymbol> symbols) {
66 66 Safe.ArgumentNotNull(symbols, "symbols");
67 67
68 68 return Token.New( Enumerable.Range(0, AlphabetBuilder.Count).Except(TranslateOrDie(symbols)).ToArray() );
69 69 }
70
71 protected abstract IndexedAlphabetBase<TSymbol> CreateAlphabet();
72
73 protected ScannerContext<TTag> BuildScannerContext<TTag>(Token regexp) {
74
75 var dfa = new RegularDFA<TSymbol, TTag>(AlphabetBuilder);
76
77 var visitor = new RegularExpressionVisitor<TTag>(dfa);
78 regexp.Accept(visitor);
79 visitor.BuildDFA();
80
81 if (dfa.IsFinalState(dfa.InitialState))
82 throw new ApplicationException("The specified language contains empty token");
83
84 var ab = CreateAlphabet();
85 var optimal = dfa.Optimize(ab);
86
87 return new ScannerContext<TTag>(
88 optimal.CreateTransitionTable(),
89 optimal.CreateFinalStateTable(),
90 optimal.CreateTagTable(),
91 optimal.InitialState,
92 ab.GetTranslationMap()
93 );
94 }
95
96 70 }
97 71
98 72
99 73 }
@@ -1,11 +1,11
1 namespace Implab.Formats.JSON {
1 namespace Implab.Formats.Json {
2 2 /// <summary>
3 3 /// internal
4 4 /// </summary>
5 enum JSONElementContext {
5 enum JsonElementContext {
6 6 None,
7 7 Object,
8 8 Array,
9 9 Closed
10 10 }
11 11 }
@@ -1,28 +1,28
1 namespace Implab.Formats.JSON {
1 namespace Implab.Formats.Json {
2 2 /// <summary>
3 3 /// Тип элемента на котором находится парсер
4 4 /// </summary>
5 public enum JSONElementType {
5 public enum JsonElementType {
6 6 None,
7 7 /// <summary>
8 8 /// Начало объекта
9 9 /// </summary>
10 10 BeginObject,
11 11 /// <summary>
12 12 /// Конец объекта
13 13 /// </summary>
14 14 EndObject,
15 15 /// <summary>
16 16 /// Начало массива
17 17 /// </summary>
18 18 BeginArray,
19 19 /// <summary>
20 20 /// Конец массива
21 21 /// </summary>
22 22 EndArray,
23 23 /// <summary>
24 24 /// Простое значение
25 25 /// </summary>
26 26 Value
27 27 }
28 28 }
@@ -1,121 +1,148
1 1 using System.Linq;
2 2 using Implab.Automaton.RegularExpressions;
3 3 using System;
4 4 using Implab.Automaton;
5 5 using Implab.Components;
6 6
7 namespace Implab.Formats.JSON {
8 class JSONGrammar : Grammar<char> {
7 namespace Implab.Formats.Json {
8 public class JsonGrammar : Grammar<char> {
9 9 public enum TokenType {
10 10 None,
11 11 BeginObject,
12 12 EndObject,
13 13 BeginArray,
14 14 EndArray,
15 15 String,
16 16 Number,
17 17 Literal,
18 18 NameSeparator,
19 19 ValueSeparator,
20 20 Whitespace,
21 21
22 22 StringBound,
23 23 EscapedChar,
24 24 UnescapedChar,
25 25 EscapedUnicode
26 26 }
27 27
28 static LazyAndWeak<JSONGrammar> _instance = new LazyAndWeak<JSONGrammar>(() => new JSONGrammar());
28 static LazyAndWeak<JsonGrammar> _instance = new LazyAndWeak<JsonGrammar>(() => new JsonGrammar());
29 29
30 public static JSONGrammar Instance {
30 public static JsonGrammar Instance {
31 31 get { return _instance.Value; }
32 32 }
33 33
34 readonly ScannerContext<TokenType> m_jsonExpression;
35 readonly ScannerContext<TokenType> m_stringExpression;
34 readonly InputScanner<TokenType> m_jsonExpression;
35 readonly InputScanner<TokenType> m_stringExpression;
36 36 readonly CharAlphabet m_defaultAlphabet = new CharAlphabet();
37 37
38 public JSONGrammar() {
38 public CharAlphabet DefaultAlphabet { get { return m_defaultAlphabet; } }
39
40 public JsonGrammar() {
39 41 DefineAlphabet(Enumerable.Range(0, 0x20).Select(x => (char)x));
40 42 var hexDigit = SymbolRangeToken('a','f').Or(SymbolRangeToken('A','F')).Or(SymbolRangeToken('0','9'));
41 43 var digit9 = SymbolRangeToken('1', '9');
42 44 var zero = SymbolToken('0');
43 45 var digit = zero.Or(digit9);
44 46 var dot = SymbolToken('.');
45 47 var minus = SymbolToken('-');
46 48 var sign = SymbolSetToken('-', '+');
47 49 var expSign = SymbolSetToken('e', 'E');
48 50 var letters = SymbolRangeToken('a', 'z');
49 51 var integer = zero.Or(digit9.Cat(digit.EClosure()));
50 52 var frac = dot.Cat(digit.Closure());
51 53 var exp = expSign.Cat(sign.Optional()).Cat(digit.Closure());
52 54 var quote = SymbolToken('"');
53 55 var backSlash = SymbolToken('\\');
54 56 var specialEscapeChars = SymbolSetToken('\\', '"', '/', 'b', 'f', 't', 'n', 'r');
55 57 var unicodeEspace = SymbolToken('u').Cat(hexDigit.Repeat(4));
56 58 var whitespace = SymbolSetToken('\n', '\r', '\t', ' ').EClosure();
57 59 var beginObject = whitespace.Cat(SymbolToken('{')).Cat(whitespace);
58 60 var endObject = whitespace.Cat(SymbolToken('}')).Cat(whitespace);
59 61 var beginArray = whitespace.Cat(SymbolToken('[')).Cat(whitespace);
60 62 var endArray = whitespace.Cat(SymbolToken(']')).Cat(whitespace);
61 63 var nameSep = whitespace.Cat(SymbolToken(':')).Cat(whitespace);
62 64 var valueSep = whitespace.Cat(SymbolToken(',')).Cat(whitespace);
63 65
64 66 var number = minus.Optional().Cat(integer).Cat(frac.Optional()).Cat(exp.Optional());
65 67 var literal = letters.Closure();
66 68 var unescaped = SymbolTokenExcept(Enumerable.Range(0, 0x20).Union(new int[] { '\\', '"' }).Select(x => (char)x));
67 69
68 70 var jsonExpression =
69 71 number.Tag(TokenType.Number)
70 72 .Or(literal.Tag(TokenType.Literal))
71 73 .Or(quote.Tag(TokenType.StringBound))
72 74 .Or(beginObject.Tag(TokenType.BeginObject))
73 75 .Or(endObject.Tag(TokenType.EndObject))
74 76 .Or(beginArray.Tag(TokenType.BeginArray))
75 77 .Or(endArray.Tag(TokenType.EndArray))
76 78 .Or(nameSep.Tag(TokenType.NameSeparator))
77 79 .Or(valueSep.Tag(TokenType.ValueSeparator))
78 80 .Or(SymbolSetToken('\n', '\r', '\t', ' ').Closure().Tag(TokenType.Whitespace));
79 81
80 82
81 83 var jsonStringExpression =
82 84 quote.Tag(TokenType.StringBound)
83 85 .Or(backSlash.Cat(specialEscapeChars).Tag(TokenType.EscapedChar))
84 86 .Or(backSlash.Cat(unicodeEspace).Tag(TokenType.EscapedUnicode))
85 87 .Or(unescaped.Closure().Tag(TokenType.UnescapedChar));
86 88
87 89
88 m_jsonExpression = BuildScannerContext<TokenType>(jsonExpression);
89 m_stringExpression = BuildScannerContext<TokenType>(jsonStringExpression);
90 m_jsonExpression = BuildScanner(jsonExpression);
91 m_stringExpression = BuildScanner(jsonStringExpression);
92 }
90 93
94 public static InputScanner<TokenType> CreateJsonExpressionScanner() {
95 return Instance.m_jsonExpression.Clone();
96 }
91 97
98 public static InputScanner<TokenType> CreateStringExpressionScanner() {
99 return Instance.m_stringExpression.Clone();
92 100 }
93 101
94 102 protected override IAlphabetBuilder<char> AlphabetBuilder {
95 103 get {
96 104 return m_defaultAlphabet;
97 105 }
98 106 }
99 107
100 public ScannerContext<TokenType> JsonExpression {
101 get {
102 return m_jsonExpression;
103 }
104 }
105
106 public ScannerContext<TokenType> JsonStringExpression {
107 get {
108 return m_stringExpression;
109 }
110 }
111
112 108 Token SymbolRangeToken(char start, char stop) {
113 109 return SymbolToken(Enumerable.Range(start, stop - start + 1).Select(x => (char)x));
114 110 }
115 111
116 protected override IndexedAlphabetBase<char> CreateAlphabet() {
117 return new CharAlphabet();
112 public InputScanner<TokenType> BuildScanner(Token regexp) {
113 var dfa = new RegularDFA<char, TokenType>(AlphabetBuilder);
114
115 var visitor = new RegularExpressionVisitor<TokenType>(dfa);
116 regexp.Accept(visitor);
117 visitor.BuildDFA();
118
119 if (dfa.IsFinalState(dfa.InitialState))
120 throw new ApplicationException("The specified language contains empty token");
121
122 var ab = new CharAlphabet();
123 var optimal = dfa.Optimize(ab);
124
125 return new InputScanner<TokenType>(
126 optimal.CreateTransitionTable(),
127 optimal.CreateFinalStateTable(),
128 NormalizeTags(optimal.CreateTagTable()),
129 optimal.InitialState,
130 ab.CreateCharMap()
131 );
132 }
133
134 static TokenType[] NormalizeTags(TokenType[][] tags) {
135 var result = new TokenType[tags.Length];
136 for(var i = 0; i< tags.Length; i++) {
137 if (tags[i] == null || tags[i].Length == 0)
138 result[i] = default(TokenType);
139 else if (tags[i].Length == 1)
140 result[i] = tags[i][0];
141 else
142 throw new Exception($"Ambigous state tags {string.Join(", ", tags[i])}");
143 }
144 return result;
118 145 }
119 146
120 147 }
121 148 }
@@ -1,294 +1,294
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 9
10 namespace Implab.Formats.JSON {
10 namespace Implab.Formats.Json {
11 11 /// <summary>
12 12 /// Pull парсер JSON данных.
13 13 /// </summary>
14 14 /// <remarks>
15 15 /// Следует отметить отдельную интерпретацию свойства <see cref="Level"/>,
16 16 /// оно означает текущий уровень вложенности объектов, однако закрывающий
17 17 /// элемент объекта и массива имеет уровень меньше, чем сам объект.
18 18 /// <code>
19 19 /// { // Level = 1
20 20 /// "name" : "Peter", // Level = 1
21 21 /// "address" : { // Level = 2
22 22 /// city : "Stern" // Level = 2
23 23 /// } // Level = 1
24 24 /// } // Level = 0
25 25 /// </code>
26 26 /// </remarks>
27 public class JSONParser : Disposable {
27 public class JsonParser : Disposable {
28 28
29 29 enum MemberContext {
30 30 MemberName,
31 31 MemberValue
32 32 }
33 33
34 34 #region Parser rules
35 35 struct ParserContext {
36 36 readonly int[,] m_dfa;
37 37 int m_state;
38 38
39 readonly JSONElementContext m_elementContext;
39 readonly JsonElementContext m_elementContext;
40 40
41 public ParserContext(int[,] dfa, int state, JSONElementContext context) {
41 public ParserContext(int[,] dfa, int state, JsonElementContext context) {
42 42 m_dfa = dfa;
43 43 m_state = state;
44 44 m_elementContext = context;
45 45 }
46 46
47 47 public bool Move(JsonTokenType token) {
48 48 var next = m_dfa[m_state, (int)token];
49 49 if (next == AutomatonConst.UNREACHABLE_STATE)
50 50 return false;
51 51 m_state = next;
52 52 return true;
53 53 }
54 54
55 public JSONElementContext ElementContext {
55 public JsonElementContext ElementContext {
56 56 get { return m_elementContext; }
57 57 }
58 58 }
59 59
60 60 static readonly ParserContext _jsonContext;
61 61 static readonly ParserContext _objectContext;
62 62 static readonly ParserContext _arrayContext;
63 63
64 static JSONParser() {
64 static JsonParser() {
65 65
66 66 var valueExpression = MakeToken(JsonTokenType.BeginArray, JsonTokenType.BeginObject, JsonTokenType.Literal, JsonTokenType.Number, JsonTokenType.String);
67 67 var memberExpression = MakeToken(JsonTokenType.String).Cat(MakeToken(JsonTokenType.NameSeparator)).Cat(valueExpression);
68 68
69 69 var objectExpression = memberExpression
70 70 .Cat(
71 71 MakeToken(JsonTokenType.ValueSeparator)
72 72 .Cat(memberExpression)
73 73 .EClosure()
74 74 )
75 75 .Optional()
76 76 .Cat(MakeToken(JsonTokenType.EndObject))
77 77 .End();
78 78
79 79 var arrayExpression = valueExpression
80 80 .Cat(
81 81 MakeToken(JsonTokenType.ValueSeparator)
82 82 .Cat(valueExpression)
83 83 .EClosure()
84 84 )
85 85 .Optional()
86 86 .Cat(MakeToken(JsonTokenType.EndArray))
87 87 .End();
88 88
89 89 var jsonExpression = valueExpression.End();
90 90
91 _jsonContext = CreateParserContext(jsonExpression, JSONElementContext.None);
92 _objectContext = CreateParserContext(objectExpression, JSONElementContext.Object);
93 _arrayContext = CreateParserContext(arrayExpression, JSONElementContext.Array);
91 _jsonContext = CreateParserContext(jsonExpression, JsonElementContext.None);
92 _objectContext = CreateParserContext(objectExpression, JsonElementContext.Object);
93 _arrayContext = CreateParserContext(arrayExpression, JsonElementContext.Array);
94 94 }
95 95
96 96 static Token MakeToken(params JsonTokenType[] input) {
97 97 return Token.New( input.Select(t => (int)t).ToArray() );
98 98 }
99 99
100 static ParserContext CreateParserContext(Token expr, JSONElementContext context) {
100 static ParserContext CreateParserContext(Token expr, JsonElementContext context) {
101 101
102 102 var dfa = new DFATable();
103 103 var builder = new RegularExpressionVisitor(dfa);
104 104 expr.Accept(builder);
105 105 builder.BuildDFA();
106 106
107 107 return new ParserContext(dfa.CreateTransitionTable(), dfa.InitialState, context);
108 108 }
109 109
110 110 #endregion
111 111
112 readonly JSONScanner m_scanner;
112 readonly JsonScanner m_scanner;
113 113 // json starts from the value context and may content even a single literal
114 114 MemberContext m_memberContext = MemberContext.MemberValue;
115 115
116 JSONElementType m_elementType;
116 JsonElementType m_elementType;
117 117 object m_elementValue;
118 118 string m_memberName = String.Empty;
119 119
120 120 Stack<ParserContext> m_stack = new Stack<ParserContext>();
121 121 ParserContext m_context = _jsonContext;
122 122
123 123 /// <summary>
124 124 /// Создает новый парсер на основе строки, содержащей JSON
125 125 /// </summary>
126 126 /// <param name="text"></param>
127 public JSONParser(string text) {
127 public JsonParser(string text) {
128 128 Safe.ArgumentNotEmpty(text, "text");
129 m_scanner = new JSONScanner(text);
129 m_scanner = JsonStringScanner.Create(text);
130 130 }
131 131
132 132 /// <summary>
133 133 /// Создает новый экземпляр парсера, на основе текстового потока.
134 134 /// </summary>
135 135 /// <param name="reader">Текстовый поток.</param>
136 public JSONParser(TextReader reader) {
136 public JsonParser(TextReader reader) {
137 137 Safe.ArgumentNotNull(reader, "reader");
138 m_scanner = new JSONScanner(reader);
138 m_scanner = JsonTextScanner.Create(reader);
139 139 }
140 140
141 141 public int Level {
142 142 get { return m_stack.Count; }
143 143 }
144 144
145 145 /// <summary>
146 146 /// Тип текущего элемента на котором стоит парсер.
147 147 /// </summary>
148 public JSONElementType ElementType {
148 public JsonElementType ElementType {
149 149 get { return m_elementType; }
150 150 }
151 151
152 152 /// <summary>
153 153 /// Имя элемента - имя свойства родительского контейнера. Для элементов массивов и корневого всегда
154 154 /// пустая строка.
155 155 /// </summary>
156 156 public string ElementName {
157 157 get { return m_memberName; }
158 158 }
159 159
160 160 /// <summary>
161 /// Значение элемента. Только для элементов типа <see cref="JSONElementType.Value"/>, для остальных <c>null</c>
161 /// Значение элемента. Только для элементов типа <see cref="JsonElementType.Value"/>, для остальных <c>null</c>
162 162 /// </summary>
163 163 public object ElementValue {
164 164 get { return m_elementValue; }
165 165 }
166 166
167 167 /// <summary>
168 168 /// Читает слеюудущий объект из потока
169 169 /// </summary>
170 170 /// <returns><c>true</c> - операция чтения прошла успешно, <c>false</c> - конец данных</returns>
171 171 public bool Read() {
172 172 object tokenValue;
173 173 JsonTokenType tokenType;
174 174
175 175 m_memberName = String.Empty;
176 176
177 177 while (m_scanner.ReadToken(out tokenValue, out tokenType)) {
178 178 if(!m_context.Move(tokenType))
179 179 UnexpectedToken(tokenValue, tokenType);
180 180
181 181 switch (tokenType) {
182 182 case JsonTokenType.BeginObject:
183 183 m_stack.Push(m_context);
184 184 m_context = _objectContext;
185 185
186 186 m_elementValue = null;
187 187 m_memberContext = MemberContext.MemberName;
188 m_elementType = JSONElementType.BeginObject;
188 m_elementType = JsonElementType.BeginObject;
189 189 return true;
190 190 case JsonTokenType.EndObject:
191 191 if (m_stack.Count == 0)
192 192 UnexpectedToken(tokenValue, tokenType);
193 193 m_context = m_stack.Pop();
194 194
195 195 m_elementValue = null;
196 m_elementType = JSONElementType.EndObject;
196 m_elementType = JsonElementType.EndObject;
197 197 return true;
198 198 case JsonTokenType.BeginArray:
199 199 m_stack.Push(m_context);
200 200 m_context = _arrayContext;
201 201
202 202 m_elementValue = null;
203 203 m_memberContext = MemberContext.MemberValue;
204 m_elementType = JSONElementType.BeginArray;
204 m_elementType = JsonElementType.BeginArray;
205 205 return true;
206 206 case JsonTokenType.EndArray:
207 207 if (m_stack.Count == 0)
208 208 UnexpectedToken(tokenValue, tokenType);
209 209 m_context = m_stack.Pop();
210 210
211 211 m_elementValue = null;
212 m_elementType = JSONElementType.EndArray;
212 m_elementType = JsonElementType.EndArray;
213 213 return true;
214 214 case JsonTokenType.String:
215 215 if (m_memberContext == MemberContext.MemberName) {
216 216 m_memberName = (string)tokenValue;
217 217 break;
218 218 }
219 m_elementType = JSONElementType.Value;
219 m_elementType = JsonElementType.Value;
220 220 m_elementValue = tokenValue;
221 221 return true;
222 222 case JsonTokenType.Number:
223 m_elementType = JSONElementType.Value;
223 m_elementType = JsonElementType.Value;
224 224 m_elementValue = tokenValue;
225 225 return true;
226 226 case JsonTokenType.Literal:
227 m_elementType = JSONElementType.Value;
227 m_elementType = JsonElementType.Value;
228 228 m_elementValue = ParseLiteral((string)tokenValue);
229 229 return true;
230 230 case JsonTokenType.NameSeparator:
231 231 m_memberContext = MemberContext.MemberValue;
232 232 break;
233 233 case JsonTokenType.ValueSeparator:
234 m_memberContext = m_context.ElementContext == JSONElementContext.Object ? MemberContext.MemberName : MemberContext.MemberValue;
234 m_memberContext = m_context.ElementContext == JsonElementContext.Object ? MemberContext.MemberName : MemberContext.MemberValue;
235 235 break;
236 236 default:
237 237 UnexpectedToken(tokenValue, tokenType);
238 238 break;
239 239 }
240 240 }
241 if (m_context.ElementContext != JSONElementContext.None)
241 if (m_context.ElementContext != JsonElementContext.None)
242 242 throw new ParserException("Unexpedted end of data");
243 243
244 244 EOF = true;
245 245
246 246 return false;
247 247 }
248 248
249 249 object ParseLiteral(string literal) {
250 250 switch (literal) {
251 251 case "null":
252 252 return null;
253 253 case "false":
254 254 return false;
255 255 case "true":
256 256 return true;
257 257 default:
258 258 UnexpectedToken(literal, JsonTokenType.Literal);
259 259 return null; // avoid compliler error
260 260 }
261 261 }
262 262
263 263 void UnexpectedToken(object value, JsonTokenType tokenType) {
264 264 throw new ParserException(String.Format("Unexpected token {0}: '{1}'", tokenType, value));
265 265 }
266 266
267 267
268 268 /// <summary>
269 269 /// Признак конца потока
270 270 /// </summary>
271 271 public bool EOF {
272 272 get;
273 273 private set;
274 274 }
275 275
276 276 protected override void Dispose(bool disposing) {
277 277 if (disposing)
278 278 m_scanner.Dispose();
279 279 }
280 280
281 281 /// <summary>
282 282 /// Переходит в конец текущего объекта.
283 283 /// </summary>
284 284 public void SeekElementEnd() {
285 285 var level = Level - 1;
286 286
287 287 Debug.Assert(level >= 0);
288 288
289 289 while (Level != level)
290 290 Read();
291 291 }
292 292 }
293 293
294 294 }
@@ -1,109 +1,134
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 namespace Implab.Formats.JSON {
8 namespace Implab.Formats.Json {
9 9 /// <summary>
10 10 /// Сканнер (лексер), разбивающий поток символов на токены JSON.
11 11 /// </summary>
12 public class JSONScanner : Disposable {
13 readonly StringBuilder m_builder = new StringBuilder();
14
15 readonly ScannerContext<JSONGrammar.TokenType> m_jsonContext = JSONGrammar.Instance.JsonExpression;
16 readonly ScannerContext<JSONGrammar.TokenType> m_stringContext = JSONGrammar.Instance.JsonStringExpression;
17
12 public abstract class JsonScanner : Disposable {
13 readonly InputScanner<JsonGrammar.TokenType> m_jsonContext = JsonGrammar.CreateJsonExpressionScanner();
14 readonly InputScanner<JsonGrammar.TokenType> m_stringContext = JsonGrammar.CreateStringExpressionScanner();
18 15
19 readonly TextScanner m_scanner;
16 readonly char[] m_unescapeBuf = new char[4];
17 readonly char[] m_buffer;
18 int m_length;
19 int m_pos;
20 readonly StringBuilder m_tokenBuilder = new StringBuilder();
20 21
21 /// <summary>
22 /// Создает новый экземпляр сканнера
23 /// </summary>
24 public JSONScanner(string text) {
25 Safe.ArgumentNotEmpty(text, "text");
26
27 m_scanner = new StringScanner(text);
22 protected JsonScanner(char[] buffer, int pos, int length) {
23 m_buffer = buffer;
24 m_pos = pos;
25 m_length = length;
28 26 }
29 27
30 public JSONScanner(TextReader reader, int bufferMax, int chunkSize) {
31 Safe.ArgumentNotNull(reader, "reader");
28 bool Read(InputScanner<JsonGrammar.TokenType> scanner, out JsonGrammar.TokenType tokenType) {
29 scanner.Reset();
32 30
33 m_scanner = new ReaderScanner(reader, bufferMax, chunkSize);
31 if (m_pos == m_length) {
32 m_pos = 0;
33 m_length = Read(m_buffer, 0, m_buffer.Length);
34 if (m_length == 0) {
35 tokenType = JsonGrammar.TokenType.None;
36 return false; // EOF
37 }
38 }
39
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 m_pos = 0;
43 m_length = Read(m_buffer, 0, m_buffer.Length);
44 }
45 var scannerPos = scanner.Position;
46 if (scannerPos != m_pos) {
47 m_tokenBuilder.Append(m_buffer, m_pos, scannerPos - m_pos);
48 m_pos = scannerPos;
49 }
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 tokenType = scanner.Tag;
61 return true;
34 62 }
35 63
36 public JSONScanner(TextReader reader) : this(reader, 1024*1024, 1024){
37 }
64 protected abstract int Read(char[] buffer, int offset, int size);
65
38 66
39 67 /// <summary>
40 68 /// Читает следующий лексический элемент из входных данных.
41 69 /// </summary>
42 70 /// <param name="tokenValue">Возвращает значение прочитанного токена.</param>
43 71 /// <param name="tokenType">Возвращает тип прочитанного токена.</param>
44 72 /// <returns><c>true</c> - чтение произведено успешно. <c>false</c> - достигнут конец входных данных</returns>
45 73 /// <remarks>В случе если токен не распознается, возникает исключение. Значения токенов обрабатываются, т.е.
46 74 /// в строках обрабатываются экранированные символы, числа становтся типа double.</remarks>
47 75 public bool ReadToken(out object tokenValue, out JsonTokenType tokenType) {
48 JSONGrammar.TokenType[] tag;
49 while (m_jsonContext.Execute(m_scanner, out tag)) {
50 switch (tag[0]) {
51 case JSONGrammar.TokenType.StringBound:
76 JsonGrammar.TokenType tag;
77 m_tokenBuilder.Clear();
78 while (Read(m_jsonContext, out tag)) {
79 switch (tag) {
80 case JsonGrammar.TokenType.StringBound:
52 81 tokenValue = ReadString();
53 82 tokenType = JsonTokenType.String;
54 83 break;
55 case JSONGrammar.TokenType.Number:
56 tokenValue = Double.Parse(m_scanner.GetTokenValue(), CultureInfo.InvariantCulture);
84 case JsonGrammar.TokenType.Number:
85 tokenValue = Double.Parse(m_tokenBuilder.ToString(), CultureInfo.InvariantCulture);
57 86 tokenType = JsonTokenType.Number;
58 87 break;
59 case JSONGrammar.TokenType.Whitespace:
88 case JsonGrammar.TokenType.Literal:
89 tokenType = JsonTokenType.Literal;
90 tokenValue = m_tokenBuilder.ToString();
91 break;
92 case JsonGrammar.TokenType.Whitespace:
93 m_tokenBuilder.Clear();
60 94 continue;
61 95 default:
62 tokenType = (JsonTokenType)tag[0];
63 tokenValue = m_scanner.GetTokenValue();
96 tokenType = (JsonTokenType)tag;
97 tokenValue = null;
64 98 break;
65 99 }
66 100 return true;
67 101 }
68 102 tokenValue = null;
69 103 tokenType = JsonTokenType.None;
70 104 return false;
71 105 }
72 106
73 107 string ReadString() {
74 int pos = 0;
75 var buf = new char[6]; // the buffer for unescaping chars
76
77 JSONGrammar.TokenType[] tag;
78 m_builder.Clear();
108 JsonGrammar.TokenType tag;
109 m_tokenBuilder.Clear();
79 110
80 while (m_stringContext.Execute(m_scanner, out tag)) {
81 switch (tag[0]) {
82 case JSONGrammar.TokenType.StringBound:
83 return m_builder.ToString();
84 case JSONGrammar.TokenType.UnescapedChar:
85 m_scanner.CopyTokenTo(m_builder);
111 while (Read(m_stringContext, out tag)) {
112 switch (tag) {
113 case JsonGrammar.TokenType.StringBound:
114 m_tokenBuilder.Length--;
115 return m_tokenBuilder.ToString();
116 case JsonGrammar.TokenType.UnescapedChar:
86 117 break;
87 case JSONGrammar.TokenType.EscapedUnicode: // \xXXXX - unicode escape sequence
88 m_scanner.CopyTokenTo(buf, 0);
89 m_builder.Append(StringTranslator.TranslateHexUnicode(buf, 2));
90 pos++;
118 case JsonGrammar.TokenType.EscapedUnicode: // \xXXXX - unicode escape sequence
119 m_tokenBuilder.CopyTo(m_tokenBuilder.Length - 4, m_unescapeBuf, 0, 4);
120 m_tokenBuilder.Length -= 6;
121 m_tokenBuilder.Append(StringTranslator.TranslateHexUnicode(m_unescapeBuf, 0));
91 122 break;
92 case JSONGrammar.TokenType.EscapedChar: // \t - escape sequence
93 m_scanner.CopyTokenTo(buf, 0);
94 m_builder.Append(StringTranslator.TranslateEscapedChar(buf[1]));
123 case JsonGrammar.TokenType.EscapedChar: // \t - escape sequence
124 var ch = m_tokenBuilder[m_tokenBuilder.Length-1];
125 m_tokenBuilder.Length -= 2;
126 m_tokenBuilder.Append(StringTranslator.TranslateEscapedChar(ch));
95 127 break;
96 128 }
97
98 129 }
99 130
100 131 throw new ParserException("Unexpected end of data");
101 132 }
102
103 protected override void Dispose(bool disposing) {
104 if (disposing)
105 m_scanner.Dispose();
106 base.Dispose(disposing);
107 }
108 133 }
109 134 }
@@ -1,44 +1,44
1 namespace Implab.Formats.JSON {
1 namespace Implab.Formats.Json {
2 2 /// <summary>
3 /// Тип токенов, возвращаемых <see cref="JSONScanner"/>.
3 /// Тип токенов, возвращаемых <see cref="JsonScanner"/>.
4 4 /// </summary>
5 5 public enum JsonTokenType : int {
6 6 None = 0,
7 7 /// <summary>
8 8 /// Начало объекта
9 9 /// </summary>
10 10 BeginObject,
11 11 /// <summary>
12 12 /// Конец объекта
13 13 /// </summary>
14 14 EndObject,
15 15 /// <summary>
16 16 /// Начало массива
17 17 /// </summary>
18 18 BeginArray,
19 19 /// <summary>
20 20 /// Конец массива
21 21 /// </summary>
22 22 EndArray,
23 23 /// <summary>
24 24 /// Строка
25 25 /// </summary>
26 26 String,
27 27 /// <summary>
28 28 /// Число
29 29 /// </summary>
30 30 Number,
31 31 /// <summary>
32 32 /// Литерал
33 33 /// </summary>
34 34 Literal,
35 35 /// <summary>
36 36 /// Разделитель имени <c>:</c>
37 37 /// </summary>
38 38 NameSeparator,
39 39 /// <summary>
40 40 /// Разделитель имени <c>,</c>
41 41 /// </summary>
42 42 ValueSeparator
43 43 }
44 44 }
@@ -1,319 +1,319
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.IO;
4 4 using System.Globalization;
5 5 using System.Diagnostics;
6 6
7 namespace Implab.Formats.JSON {
8 public class JSONWriter {
7 namespace Implab.Formats.Json {
8 public class JsonWriter {
9 9 struct Context {
10 10 public bool needComma;
11 public JSONElementContext element;
11 public JsonElementContext element;
12 12 }
13 13 Stack<Context> m_contextStack = new Stack<Context>();
14 14 Context m_context;
15 15
16 16 const int BUFFER_SIZE = 64;
17 17
18 18 TextWriter m_writer;
19 19 readonly bool m_indent = true;
20 20 readonly int m_indentSize = 4;
21 21 readonly char[] m_buffer = new char[BUFFER_SIZE];
22 22 int m_bufferPos;
23 23
24 24 static readonly char [] _hex = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
25 25 static readonly char [] _escapeBKS,
26 26 _escapeFWD,
27 27 _escapeCR,
28 28 _escapeNL,
29 29 _escapeTAB,
30 30 _escapeBSLASH,
31 31 _escapeQ;
32 32
33 static JSONWriter() {
33 static JsonWriter() {
34 34 _escapeBKS = "\\b".ToCharArray();
35 35 _escapeFWD = "\\f".ToCharArray();
36 36 _escapeCR = "\\r".ToCharArray();
37 37 _escapeNL = "\\n".ToCharArray();
38 38 _escapeTAB = "\\t".ToCharArray();
39 39 _escapeBSLASH = "\\\\".ToCharArray();
40 40 _escapeQ = "\\\"".ToCharArray();
41 41 }
42 42
43 public JSONWriter(TextWriter writer) {
43 public JsonWriter(TextWriter writer) {
44 44 Safe.ArgumentNotNull(writer, "writer");
45 45 m_writer = writer;
46 46 }
47 47
48 public JSONWriter(TextWriter writer, bool indent) {
48 public JsonWriter(TextWriter writer, bool indent) {
49 49 Safe.ArgumentNotNull(writer, "writer");
50 50
51 51 m_writer = writer;
52 52 m_indent = indent;
53 53 }
54 54
55 55 void WriteIndent() {
56 56 if (m_indent) {
57 57 var indent = new char[m_contextStack.Count * m_indentSize + 1];
58 58 indent[0] = '\n';
59 59 for (int i = 1; i < indent.Length; i++)
60 60 indent[i] = ' ';
61 61 m_writer.Write(new String(indent));
62 62 } else {
63 63 m_writer.Write(' ');
64 64 }
65 65 }
66 66
67 67 void WriteMemberName(string name) {
68 68 Safe.ArgumentNotEmpty(name, "name");
69 if (m_context.element != JSONElementContext.Object)
69 if (m_context.element != JsonElementContext.Object)
70 70 OperationNotApplicable("WriteMember");
71 71 if (m_context.needComma)
72 72 m_writer.Write(",");
73 73
74 74 WriteIndent();
75 75 m_context.needComma = true;
76 76 Write(name);
77 77 m_writer.Write(" : ");
78 78 }
79 79
80 80 public void WriteValue(string name, string value) {
81 81 WriteMemberName(name);
82 82 Write(value);
83 83 }
84 84
85 85 public void WriteValue(string name, bool value) {
86 86 WriteMemberName(name);
87 87 Write(value);
88 88 }
89 89
90 90 public void WriteValue(string name, double value) {
91 91 WriteMemberName(name);
92 92 Write(value);
93 93 }
94 94
95 95 public void WriteValue(string value) {
96 if (m_context.element == JSONElementContext.Array) {
96 if (m_context.element == JsonElementContext.Array) {
97 97
98 98 if (m_context.needComma)
99 99 m_writer.Write(",");
100 100 WriteIndent();
101 101 m_context.needComma = true;
102 102
103 103 Write(value);
104 } else if (m_context.element == JSONElementContext.None) {
104 } else if (m_context.element == JsonElementContext.None) {
105 105 Write(value);
106 m_context.element = JSONElementContext.Closed;
106 m_context.element = JsonElementContext.Closed;
107 107 } else {
108 108 OperationNotApplicable("WriteValue");
109 109 }
110 110 }
111 111
112 112 public void WriteValue(bool value) {
113 if (m_context.element == JSONElementContext.Array) {
113 if (m_context.element == JsonElementContext.Array) {
114 114
115 115 if (m_context.needComma)
116 116 m_writer.Write(",");
117 117 WriteIndent();
118 118 m_context.needComma = true;
119 119
120 120 Write(value);
121 } else if (m_context.element == JSONElementContext.None) {
121 } else if (m_context.element == JsonElementContext.None) {
122 122 Write(value);
123 m_context.element = JSONElementContext.Closed;
123 m_context.element = JsonElementContext.Closed;
124 124 } else {
125 125 OperationNotApplicable("WriteValue");
126 126 }
127 127 }
128 128
129 129 public void WriteValue(double value) {
130 if (m_context.element == JSONElementContext.Array) {
130 if (m_context.element == JsonElementContext.Array) {
131 131
132 132 if (m_context.needComma)
133 133 m_writer.Write(",");
134 134 WriteIndent();
135 135 m_context.needComma = true;
136 136
137 137 Write(value);
138 } else if (m_context.element == JSONElementContext.None) {
138 } else if (m_context.element == JsonElementContext.None) {
139 139 Write(value);
140 m_context.element = JSONElementContext.Closed;
140 m_context.element = JsonElementContext.Closed;
141 141 } else {
142 142 OperationNotApplicable("WriteValue");
143 143 }
144 144 }
145 145
146 146 public void BeginObject() {
147 if (m_context.element != JSONElementContext.None && m_context.element != JSONElementContext.Array)
147 if (m_context.element != JsonElementContext.None && m_context.element != JsonElementContext.Array)
148 148 OperationNotApplicable("BeginObject");
149 149 if (m_context.needComma)
150 150 m_writer.Write(",");
151 151
152 152 WriteIndent();
153 153
154 154 m_context.needComma = true;
155 155
156 156 m_contextStack.Push(m_context);
157 157
158 m_context = new Context { element = JSONElementContext.Object, needComma = false };
158 m_context = new Context { element = JsonElementContext.Object, needComma = false };
159 159 m_writer.Write("{");
160 160 }
161 161
162 162 public void BeginObject(string name) {
163 163 WriteMemberName(name);
164 164
165 165 m_contextStack.Push(m_context);
166 166
167 m_context = new Context { element = JSONElementContext.Object, needComma = false };
167 m_context = new Context { element = JsonElementContext.Object, needComma = false };
168 168 m_writer.Write("{");
169 169 }
170 170
171 171 public void EndObject() {
172 if (m_context.element != JSONElementContext.Object)
172 if (m_context.element != JsonElementContext.Object)
173 173 OperationNotApplicable("EndObject");
174 174
175 175 m_context = m_contextStack.Pop();
176 176 if (m_contextStack.Count == 0)
177 m_context.element = JSONElementContext.Closed;
177 m_context.element = JsonElementContext.Closed;
178 178 WriteIndent();
179 179 m_writer.Write("}");
180 180 }
181 181
182 182 public void BeginArray() {
183 if (m_context.element != JSONElementContext.None && m_context.element != JSONElementContext.Array)
183 if (m_context.element != JsonElementContext.None && m_context.element != JsonElementContext.Array)
184 184 throw new InvalidOperationException();
185 185 if (m_context.needComma) {
186 186 m_writer.Write(",");
187 187
188 188 }
189 189 m_context.needComma = true;
190 190
191 191 WriteIndent();
192 192 m_contextStack.Push(m_context);
193 m_context = new Context { element = JSONElementContext.Array, needComma = false };
193 m_context = new Context { element = JsonElementContext.Array, needComma = false };
194 194 m_writer.Write("[");
195 195 }
196 196
197 197 public void BeginArray(string name) {
198 198 WriteMemberName(name);
199 199
200 200 m_contextStack.Push(m_context);
201 201
202 m_context = new Context { element = JSONElementContext.Array, needComma = false };
202 m_context = new Context { element = JsonElementContext.Array, needComma = false };
203 203 m_writer.Write("[");
204 204 }
205 205
206 206 public void EndArray() {
207 if (m_context.element != JSONElementContext.Array)
207 if (m_context.element != JsonElementContext.Array)
208 208 OperationNotApplicable("EndArray");
209 209
210 210 m_context = m_contextStack.Pop();
211 211 if (m_contextStack.Count == 0)
212 m_context.element = JSONElementContext.Closed;
212 m_context.element = JsonElementContext.Closed;
213 213 WriteIndent();
214 214 m_writer.Write("]");
215 215 }
216 216
217 217 void Write(bool value) {
218 218 m_writer.Write(value ? "true" : "false");
219 219 }
220 220
221 221 void FlushBuffer() {
222 222 if (m_bufferPos > 0) {
223 223 m_writer.Write(m_buffer, 0, m_bufferPos);
224 224 m_bufferPos = 0;
225 225 }
226 226 }
227 227
228 228 void Write(string value) {
229 229 if (value == null) {
230 230 m_writer.Write("null");
231 231 return;
232 232 }
233 233
234 234 Debug.Assert(m_bufferPos == 0);
235 235
236 236 var chars = value.ToCharArray();
237 237 m_buffer[m_bufferPos++] = '"';
238 238
239 239 // Analysis disable once ForCanBeConvertedToForeach
240 240 for (int i = 0; i < chars.Length; i++) {
241 241 var ch = chars[i];
242 242
243 243 char[] escapeSeq;
244 244
245 245 switch (ch) {
246 246 case '\b':
247 247 escapeSeq = _escapeBKS;
248 248 break;
249 249 case '\f':
250 250 escapeSeq = _escapeFWD;
251 251 break;
252 252 case '\r':
253 253 escapeSeq = _escapeCR;
254 254 break;
255 255 case '\n':
256 256 escapeSeq = _escapeNL;
257 257 break;
258 258 case '\t':
259 259 escapeSeq = _escapeTAB;
260 260 break;
261 261 case '\\':
262 262 escapeSeq = _escapeBSLASH;
263 263 break;
264 264 case '"':
265 265 escapeSeq = _escapeQ;
266 266 break;
267 267 default:
268 268 if (ch < 0x20) {
269 269 if (m_bufferPos + 6 > BUFFER_SIZE)
270 270 FlushBuffer();
271 271
272 272 m_buffer[m_bufferPos++] = '\\';
273 273 m_buffer[m_bufferPos++] = 'u';
274 274 m_buffer[m_bufferPos++] = '0';
275 275 m_buffer[m_bufferPos++] = '0';
276 276 m_buffer[m_bufferPos++] = _hex[ch >> 4 & 0xf];
277 277 m_buffer[m_bufferPos++] = _hex[ch & 0xf];
278 278
279 279 } else {
280 280 if (m_bufferPos >= BUFFER_SIZE)
281 281 FlushBuffer();
282 282 m_buffer[m_bufferPos++] = ch;
283 283 }
284 284 continue;
285 285 }
286 286
287 287 if (m_bufferPos + escapeSeq.Length > BUFFER_SIZE)
288 288 FlushBuffer();
289 289
290 290 Array.Copy(escapeSeq, 0, m_buffer, m_bufferPos, escapeSeq.Length);
291 291 m_bufferPos += escapeSeq.Length;
292 292
293 293 }
294 294
295 295 if (m_bufferPos >= BUFFER_SIZE)
296 296 FlushBuffer();
297 297
298 298 m_buffer[m_bufferPos++] = '"';
299 299
300 300 FlushBuffer();
301 301 }
302 302
303 303 void Write(double value) {
304 304 if (double.IsNaN(value))
305 305 Write("NaN");
306 306 else if (double.IsNegativeInfinity(value))
307 307 Write("-Infinity");
308 308 else if (double.IsPositiveInfinity(value))
309 309 Write("Infinity");
310 310 else
311 311 m_writer.Write(value.ToString(CultureInfo.InvariantCulture));
312 312 }
313 313
314 314 void OperationNotApplicable(string opName) {
315 315 throw new InvalidOperationException(String.Format("The operation '{0}' isn't applicable in the context of '{1}'", opName, m_context.element ));
316 316 }
317 317
318 318 }
319 319 }
@@ -1,52 +1,52
1 1 using Implab;
2 2 using Implab.Formats;
3 3 using System;
4 4 using System.Collections.Generic;
5 5 using System.Diagnostics;
6 6 using System.Linq;
7 7 using System.Text;
8 8 using System.Threading.Tasks;
9 9
10 namespace Implab.Formats.JSON {
10 namespace Implab.Formats.Json {
11 11 /// <summary>
12 12 /// Класс для преобразования экранированной строки JSON
13 13 /// </summary>
14 14 static class StringTranslator {
15 15 static readonly char[] _escMap;
16 16 static readonly int[] _hexMap;
17 17
18 18 static StringTranslator() {
19 19 var chars = new char[] { 'b', 'f', 't', 'r', 'n', '\\', '/', '"' };
20 20 var vals = new char[] { '\b', '\f', '\t', '\r', '\n', '\\', '/', '"' };
21 21
22 22 _escMap = new char[chars.Max() + 1];
23 23
24 24 for (int i = 0; i < chars.Length; i++)
25 25 _escMap[chars[i]] = vals[i];
26 26
27 27 var hexs = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F' };
28 28 var ints = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 10, 11, 12, 13, 14, 15 };
29 29
30 30 _hexMap = new int[hexs.Max() + 1];
31 31
32 32 for (int i = 0; i < hexs.Length; i++)
33 33 _hexMap[hexs[i]] = ints[i];
34 34
35 35 }
36 36
37 37 internal static char TranslateEscapedChar(char symbol) {
38 38 return _escMap[symbol];
39 39 }
40 40
41 41 internal static char TranslateHexUnicode(char[] symbols, int offset) {
42 42 Debug.Assert(symbols != null);
43 43 Debug.Assert(symbols.Length - offset >= 4);
44 44
45 45 int value = (_hexMap[symbols[offset]] << 12)
46 46 | (_hexMap[symbols[offset + 1]] << 8)
47 47 | (_hexMap[symbols[offset + 2]] << 4)
48 48 | (_hexMap[symbols[offset + 3]]);
49 49 return (char)value;
50 50 }
51 51 }
52 52 }
@@ -1,281 +1,279
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 73 </ItemGroup>
74 74 <ItemGroup>
75 75 <Compile Include="Components\StateChangeEventArgs.cs" />
76 76 <Compile Include="CustomEqualityComparer.cs" />
77 77 <Compile Include="Diagnostics\ConsoleTraceListener.cs" />
78 78 <Compile Include="Diagnostics\LogChannel.cs" />
79 79 <Compile Include="Diagnostics\LogicalOperation.cs" />
80 80 <Compile Include="Diagnostics\TextFileListener.cs" />
81 81 <Compile Include="Diagnostics\Trace.cs" />
82 82 <Compile Include="Diagnostics\TraceLog.cs" />
83 83 <Compile Include="Diagnostics\TraceEvent.cs" />
84 84 <Compile Include="Diagnostics\TraceEventType.cs" />
85 85 <Compile Include="Diagnostics\TraceSourceAttribute.cs" />
86 <Compile Include="Formats\CharMap.cs" />
87 <Compile Include="Formats\InputScanner.cs" />
88 <Compile Include="Formats\Json\JsonStringScanner.cs" />
89 <Compile Include="Formats\Json\JsonTextScanner.cs" />
86 90 <Compile Include="ICancellable.cs" />
87 91 <Compile Include="IProgressHandler.cs" />
88 92 <Compile Include="IProgressNotifier.cs" />
89 93 <Compile Include="IPromiseT.cs" />
90 94 <Compile Include="IPromise.cs" />
91 95 <Compile Include="IServiceLocator.cs" />
92 96 <Compile Include="ITaskController.cs" />
93 97 <Compile Include="Parallels\DispatchPool.cs" />
94 98 <Compile Include="Parallels\ArrayTraits.cs" />
95 99 <Compile Include="Parallels\MTQueue.cs" />
96 100 <Compile Include="Parallels\WorkerPool.cs" />
97 101 <Compile Include="ProgressInitEventArgs.cs" />
98 102 <Compile Include="Properties\AssemblyInfo.cs" />
99 103 <Compile Include="Parallels\AsyncPool.cs" />
100 104 <Compile Include="Safe.cs" />
101 105 <Compile Include="SyncContextPromise.cs" />
102 106 <Compile Include="ValueEventArgs.cs" />
103 107 <Compile Include="PromiseExtensions.cs" />
104 108 <Compile Include="SyncContextPromiseT.cs" />
105 109 <Compile Include="Diagnostics\OperationContext.cs" />
106 110 <Compile Include="Diagnostics\TraceContext.cs" />
107 111 <Compile Include="Diagnostics\LogEventArgs.cs" />
108 112 <Compile Include="Diagnostics\LogEventArgsT.cs" />
109 113 <Compile Include="Diagnostics\Extensions.cs" />
110 114 <Compile Include="PromiseEventType.cs" />
111 115 <Compile Include="Parallels\AsyncQueue.cs" />
112 116 <Compile Include="PromiseT.cs" />
113 117 <Compile Include="IDeferred.cs" />
114 118 <Compile Include="IDeferredT.cs" />
115 119 <Compile Include="Promise.cs" />
116 120 <Compile Include="PromiseTransientException.cs" />
117 121 <Compile Include="Parallels\Signal.cs" />
118 122 <Compile Include="Parallels\SharedLock.cs" />
119 123 <Compile Include="Diagnostics\ILogWriter.cs" />
120 124 <Compile Include="Diagnostics\ListenerBase.cs" />
121 125 <Compile Include="Parallels\BlockingQueue.cs" />
122 126 <Compile Include="AbstractEvent.cs" />
123 127 <Compile Include="AbstractPromise.cs" />
124 128 <Compile Include="AbstractPromiseT.cs" />
125 129 <Compile Include="FuncTask.cs" />
126 130 <Compile Include="FuncTaskBase.cs" />
127 131 <Compile Include="FuncTaskT.cs" />
128 132 <Compile Include="ActionChainTaskBase.cs" />
129 133 <Compile Include="ActionChainTask.cs" />
130 134 <Compile Include="ActionChainTaskT.cs" />
131 135 <Compile Include="FuncChainTaskBase.cs" />
132 136 <Compile Include="FuncChainTask.cs" />
133 137 <Compile Include="FuncChainTaskT.cs" />
134 138 <Compile Include="ActionTaskBase.cs" />
135 139 <Compile Include="ActionTask.cs" />
136 140 <Compile Include="ActionTaskT.cs" />
137 141 <Compile Include="ICancellationToken.cs" />
138 142 <Compile Include="SuccessPromise.cs" />
139 143 <Compile Include="SuccessPromiseT.cs" />
140 144 <Compile Include="PromiseAwaiterT.cs" />
141 145 <Compile Include="PromiseAwaiter.cs" />
142 146 <Compile Include="Components\ComponentContainer.cs" />
143 147 <Compile Include="Components\Disposable.cs" />
144 148 <Compile Include="Components\DisposablePool.cs" />
145 149 <Compile Include="Components\ObjectPool.cs" />
146 150 <Compile Include="Components\ServiceLocator.cs" />
147 151 <Compile Include="Components\IInitializable.cs" />
148 152 <Compile Include="TaskController.cs" />
149 153 <Compile Include="Components\App.cs" />
150 154 <Compile Include="Components\IRunnable.cs" />
151 155 <Compile Include="Components\ExecutionState.cs" />
152 156 <Compile Include="Components\RunnableComponent.cs" />
153 157 <Compile Include="Components\IFactory.cs" />
154 158 <Compile Include="Automaton\IAlphabet.cs" />
155 159 <Compile Include="Automaton\ParserException.cs" />
156 160 <Compile Include="Automaton\IndexedAlphabetBase.cs" />
157 161 <Compile Include="Automaton\IAlphabetBuilder.cs" />
158 162 <Compile Include="Automaton\RegularExpressions\AltToken.cs" />
159 163 <Compile Include="Automaton\RegularExpressions\BinaryToken.cs" />
160 164 <Compile Include="Automaton\RegularExpressions\CatToken.cs" />
161 165 <Compile Include="Automaton\RegularExpressions\StarToken.cs" />
162 166 <Compile Include="Automaton\RegularExpressions\SymbolToken.cs" />
163 167 <Compile Include="Automaton\RegularExpressions\EmptyToken.cs" />
164 168 <Compile Include="Automaton\RegularExpressions\Token.cs" />
165 169 <Compile Include="Automaton\RegularExpressions\IVisitor.cs" />
166 170 <Compile Include="Automaton\AutomatonTransition.cs" />
167 <Compile Include="Formats\JSON\JSONElementContext.cs" />
168 <Compile Include="Formats\JSON\JSONElementType.cs" />
169 <Compile Include="Formats\JSON\JSONGrammar.cs" />
170 <Compile Include="Formats\JSON\JSONParser.cs" />
171 <Compile Include="Formats\JSON\JSONScanner.cs" />
172 <Compile Include="Formats\JSON\JsonTokenType.cs" />
173 <Compile Include="Formats\JSON\JSONWriter.cs" />
174 <Compile Include="Formats\JSON\JSONXmlReader.cs" />
175 <Compile Include="Formats\JSON\JSONXmlReaderOptions.cs" />
176 <Compile Include="Formats\JSON\StringTranslator.cs" />
171 <Compile Include="Formats\Json\JsonElementContext.cs" />
172 <Compile Include="Formats\Json\JsonElementType.cs" />
173 <Compile Include="Formats\Json\JsonGrammar.cs" />
174 <Compile Include="Formats\Json\JsonParser.cs" />
175 <Compile Include="Formats\Json\JsonScanner.cs" />
176 <Compile Include="Formats\Json\JsonTokenType.cs" />
177 <Compile Include="Formats\Json\JsonWriter.cs" />
178 <Compile Include="Formats\Json\StringTranslator.cs" />
177 179 <Compile Include="Automaton\MapAlphabet.cs" />
178 180 <Compile Include="Formats\CharAlphabet.cs" />
179 181 <Compile Include="Formats\ByteAlphabet.cs" />
180 182 <Compile Include="Automaton\IDFATable.cs" />
181 183 <Compile Include="Automaton\IDFATableBuilder.cs" />
182 184 <Compile Include="Automaton\DFATable.cs" />
183 185 <Compile Include="Automaton\RegularExpressions\RegularExpressionVisitor.cs" />
184 186 <Compile Include="Automaton\RegularExpressions\ITaggedDFABuilder.cs" />
185 <Compile Include="Formats\TextScanner.cs" />
186 <Compile Include="Formats\StringScanner.cs" />
187 <Compile Include="Formats\ReaderScanner.cs" />
188 <Compile Include="Formats\ScannerContext.cs" />
189 187 <Compile Include="Formats\Grammar.cs" />
190 188 <Compile Include="Automaton\RegularExpressions\EndTokenT.cs" />
191 189 <Compile Include="Automaton\RegularExpressions\EndToken.cs" />
192 190 <Compile Include="Automaton\RegularExpressions\RegularExpressionVisitorT.cs" />
193 191 <Compile Include="Automaton\AutomatonConst.cs" />
194 192 <Compile Include="Automaton\RegularExpressions\RegularDFA.cs" />
195 193 <Compile Include="Components\LazyAndWeak.cs" />
196 194 <Compile Include="AbstractTask.cs" />
197 195 <Compile Include="AbstractTaskT.cs" />
198 196 <Compile Include="FailedPromise.cs" />
199 197 <Compile Include="FailedPromiseT.cs" />
200 198 <Compile Include="Components\PollingComponent.cs" />
201 199 <Compile Include="Xml\JsonXmlReader.cs" />
202 200 <Compile Include="Xml\JsonXmlReaderOptions.cs" />
203 201 <Compile Include="Xml\JsonXmlReaderPosition.cs" />
204 202 <Compile Include="Xml\XmlSimpleAttribute.cs" />
205 203 <Compile Include="Xml\XmlNameContext.cs" />
206 204 </ItemGroup>
207 205 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
208 206 <ItemGroup />
209 207 <ProjectExtensions>
210 208 <MonoDevelop>
211 209 <Properties>
212 210 <Policies>
213 211 <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" />
214 212 <TextStylePolicy FileWidth="120" TabWidth="4" IndentWidth="4" RemoveTrailingWhitespace="True" NoTabsAfterNonTabs="False" TabsToSpaces="True" EolMarker="Unix" scope="text/x-csharp" />
215 213 <DotNetNamingPolicy DirectoryNamespaceAssociation="PrefixedHierarchical" ResourceNamePolicy="MSBuild" />
216 214 <TextStylePolicy FileWidth="120" TabWidth="4" TabsToSpaces="False" IndentWidth="4" RemoveTrailingWhitespace="True" NoTabsAfterNonTabs="False" EolMarker="Native" scope="application/xml" />
217 215 <XmlFormattingPolicy scope="application/xml">
218 216 <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" />
219 217 </XmlFormattingPolicy>
220 218 <TextStylePolicy FileWidth="120" TabWidth="4" TabsToSpaces="False" IndentWidth="4" RemoveTrailingWhitespace="True" NoTabsAfterNonTabs="False" EolMarker="Native" scope="text/plain" />
221 219 <NameConventionPolicy>
222 220 <Rules>
223 221 <NamingRule Name="Namespaces" AffectedEntity="Namespace" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
224 222 <NamingRule Name="Types" AffectedEntity="Class, Struct, Enum, Delegate" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
225 223 <NamingRule Name="Interfaces" AffectedEntity="Interface" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
226 224 <RequiredPrefixes>
227 225 <String>I</String>
228 226 </RequiredPrefixes>
229 227 </NamingRule>
230 228 <NamingRule Name="Attributes" AffectedEntity="CustomAttributes" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
231 229 <RequiredSuffixes>
232 230 <String>Attribute</String>
233 231 </RequiredSuffixes>
234 232 </NamingRule>
235 233 <NamingRule Name="Event Arguments" AffectedEntity="CustomEventArgs" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
236 234 <RequiredSuffixes>
237 235 <String>EventArgs</String>
238 236 </RequiredSuffixes>
239 237 </NamingRule>
240 238 <NamingRule Name="Exceptions" AffectedEntity="CustomExceptions" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
241 239 <RequiredSuffixes>
242 240 <String>Exception</String>
243 241 </RequiredSuffixes>
244 242 </NamingRule>
245 243 <NamingRule Name="Methods" AffectedEntity="Methods" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
246 244 <NamingRule Name="Static Readonly Fields" AffectedEntity="ReadonlyField" VisibilityMask="Internal, Protected, Public" NamingStyle="CamelCase" IncludeInstanceMembers="False" IncludeStaticEntities="True" />
247 245 <NamingRule Name="Fields (Non Private)" AffectedEntity="Field" VisibilityMask="Internal, Public" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
248 246 <NamingRule Name="ReadOnly Fields (Non Private)" AffectedEntity="ReadonlyField" VisibilityMask="Internal, Public" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False" />
249 247 <NamingRule Name="Fields (Private)" AffectedEntity="Field, ReadonlyField" VisibilityMask="Private, Protected" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False">
250 248 <RequiredPrefixes>
251 249 <String>m_</String>
252 250 </RequiredPrefixes>
253 251 </NamingRule>
254 252 <NamingRule Name="Static Fields (Private)" AffectedEntity="Field" VisibilityMask="Private" NamingStyle="CamelCase" IncludeInstanceMembers="False" IncludeStaticEntities="True">
255 253 <RequiredPrefixes>
256 254 <String>_</String>
257 255 </RequiredPrefixes>
258 256 </NamingRule>
259 257 <NamingRule Name="ReadOnly Fields (Private)" AffectedEntity="ReadonlyField" VisibilityMask="Private, Protected" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False">
260 258 <RequiredPrefixes>
261 259 <String>m_</String>
262 260 </RequiredPrefixes>
263 261 </NamingRule>
264 262 <NamingRule Name="Constant Fields" AffectedEntity="ConstantField" VisibilityMask="VisibilityMask" NamingStyle="AllUpper" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
265 263 <NamingRule Name="Properties" AffectedEntity="Property" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
266 264 <NamingRule Name="Events" AffectedEntity="Event" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
267 265 <NamingRule Name="Enum Members" AffectedEntity="EnumMember" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
268 266 <NamingRule Name="Parameters" AffectedEntity="Parameter, LocalVariable" VisibilityMask="VisibilityMask" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
269 267 <NamingRule Name="Type Parameters" AffectedEntity="TypeParameter" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
270 268 <RequiredPrefixes>
271 269 <String>T</String>
272 270 </RequiredPrefixes>
273 271 </NamingRule>
274 272 </Rules>
275 273 </NameConventionPolicy>
276 274 </Policies>
277 275 </Properties>
278 276 </MonoDevelop>
279 277 </ProjectExtensions>
280 278 <ItemGroup />
281 279 </Project> No newline at end of file
@@ -1,152 +1,166
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.Linq;
4 4 using System.Text;
5 5 using System.Text.RegularExpressions;
6 6 using System.Diagnostics;
7 7 using System.Collections;
8 using System.Runtime.CompilerServices;
8 9
9 10 #if NET_4_5
10 11 using System.Threading.Tasks;
11 12 #endif
12 13
13 14 namespace Implab
14 15 {
15 16 public static class Safe
16 17 {
18 [MethodImpl(MethodImplOptions.AggressiveInlining)]
17 19 public static void ArgumentAssert(bool condition, string paramName) {
18 20 if (!condition)
19 21 throw new ArgumentException("The parameter is invalid", paramName);
20 22 }
21 23
24 [MethodImpl(MethodImplOptions.AggressiveInlining)]
22 25 public static void ArgumentMatch(string value, string paramName, Regex rx) {
23 26 if (rx == null)
24 27 throw new ArgumentNullException("rx");
25 28 if (!rx.IsMatch(value))
26 29 throw new ArgumentException(String.Format("The prameter value must match {0}", rx), paramName);
27 30 }
28 31
32 [MethodImpl(MethodImplOptions.AggressiveInlining)]
29 33 public static void ArgumentNotEmpty(string value, string paramName) {
30 34 if (String.IsNullOrEmpty(value))
31 35 throw new ArgumentException("The parameter can't be empty", paramName);
32 36 }
33 37
38 [MethodImpl(MethodImplOptions.AggressiveInlining)]
34 39 public static void ArgumentNotEmpty<T>(T[] value, string paramName) {
35 40 if (value == null || value.Length == 0)
36 41 throw new ArgumentException("The array must be not emty", paramName);
37 42 }
38 43
44 [MethodImpl(MethodImplOptions.AggressiveInlining)]
39 45 public static void ArgumentNotNull(object value, string paramName) {
40 46 if (value == null)
41 47 throw new ArgumentNullException(paramName);
42 48 }
43 49
50 [MethodImpl(MethodImplOptions.AggressiveInlining)]
51 internal static void ArgumentGreaterThan(int value, int min, string paramName) {
52 if (value < min)
53 throw new ArgumentOutOfRangeException(paramName);
54 }
55
56 [MethodImpl(MethodImplOptions.AggressiveInlining)]
44 57 public static void ArgumentInRange(int value, int min, int max, string paramName) {
45 58 if (value < min || value > max)
46 59 throw new ArgumentOutOfRangeException(paramName);
47 60 }
48 61
62 [MethodImpl(MethodImplOptions.AggressiveInlining)]
49 63 public static void ArgumentOfType(object value, Type type, string paramName) {
50 64 if (!type.IsInstanceOfType(value))
51 65 throw new ArgumentException(String.Format("The parameter must be of type {0}", type), paramName);
52 66 }
53 67
54 68 public static void Dispose(params IDisposable[] objects) {
55 69 foreach (var d in objects)
56 70 if (d != null)
57 71 d.Dispose();
58 72 }
59 73
60 74 public static void Dispose(params object[] objects) {
61 75 foreach (var obj in objects) {
62 76 var d = obj as IDisposable;
63 77 if (d != null)
64 78 d.Dispose();
65 79 }
66 80 }
67 81
68 82 public static void DisposeCollection(IEnumerable<IDisposable> objects) {
69 83 foreach (var d in objects)
70 84 Dispose(d);
71 85 }
72 86
73 87 public static void DisposeCollection(IEnumerable objects) {
74 88 foreach (var d in objects)
75 89 Dispose(d);
76 90 }
77 91
78 92 public static void Dispose(object obj) {
79 93 if (obj is IDisposable) {
80 94 Dispose((IDisposable)obj);
81 95 } else if (obj is IEnumerable) {
82 96 DisposeCollection((IEnumerable)obj);
83 97 }
84 98 }
85 99
86 100 [DebuggerStepThrough]
87 101 public static void DispatchEvent<T>(this EventHandler<T> handler, object sender, T args) {
88 102 if (handler != null)
89 103 handler(sender, args);
90 104 }
91 105
92 106 [DebuggerStepThrough]
93 107 public static void DispatchEvent(this EventHandler handler, object sender, EventArgs args) {
94 108 if (handler != null)
95 109 handler(sender, args);
96 110 }
97 111
98 112 [DebuggerStepThrough]
99 113 public static IPromise<T> Run<T>(Func<T> action) {
100 114 ArgumentNotNull(action, "action");
101 115
102 116 try {
103 117 return Promise<T>.FromResult(action());
104 118 } catch (Exception err) {
105 119 return Promise<T>.FromException(err);
106 120 }
107 121 }
108 122
109 123 [DebuggerStepThrough]
110 124 public static IPromise Run(Action action) {
111 125 ArgumentNotNull(action, "action");
112 126
113 127 try {
114 128 action();
115 129 return Promise.Success;
116 130 } catch (Exception err) {
117 131 return new FailedPromise(err);
118 132 }
119 133 }
120 134
121 135 [DebuggerStepThrough]
122 136 public static IPromise Run(Func<IPromise> action) {
123 137 ArgumentNotNull(action, "action");
124 138
125 139 try {
126 140 return action() ?? new FailedPromise(new Exception("The action returned null"));
127 141 } catch (Exception err) {
128 142 return new FailedPromise(err);
129 143 }
130 144 }
131 145
132 146 public static void NoWait(IPromise promise) {
133 147 }
134 148
135 149 [DebuggerStepThrough]
136 150 public static IPromise<T> Run<T>(Func<IPromise<T>> action) {
137 151 ArgumentNotNull(action, "action");
138 152
139 153 try {
140 154 return action() ?? Promise<T>.FromException(new Exception("The action returned null"));
141 155 } catch (Exception err) {
142 156 return Promise<T>.FromException(err);
143 157 }
144 158 }
145 159
146 160 #if NET_4_5
147 161 public static void NoWait(Task t) {
148 162 }
149 163 #endif
150 164
151 165 }
152 166 }
@@ -1,629 +1,626
1 using Implab.Formats.JSON;
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 using System.Text;
7 using System.Threading.Tasks;
8 6 using System.Xml;
9 7
10 8 namespace Implab.Xml {
11 9 public class JsonXmlReader : XmlReader {
12 10 struct JsonContext {
13 11 public string localName;
14 12 public bool skip;
15 13 }
16 14
17 JSONParser m_parser;
15 JsonParser m_parser;
18 16 JsonXmlReaderOptions m_options;
19 17 JsonXmlReaderPosition m_position = JsonXmlReaderPosition.Initial;
20 18 XmlNameTable m_nameTable;
21 19
22 20 readonly string m_jsonRootName;
23 21 readonly string m_jsonNamespace;
24 22 readonly string m_jsonPrefix;
25 23 readonly bool m_jsonFlattenArrays;
26 24 readonly string m_jsonArrayItemName;
27 25
28 26 string m_jsonLocalName;
29 27 string m_jsonValueName;
30 28 bool m_jsonSkip; // indicates wheather to generate closing tag for objects or arrays
31 29
32 30 readonly Stack<JsonContext> m_jsonNameStack = new Stack<JsonContext>();
33 31
34 32 XmlQualifiedName m_elementQName;
35 33 string m_elementPrefix;
36 34 int m_elementDepth;
37 35 bool m_elementIsEmpty;
38 36
39 37 XmlQualifiedName m_qName;
40 38 string m_prefix;
41 39 int m_xmlDepth;
42 40
43 41 XmlSimpleAttribute[] m_attributes;
44 42 object m_value;
45 43 bool m_isEmpty;
46 44
47 45 XmlNodeType m_nodeType = XmlNodeType.None;
48 46
49 47 bool m_isAttribute; // indicates that we are reading attribute nodes
50 48 int m_currentAttribute;
51 49 bool m_currentAttributeRead;
52 50
53 51
54 52 XmlNameContext m_context;
55 int m_nextPrefix = 1;
56 53
57 54 readonly string m_xmlnsPrefix;
58 55 readonly string m_xmlnsNamespace;
59 56 readonly string m_xsiPrefix;
60 57 readonly string m_xsiNamespace;
61 58
62 59
63 public JsonXmlReader(JSONParser parser, JsonXmlReaderOptions options) {
60 public JsonXmlReader(JsonParser parser, JsonXmlReaderOptions options) {
64 61 Safe.ArgumentNotNull(parser, nameof(parser));
65 62 m_parser = parser;
66 63
67 64 m_options = options ?? new JsonXmlReaderOptions();
68 65
69 66 m_jsonFlattenArrays = m_options.FlattenArrays;
70 67 m_nameTable = m_options.NameTable ?? new NameTable();
71 68
72 69 m_jsonRootName = m_nameTable.Add(string.IsNullOrEmpty(m_options.RootName) ? "data" : m_options.RootName);
73 70 m_jsonArrayItemName = m_nameTable.Add(string.IsNullOrEmpty(m_options.ArrayItemName) ? "item" : m_options.ArrayItemName);
74 71 m_jsonNamespace = m_nameTable.Add(m_options.NamespaceUri ?? string.Empty);
75 72 m_jsonPrefix = m_nameTable.Add(m_options.NodesPrefix ?? string.Empty);
76 73 m_xmlnsPrefix = m_nameTable.Add(XmlNameContext.XmlnsPrefix);
77 74 m_xmlnsNamespace = m_nameTable.Add(XmlNameContext.XmlnsNamespace);
78 75 m_xsiPrefix = m_nameTable.Add(XmlNameContext.XsiPrefix);
79 76 m_xsiNamespace = m_nameTable.Add(XmlNameContext.XsiNamespace);
80 77
81 78 // TODO validate m_jsonRootName, m_jsonArrayItemName
82 79
83 80 m_context = new XmlNameContext(null);
84 81 }
85 82
86 83 public override int AttributeCount {
87 84 get {
88 85 return m_attributes == null ? 0 : m_attributes.Length;
89 86 }
90 87 }
91 88
92 89 public override string BaseURI {
93 90 get {
94 91 return string.Empty;
95 92 }
96 93 }
97 94
98 95 public override int Depth {
99 96 get {
100 97 return m_xmlDepth;
101 98 }
102 99 }
103 100
104 101 public override bool EOF {
105 102 get {
106 103 return m_position == JsonXmlReaderPosition.Eof;
107 104 }
108 105 }
109 106
110 107 public override bool IsEmptyElement {
111 108 get { return m_isEmpty; }
112 109 }
113 110
114 111
115 112 public override string LocalName {
116 113 get {
117 114 return m_qName.Name;
118 115 }
119 116 }
120 117
121 118 public override string NamespaceURI {
122 119 get {
123 120 return m_qName.Namespace;
124 121 }
125 122 }
126 123
127 124 public override XmlNameTable NameTable {
128 125 get {
129 126 return m_nameTable;
130 127 }
131 128 }
132 129
133 130 public override XmlNodeType NodeType {
134 131 get {
135 132 return m_nodeType;
136 133 }
137 134 }
138 135
139 136 public override string Prefix {
140 137 get {
141 138 return m_prefix;
142 139 }
143 140 }
144 141
145 142 public override ReadState ReadState {
146 143 get {
147 144 switch (m_position) {
148 145 case JsonXmlReaderPosition.Initial:
149 146 return ReadState.Initial;
150 147 case JsonXmlReaderPosition.Eof:
151 148 return ReadState.EndOfFile;
152 149 case JsonXmlReaderPosition.Closed:
153 150 return ReadState.Closed;
154 151 case JsonXmlReaderPosition.Error:
155 152 return ReadState.Error;
156 153 default:
157 154 return ReadState.Interactive;
158 155 };
159 156 }
160 157 }
161 158
162 159 public override string Value {
163 160 get {
164 161 return ConvertValueToString(m_value);
165 162 }
166 163 }
167 164
168 165 static string ConvertValueToString(object value) {
169 166 if (value == null)
170 167 return string.Empty;
171 168
172 169 switch (Convert.GetTypeCode(value)) {
173 170 case TypeCode.Double:
174 171 return ((double)value).ToString(CultureInfo.InvariantCulture);
175 172 case TypeCode.String:
176 173 return (string)value;
177 174 case TypeCode.Boolean:
178 175 return (bool)value ? "true" : "false";
179 176 default:
180 177 return value.ToString();
181 178 }
182 179 }
183 180
184 181 public override string GetAttribute(int i) {
185 182 Safe.ArgumentInRange(i, 0, AttributeCount - 1, nameof(i));
186 183 return ConvertValueToString(m_attributes[i].Value);
187 184 }
188 185
189 186 public override string GetAttribute(string name) {
190 187 if (m_attributes == null)
191 188 return null;
192 189 var qName = m_context.Resolve(name);
193 190 var attr = Array.Find(m_attributes, x => x.QName == qName);
194 191 var value = ConvertValueToString(attr?.Value);
195 192 return value == string.Empty ? null : value;
196 193 }
197 194
198 195 public override string GetAttribute(string name, string namespaceURI) {
199 196 if (m_attributes == null)
200 197 return null;
201 198 var qName = new XmlQualifiedName(name, namespaceURI);
202 199 var attr = Array.Find(m_attributes, x => x.QName == qName);
203 200 var value = ConvertValueToString(attr?.Value);
204 201 return value == string.Empty ? null : value;
205 202 }
206 203
207 204 public override string LookupNamespace(string prefix) {
208 205 return m_context.ResolvePrefix(prefix);
209 206 }
210 207
211 208 public override bool MoveToAttribute(string name) {
212 209 if (m_attributes == null || m_attributes.Length == 0)
213 210 return false;
214 211
215 212 var qName = m_context.Resolve(name);
216 213 var index = Array.FindIndex(m_attributes, x => x.QName == qName);
217 214 if (index >= 0) {
218 215 MoveToAttributeImpl(index);
219 216 return true;
220 217 }
221 218 return false;
222 219 }
223 220
224 221 public override bool MoveToAttribute(string name, string ns) {
225 222 if (m_attributes == null || m_attributes.Length == 0)
226 223 return false;
227 224
228 225 var qName = m_context.Resolve(name);
229 226 var index = Array.FindIndex(m_attributes, x => x.QName == qName);
230 227 if (index >= 0) {
231 228 MoveToAttributeImpl(index);
232 229 return true;
233 230 }
234 231 return false;
235 232 }
236 233
237 234 void MoveToAttributeImpl(int i) {
238 235 if (!m_isAttribute) {
239 236 m_elementQName = m_qName;
240 237 m_elementDepth = m_xmlDepth;
241 238 m_elementPrefix = m_prefix;
242 239 m_elementIsEmpty = m_isEmpty;
243 240 m_isAttribute = true;
244 241 }
245 242
246 243 var attr = m_attributes[i];
247 244
248 245
249 246 m_currentAttribute = i;
250 247 m_currentAttributeRead = false;
251 248 m_nodeType = XmlNodeType.Attribute;
252 249
253 250 m_xmlDepth = m_elementDepth + 1;
254 251 m_qName = attr.QName;
255 252 m_value = attr.Value;
256 253 m_prefix = attr.Prefix;
257 254 }
258 255
259 256 public override bool MoveToElement() {
260 257 if (m_isAttribute) {
261 258 m_value = null;
262 259 m_nodeType = XmlNodeType.Element;
263 260 m_xmlDepth = m_elementDepth;
264 261 m_prefix = m_elementPrefix;
265 262 m_qName = m_elementQName;
266 263 m_isEmpty = m_elementIsEmpty;
267 264 m_isAttribute = false;
268 265 return true;
269 266 }
270 267 return false;
271 268 }
272 269
273 270 public override bool MoveToFirstAttribute() {
274 271 if (m_attributes != null && m_attributes.Length > 0) {
275 272 MoveToAttributeImpl(0);
276 273 return true;
277 274 }
278 275 return false;
279 276 }
280 277
281 278 public override bool MoveToNextAttribute() {
282 279 if (m_isAttribute) {
283 280 var next = m_currentAttribute + 1;
284 281 if (next < AttributeCount) {
285 282 MoveToAttributeImpl(next);
286 283 return true;
287 284 }
288 285 return false;
289 286 } else {
290 287 return MoveToFirstAttribute();
291 288 }
292 289
293 290 }
294 291
295 292 public override bool ReadAttributeValue() {
296 293 if (!m_isAttribute || m_currentAttributeRead)
297 294 return false;
298 295
299 296 ValueNode(m_attributes[m_currentAttribute].Value);
300 297 m_currentAttributeRead = true;
301 298 return true;
302 299 }
303 300
304 301 public override void ResolveEntity() {
305 302 /* do nothing */
306 303 }
307 304
308 305 /// <summary>
309 306 /// Determines do we need to increase depth after the current node
310 307 /// </summary>
311 308 /// <returns></returns>
312 309 public bool IsSibling() {
313 310 switch (m_nodeType) {
314 311 case XmlNodeType.None: // start document
315 312 case XmlNodeType.Attribute: // after attribute only it's content can be iterated with ReadAttributeValue method
316 313 return false;
317 314 case XmlNodeType.Element:
318 315 // if the elemnt is empty the next element will be it's sibling
319 316 return m_isEmpty;
320 317
321 318 case XmlNodeType.Document:
322 319 case XmlNodeType.DocumentFragment:
323 320 case XmlNodeType.Entity:
324 321 case XmlNodeType.Text:
325 322 case XmlNodeType.CDATA:
326 323 case XmlNodeType.EntityReference:
327 324 case XmlNodeType.ProcessingInstruction:
328 325 case XmlNodeType.Comment:
329 326 case XmlNodeType.DocumentType:
330 327 case XmlNodeType.Notation:
331 328 case XmlNodeType.Whitespace:
332 329 case XmlNodeType.SignificantWhitespace:
333 330 case XmlNodeType.EndElement:
334 331 case XmlNodeType.EndEntity:
335 332 case XmlNodeType.XmlDeclaration:
336 333 default:
337 334 return true;
338 335 }
339 336 }
340 337
341 338 void ValueNode(object value) {
342 339 if (!IsSibling()) // the node is nested
343 340 m_xmlDepth++;
344 341
345 342 m_qName = XmlQualifiedName.Empty;
346 343 m_nodeType = XmlNodeType.Text;
347 344 m_prefix = string.Empty;
348 345 m_value = value;
349 346 m_isEmpty = false;
350 347 m_attributes = null;
351 348 }
352 349
353 350 void ElementNode(string name, string ns, XmlSimpleAttribute[] attrs, bool empty) {
354 351 if (!IsSibling()) // the node is nested
355 352 m_xmlDepth++;
356 353
357 354 m_context = new XmlNameContext(m_context);
358 355 List<XmlSimpleAttribute> definedAttrs = null;
359 356
360 357 // define new namespaces
361 358 if (attrs != null) {
362 359 foreach (var attr in attrs) {
363 360 if (attr.QName.Name == "xmlns") {
364 361 m_context.DefinePrefix(ConvertValueToString(attr.Value), string.Empty);
365 362 } else if (attr.Prefix == m_xmlnsPrefix) {
366 363 m_context.DefinePrefix(ConvertValueToString(attr.Value), attr.QName.Name);
367 364 } else {
368 365 string attrPrefix;
369 366 if (string.IsNullOrEmpty(attr.QName.Namespace))
370 367 continue;
371 368
372 369 // auto-define prefixes
373 370 if (!m_context.LookupNamespacePrefix(attr.QName.Namespace, out attrPrefix) || string.IsNullOrEmpty(attrPrefix)) {
374 371 // new namespace prefix added
375 372 attrPrefix = m_context.CreateNamespacePrefix(attr.QName.Namespace);
376 373 attr.Prefix = attrPrefix;
377 374
378 375 if (definedAttrs == null)
379 376 definedAttrs = new List<XmlSimpleAttribute>();
380 377
381 378 definedAttrs.Add(new XmlSimpleAttribute(attrPrefix, m_xmlnsNamespace, m_xmlnsPrefix, attr.QName.Namespace));
382 379 }
383 380 }
384 381 }
385 382 }
386 383
387 384 string p;
388 385 // auto-define prefixes
389 386 if (!m_context.LookupNamespacePrefix(ns, out p)) {
390 387 p = m_context.CreateNamespacePrefix(ns);
391 388 if (definedAttrs == null)
392 389 definedAttrs = new List<XmlSimpleAttribute>();
393 390
394 391 definedAttrs.Add(new XmlSimpleAttribute(p, m_xmlnsNamespace, m_xmlnsPrefix, ns));
395 392 }
396 393
397 394 if (definedAttrs != null) {
398 395 if (attrs != null)
399 396 definedAttrs.AddRange(attrs);
400 397 attrs = definedAttrs.ToArray();
401 398 }
402 399
403 400 m_nodeType = XmlNodeType.Element;
404 401 m_qName = new XmlQualifiedName(name, ns);
405 402 m_prefix = p;
406 403 m_value = null;
407 404 m_isEmpty = empty;
408 405 m_attributes = attrs;
409 406 }
410 407
411 408 void EndElementNode(string name, string ns) {
412 409 if (IsSibling()) // closing the element which has children
413 410 m_xmlDepth--;
414 411
415 412 string p;
416 413 if (!m_context.LookupNamespacePrefix(ns, out p))
417 414 throw new Exception($"Failed to lookup namespace '{ns}'");
418 415
419 416 m_context = m_context.ParentContext;
420 417 m_nodeType = XmlNodeType.EndElement;
421 418 m_prefix = p;
422 419 m_qName = new XmlQualifiedName(name, ns);
423 420 m_value = null;
424 421 m_attributes = null;
425 422 m_isEmpty = false;
426 423 }
427 424
428 425 void XmlDeclaration() {
429 426 if (!IsSibling()) // the node is nested
430 427 m_xmlDepth++;
431 428 m_nodeType = XmlNodeType.XmlDeclaration;
432 429 m_qName = new XmlQualifiedName("xml");
433 430 m_value = "version='1.0'";
434 431 m_prefix = string.Empty;
435 432 m_attributes = null;
436 433 m_isEmpty = false;
437 434 }
438 435
439 436 public override bool Read() {
440 437 try {
441 438 string elementName;
442 439 XmlSimpleAttribute[] elementAttrs = null;
443 440 MoveToElement();
444 441
445 442 switch (m_position) {
446 443 case JsonXmlReaderPosition.Initial:
447 444 m_jsonLocalName = m_jsonRootName;
448 445 m_jsonSkip = false;
449 446 XmlDeclaration();
450 447 m_position = JsonXmlReaderPosition.Declaration;
451 448 return true;
452 449 case JsonXmlReaderPosition.Declaration:
453 450 elementAttrs = new[] {
454 451 new XmlSimpleAttribute(m_xsiPrefix, m_xmlnsNamespace, m_xmlnsPrefix, m_xsiNamespace),
455 452 string.IsNullOrEmpty(m_jsonPrefix) ?
456 453 new XmlSimpleAttribute(m_xmlnsPrefix, string.Empty, string.Empty, m_jsonNamespace) :
457 454 new XmlSimpleAttribute(m_jsonPrefix, m_xmlnsNamespace, m_xmlnsPrefix, m_jsonNamespace)
458 455 };
459 456 break;
460 457 case JsonXmlReaderPosition.ValueElement:
461 458 if (!m_isEmpty) {
462 459 ValueNode(m_parser.ElementValue);
463 460 m_position = JsonXmlReaderPosition.ValueContent;
464 461 return true;
465 462 } else {
466 463 m_position = JsonXmlReaderPosition.ValueEndElement;
467 464 break;
468 465 }
469 466 case JsonXmlReaderPosition.ValueContent:
470 467 EndElementNode(m_jsonValueName, m_jsonNamespace);
471 468 m_position = JsonXmlReaderPosition.ValueEndElement;
472 469 return true;
473 470 case JsonXmlReaderPosition.Eof:
474 471 case JsonXmlReaderPosition.Closed:
475 472 case JsonXmlReaderPosition.Error:
476 473 return false;
477 474 }
478 475
479 476 while (m_parser.Read()) {
480 477 var jsonName = m_nameTable.Add(m_parser.ElementName);
481 478
482 479 switch (m_parser.ElementType) {
483 case JSONElementType.BeginObject:
480 case JsonElementType.BeginObject:
484 481 if (!EnterJsonObject(jsonName, out elementName))
485 482 continue;
486 483
487 484 m_position = JsonXmlReaderPosition.BeginObject;
488 485 ElementNode(elementName, m_jsonNamespace, elementAttrs, false);
489 486 break;
490 case JSONElementType.EndObject:
487 case JsonElementType.EndObject:
491 488 if (!LeaveJsonScope(out elementName))
492 489 continue;
493 490
494 491 m_position = JsonXmlReaderPosition.EndObject;
495 492 EndElementNode(elementName, m_jsonNamespace);
496 493 break;
497 case JSONElementType.BeginArray:
494 case JsonElementType.BeginArray:
498 495 if (!EnterJsonArray(jsonName, out elementName))
499 496 continue;
500 497
501 498 m_position = JsonXmlReaderPosition.BeginArray;
502 499 ElementNode(elementName, m_jsonNamespace, elementAttrs, false);
503 500 break;
504 case JSONElementType.EndArray:
501 case JsonElementType.EndArray:
505 502 if (!LeaveJsonScope(out elementName))
506 503 continue;
507 504
508 505 m_position = JsonXmlReaderPosition.EndArray;
509 506 EndElementNode(elementName, m_jsonNamespace);
510 507 break;
511 case JSONElementType.Value:
508 case JsonElementType.Value:
512 509 if (!VisitJsonValue(jsonName, out m_jsonValueName))
513 510 continue;
514 511
515 512 m_position = JsonXmlReaderPosition.ValueElement;
516 513 if (m_parser.ElementValue == null)
517 514 // generate empty element with xsi:nil="true" attribute
518 515 ElementNode(
519 516 m_jsonValueName,
520 517 m_jsonNamespace,
521 518 new[] {
522 519 new XmlSimpleAttribute("nil", m_xsiNamespace, m_xsiPrefix, true)
523 520 },
524 521 true
525 522 );
526 523 else
527 524 ElementNode(m_jsonValueName, m_jsonNamespace, elementAttrs, m_parser.ElementValue as string == string.Empty);
528 525 break;
529 526 default:
530 527 throw new Exception($"Unexpected JSON element {m_parser.ElementType}: {m_parser.ElementName}");
531 528 }
532 529 return true;
533 530 }
534 531
535 532 m_position = JsonXmlReaderPosition.Eof;
536 533 return false;
537 534 } catch {
538 535 m_position = JsonXmlReaderPosition.Error;
539 536 throw;
540 537 }
541 538 }
542 539
543 540 void SaveJsonName() {
544 541 m_jsonNameStack.Push(new JsonContext {
545 542 skip = m_jsonSkip,
546 543 localName = m_jsonLocalName
547 544 });
548 545
549 546 }
550 547
551 548 bool EnterJsonObject(string name, out string elementName) {
552 549 SaveJsonName();
553 550 m_jsonSkip = false;
554 551
555 552 if (string.IsNullOrEmpty(name)) {
556 553 if (m_jsonNameStack.Count != 1 && !m_jsonFlattenArrays)
557 554 m_jsonLocalName = m_jsonArrayItemName;
558 555 } else {
559 556 m_jsonLocalName = name;
560 557 }
561 558
562 559 elementName = m_jsonLocalName;
563 560 return true;
564 561 }
565 562
566 563 /// <summary>
567 564 /// Called when JSON parser visits BeginArray ('[') element.
568 565 /// </summary>
569 566 /// <param name="name">Optional property name if the array is the member of an object</param>
570 567 /// <returns>true if element should be emited, false otherwise</returns>
571 568 bool EnterJsonArray(string name, out string elementName) {
572 569 SaveJsonName();
573 570
574 571 if (string.IsNullOrEmpty(name)) {
575 572 // m_jsonNameStack.Count == 1 means the root node
576 573 if (m_jsonNameStack.Count != 1 && !m_jsonFlattenArrays)
577 574 m_jsonLocalName = m_jsonArrayItemName;
578 575
579 576 m_jsonSkip = false; // we should not flatten arrays inside arrays or in the document root
580 577 } else {
581 578 m_jsonLocalName = name;
582 579 m_jsonSkip = m_jsonFlattenArrays;
583 580 }
584 581 elementName = m_jsonLocalName;
585 582
586 583 return !m_jsonSkip;
587 584 }
588 585
589 586 bool VisitJsonValue(string name, out string elementName) {
590 587 if (string.IsNullOrEmpty(name)) {
591 588 // m_jsonNameStack.Count == 0 means that JSON document consists from simple value
592 589 elementName = (m_jsonNameStack.Count == 0 || m_jsonFlattenArrays) ? m_jsonLocalName : m_jsonArrayItemName;
593 590 } else {
594 591 elementName = name;
595 592 }
596 593 return true;
597 594 }
598 595
599 596 bool LeaveJsonScope(out string elementName) {
600 597 elementName = m_jsonLocalName;
601 598 var skip = m_jsonSkip;
602 599
603 600 var prev = m_jsonNameStack.Pop();
604 601 m_jsonLocalName = prev.localName;
605 602 m_jsonSkip = prev.skip;
606 603
607 604 return !skip;
608 605 }
609 606
610 607 public override string ToString() {
611 608 switch (NodeType) {
612 609 case XmlNodeType.Element:
613 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 ? "/" : "")}>";
614 611 case XmlNodeType.Attribute:
615 612 return $"@{Name}";
616 613 case XmlNodeType.Text:
617 614 return $"{Value}";
618 615 case XmlNodeType.CDATA:
619 616 return $"<![CDATA[{Value}]]>";
620 617 case XmlNodeType.EntityReference:
621 618 return $"&{Name};";
622 619 case XmlNodeType.EndElement:
623 620 return $"</{Name}>";
624 621 default:
625 622 return $".{NodeType} {Name} {Value}";
626 623 }
627 624 }
628 625 }
629 626 }
@@ -1,66 +1,66
1 1
2 2 using System;
3 3 using System.Xml;
4 4
5 namespace Implab.Formats.JSON {
5 namespace Implab.Xml {
6 6 /// <summary>
7 /// Набор необязательных параметров для <see cref="JSONXmlReader"/>, позволяющий управлять процессом
7 /// Набор необязательных параметров для <see cref="JsonXmlReader"/>, позволяющий управлять процессом
8 8 /// интерпретации <c>JSON</c> документа.
9 9 /// </summary>
10 public class JSONXmlReaderOptions : ICloneable {
10 public class JsonXmlReaderOptions : ICloneable {
11 11 /// <summary>
12 12 /// Пространство имен в котором будут располагаться читаемые элементы документа
13 13 /// </summary>
14 public string NamespaceURI {
14 public string NamespaceUri {
15 15 get;
16 16 set;
17 17 }
18 18
19 19 /// <summary>
20 20 /// Интерпретировать массивы как множественные элементы (убирает один уровень вложенности), иначе массив
21 21 /// представляется в виде узла, дочерними элементами которого являются элементы массива, имена дочерних элементов
22 22 /// определяются свойством <see cref="ArrayItemName"/>. По умолчанию <c>false</c>.
23 23 /// </summary>
24 24 public bool FlattenArrays {
25 25 get;
26 26 set;
27 27 }
28 28
29 29 /// <summary>
30 30 /// Префикс, для узлов документа
31 31 /// </summary>
32 32 public string NodesPrefix {
33 33 get;
34 34 set;
35 35 }
36 36
37 37 /// <summary>
38 38 /// Имя корневого элемента в xml документе
39 39 /// </summary>
40 40 public string RootName {
41 41 get;
42 42 set;
43 43 }
44 44
45 45 /// <summary>
46 46 /// Имя элемента для массивов, если не включена опция <see cref="FlattenArrays"/>.
47 47 /// По умолчанию <c>item</c>.
48 48 /// </summary>
49 49 public string ArrayItemName {
50 50 get;
51 51 set;
52 52 }
53 53
54 54 /// <summary>
55 55 /// Таблица атомизированных строк для построения документа.
56 56 /// </summary>
57 57 public XmlNameTable NameTable {
58 58 get;
59 59 set;
60 60 }
61 61
62 62 public object Clone() {
63 63 return MemberwiseClone();
64 64 }
65 65 }
66 66 }
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
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