Mercurial > pub > ImplabNet
annotate Implab/JSON/JSONWriter.cs @ 145:706fccb85524 v2
RC: cancellation support for promises + tests
| author | cin |
|---|---|
| date | Sun, 08 Mar 2015 02:52:27 +0300 |
| parents | 2100965eb97f |
| children | 3258399cba83 |
| rev | line source |
|---|---|
| 55 | 1 using System; |
| 2 using System.Collections.Generic; | |
| 3 using System.IO; | |
|
142
2100965eb97f
fixed JSONWriter handling Infinity, NaN and locale aware number formatting
cin
parents:
141
diff
changeset
|
4 using System.Globalization; |
| 55 | 5 |
| 6 namespace Implab.JSON { | |
| 7 public class JSONWriter { | |
| 8 struct Context { | |
| 9 public bool needComma; | |
| 10 public JSONElementContext element; | |
| 11 } | |
| 12 Stack<Context> m_contextStack = new Stack<Context>(); | |
| 13 Context m_context; | |
| 14 | |
| 15 TextWriter m_writer; | |
| 72 | 16 readonly bool m_indent = true; |
| 17 readonly int m_indentSize = 4; | |
| 55 | 18 |
| 19 static readonly char [] _escapeBKS, | |
| 20 _escapeFWD, | |
| 21 _escapeCR, | |
| 22 _escapeNL, | |
| 23 _escapeTAB, | |
| 24 _escapeBSLASH, | |
| 25 _escapeQ; | |
| 26 | |
| 27 static JSONWriter() { | |
| 28 _escapeBKS = "\\b".ToCharArray(); | |
| 29 _escapeFWD = "\\f".ToCharArray(); | |
| 30 _escapeCR = "\\r".ToCharArray(); | |
| 31 _escapeNL = "\\n".ToCharArray(); | |
| 32 _escapeTAB = "\\t".ToCharArray(); | |
| 33 _escapeBSLASH = "\\\\".ToCharArray(); | |
| 34 _escapeQ = "\\\"".ToCharArray(); | |
| 35 } | |
| 36 | |
| 37 public JSONWriter(TextWriter writer) { | |
| 38 Safe.ArgumentNotNull(writer, "writer"); | |
| 39 m_writer = writer; | |
| 40 } | |
| 41 | |
| 72 | 42 public JSONWriter(TextWriter writer, bool indent) { |
| 43 Safe.ArgumentNotNull(writer, "writer"); | |
| 44 | |
| 45 m_writer = writer; | |
| 46 m_indent = indent; | |
| 47 } | |
| 48 | |
| 49 void WriteIndent() { | |
| 50 if (m_indent) { | |
| 51 var indent = new char[m_contextStack.Count * m_indentSize + 1]; | |
| 52 indent[0] = '\n'; | |
| 53 for (int i = 1; i < indent.Length; i++) | |
| 54 indent[i] = ' '; | |
| 55 m_writer.Write(new String(indent)); | |
| 56 } else { | |
| 57 m_writer.Write(' '); | |
| 58 } | |
| 59 } | |
| 60 | |
| 55 | 61 void WriteMemberName(string name) { |
| 62 Safe.ArgumentNotEmpty(name, "name"); | |
| 63 if (m_context.element != JSONElementContext.Object) | |
| 64 OperationNotApplicable("WriteMember"); | |
| 65 if (m_context.needComma) | |
| 72 | 66 m_writer.Write(","); |
| 67 | |
| 68 WriteIndent(); | |
| 55 | 69 m_context.needComma = true; |
| 70 Write(name); | |
| 71 m_writer.Write(" : "); | |
| 72 } | |
| 73 | |
| 74 public void WriteValue(string name, string value) { | |
| 75 WriteMemberName(name); | |
| 76 Write(value); | |
| 77 } | |
| 78 | |
| 79 public void WriteValue(string name, bool value) { | |
| 80 WriteMemberName(name); | |
| 81 Write(value); | |
| 82 } | |
| 83 | |
| 84 public void WriteValue(string name, double value) { | |
| 85 WriteMemberName(name); | |
| 86 Write(value); | |
| 87 } | |
| 88 | |
| 89 public void WriteValue(string value) { | |
| 141 | 90 if (m_context.element == JSONElementContext.Array) { |
| 91 | |
| 92 if (m_context.needComma) | |
| 93 m_writer.Write(","); | |
| 94 WriteIndent(); | |
| 95 m_context.needComma = true; | |
| 96 | |
| 97 Write(value); | |
| 98 } else if (m_context.element == JSONElementContext.None) { | |
| 99 Write(value); | |
| 100 m_context.element = JSONElementContext.Closed; | |
| 101 } else { | |
| 55 | 102 OperationNotApplicable("WriteValue"); |
| 141 | 103 } |
| 55 | 104 } |
| 105 | |
| 106 public void WriteValue(bool value) { | |
| 141 | 107 if (m_context.element == JSONElementContext.Array) { |
| 108 | |
| 109 if (m_context.needComma) | |
| 110 m_writer.Write(","); | |
| 111 WriteIndent(); | |
| 112 m_context.needComma = true; | |
| 113 | |
| 114 Write(value); | |
| 115 } else if (m_context.element == JSONElementContext.None) { | |
| 116 Write(value); | |
| 117 m_context.element = JSONElementContext.Closed; | |
| 118 } else { | |
| 55 | 119 OperationNotApplicable("WriteValue"); |
| 141 | 120 } |
| 55 | 121 } |
| 122 | |
| 123 public void WriteValue(double value) { | |
| 141 | 124 if (m_context.element == JSONElementContext.Array) { |
| 125 | |
| 126 if (m_context.needComma) | |
| 127 m_writer.Write(","); | |
| 128 WriteIndent(); | |
| 129 m_context.needComma = true; | |
| 130 | |
| 131 Write(value); | |
| 132 } else if (m_context.element == JSONElementContext.None) { | |
| 133 Write(value); | |
| 134 m_context.element = JSONElementContext.Closed; | |
| 135 } else { | |
| 55 | 136 OperationNotApplicable("WriteValue"); |
| 141 | 137 } |
| 55 | 138 } |
| 139 | |
| 140 public void BeginObject() { | |
| 141 if (m_context.element != JSONElementContext.None && m_context.element != JSONElementContext.Array) | |
| 142 OperationNotApplicable("BeginObject"); | |
| 143 if (m_context.needComma) | |
| 72 | 144 m_writer.Write(","); |
| 145 | |
| 146 WriteIndent(); | |
| 147 | |
| 55 | 148 m_context.needComma = true; |
| 149 | |
| 150 m_contextStack.Push(m_context); | |
| 151 | |
| 152 m_context = new Context { element = JSONElementContext.Object, needComma = false }; | |
| 72 | 153 m_writer.Write("{"); |
| 55 | 154 } |
| 155 | |
| 156 public void BeginObject(string name) { | |
| 157 WriteMemberName(name); | |
| 158 | |
| 159 m_contextStack.Push(m_context); | |
| 160 | |
| 161 m_context = new Context { element = JSONElementContext.Object, needComma = false }; | |
| 72 | 162 m_writer.Write("{"); |
| 55 | 163 } |
| 164 | |
| 165 public void EndObject() { | |
| 166 if (m_context.element != JSONElementContext.Object) | |
| 141 | 167 OperationNotApplicable("EndObject"); |
| 72 | 168 |
| 55 | 169 m_context = m_contextStack.Pop(); |
| 141 | 170 if (m_contextStack.Count == 0) |
| 171 m_context.element = JSONElementContext.Closed; | |
| 72 | 172 WriteIndent(); |
| 173 m_writer.Write("}"); | |
| 55 | 174 } |
| 175 | |
| 176 public void BeginArray() { | |
| 177 if (m_context.element != JSONElementContext.None && m_context.element != JSONElementContext.Array) | |
| 178 throw new InvalidOperationException(); | |
| 72 | 179 if (m_context.needComma) { |
| 180 m_writer.Write(","); | |
| 181 | |
| 182 } | |
| 55 | 183 m_context.needComma = true; |
| 184 | |
| 72 | 185 WriteIndent(); |
| 55 | 186 m_contextStack.Push(m_context); |
| 187 m_context = new Context { element = JSONElementContext.Array, needComma = false }; | |
| 72 | 188 m_writer.Write("["); |
| 55 | 189 } |
| 190 | |
| 191 public void BeginArray(string name) { | |
| 192 WriteMemberName(name); | |
| 193 | |
| 194 m_contextStack.Push(m_context); | |
| 195 | |
| 196 m_context = new Context { element = JSONElementContext.Array, needComma = false }; | |
| 72 | 197 m_writer.Write("["); |
| 55 | 198 } |
| 199 | |
| 200 public void EndArray() { | |
| 201 if (m_context.element != JSONElementContext.Array) | |
| 202 OperationNotApplicable("EndArray"); | |
| 203 | |
| 204 m_context = m_contextStack.Pop(); | |
| 141 | 205 if (m_contextStack.Count == 0) |
| 206 m_context.element = JSONElementContext.Closed; | |
| 72 | 207 WriteIndent(); |
| 208 m_writer.Write("]"); | |
| 55 | 209 } |
| 210 | |
| 211 void Write(bool value) { | |
| 212 m_writer.Write(value ? "true" : "false"); | |
| 213 } | |
| 214 | |
| 215 | |
| 216 void Write(string value) { | |
| 115 | 217 if (value == null) { |
| 55 | 218 m_writer.Write("null"); |
| 115 | 219 return; |
| 220 } | |
| 55 | 221 |
| 222 var chars = value.ToCharArray(); | |
| 223 m_writer.Write('"'); | |
| 224 | |
|
119
2573b562e328
Promises rewritten, added improved version of AsyncQueue
cin
parents:
118
diff
changeset
|
225 // Analysis disable once ForCanBeConvertedToForeach |
| 55 | 226 for (int i = 0; i < chars.Length; i++) { |
| 227 var ch = chars[i]; | |
| 228 | |
| 229 switch (ch) { | |
| 230 case '\b': | |
| 231 m_writer.Write(_escapeBKS); | |
| 232 break; | |
| 233 case '\f': | |
| 234 m_writer.Write(_escapeFWD); | |
| 235 break; | |
| 236 case '\r': | |
| 237 m_writer.Write(_escapeCR); | |
| 238 break; | |
| 239 case '\n': | |
| 240 m_writer.Write(_escapeNL); | |
| 241 break; | |
| 242 case '\t': | |
| 243 m_writer.Write(_escapeTAB); | |
| 244 break; | |
| 245 case '\\': | |
| 246 m_writer.Write(_escapeBSLASH); | |
| 247 break; | |
| 248 case '"': | |
| 249 m_writer.Write(_escapeQ); | |
| 250 break; | |
| 251 default: | |
| 252 if (ch < 0x20) { | |
| 253 m_writer.Write("\\u00{0:x2}",(int)ch); | |
| 254 } else { | |
| 255 m_writer.Write(ch); | |
| 256 } | |
| 257 break; | |
| 258 } | |
| 259 } | |
| 260 | |
| 261 m_writer.Write('"'); | |
| 262 } | |
| 263 | |
| 264 void Write(double value) { | |
|
142
2100965eb97f
fixed JSONWriter handling Infinity, NaN and locale aware number formatting
cin
parents:
141
diff
changeset
|
265 if (double.IsNaN(value)) |
|
2100965eb97f
fixed JSONWriter handling Infinity, NaN and locale aware number formatting
cin
parents:
141
diff
changeset
|
266 Write("NaN"); |
|
2100965eb97f
fixed JSONWriter handling Infinity, NaN and locale aware number formatting
cin
parents:
141
diff
changeset
|
267 else if (double.IsNegativeInfinity(value)) |
|
2100965eb97f
fixed JSONWriter handling Infinity, NaN and locale aware number formatting
cin
parents:
141
diff
changeset
|
268 Write("-Infinity"); |
|
2100965eb97f
fixed JSONWriter handling Infinity, NaN and locale aware number formatting
cin
parents:
141
diff
changeset
|
269 else if (double.IsPositiveInfinity(value)) |
|
2100965eb97f
fixed JSONWriter handling Infinity, NaN and locale aware number formatting
cin
parents:
141
diff
changeset
|
270 Write("Infinity"); |
|
2100965eb97f
fixed JSONWriter handling Infinity, NaN and locale aware number formatting
cin
parents:
141
diff
changeset
|
271 else |
|
2100965eb97f
fixed JSONWriter handling Infinity, NaN and locale aware number formatting
cin
parents:
141
diff
changeset
|
272 m_writer.Write(value.ToString(CultureInfo.InvariantCulture)); |
| 55 | 273 } |
| 274 | |
| 275 void OperationNotApplicable(string opName) { | |
| 276 throw new InvalidOperationException(String.Format("The operation '{0}' isn't applicable in the context of '{1}'", opName, m_context.element )); | |
| 277 } | |
| 278 | |
| 279 } | |
| 280 } |
