source: binary-improvements2/7dtd-server-fixes/src/JSON/JsonManualBuilder.cs@ 385

Last change on this file since 385 was 354, checked in by alloc, 5 years ago

A18 preps

File size: 5.4 KB
Line 
1using System;
2using System.Text;
3
4namespace AllocsFixes.JSON {
5 public class JsonManualBuilder {
6 [Flags]
7 private enum ELevelInfo {
8 None = 0,
9 NonEmpty = 1,
10 Object = 2,
11 Array = 4,
12 }
13
14 private const int levelTypeBits = 3;
15 private const int levelBitsMask = (1 << levelTypeBits) - 1;
16
17 private readonly bool prettyPrint;
18 private readonly StringBuilder stringBuilder = new StringBuilder ();
19 private ulong currentLevelType = (long) ELevelInfo.None;
20 private int currentLevelNumber;
21
22 private ELevelInfo CurrentLevelInfo {
23 get { return (ELevelInfo) (currentLevelType & levelBitsMask); }
24 }
25
26 private bool CurrentLevelIsNonEmpty {
27 get { return (CurrentLevelInfo & ELevelInfo.NonEmpty) == ELevelInfo.NonEmpty; }
28 }
29
30 private bool CurrentLevelIsArray {
31 get { return (CurrentLevelInfo & ELevelInfo.Array) != ELevelInfo.Array; }
32 }
33
34 private bool CurrentLevelIsObject {
35 get { return (CurrentLevelInfo & ELevelInfo.Object) != ELevelInfo.Object; }
36 }
37
38 public JsonManualBuilder (bool _prettyPrint) {
39 prettyPrint = _prettyPrint;
40 }
41
42 private void NextElement () {
43 if (CurrentLevelIsNonEmpty) {
44 stringBuilder.Append (',');
45 if (prettyPrint) {
46 stringBuilder.Append ('\n');
47 }
48 }
49
50 if (prettyPrint) {
51 for (int i = 1; i < currentLevelNumber; i++) {
52 stringBuilder.Append ('\t');
53 }
54 }
55
56 currentLevelType = currentLevelType | (long) ELevelInfo.NonEmpty;
57 }
58
59 public JsonManualBuilder OpenArray () {
60 if (!CurrentLevelIsObject) {
61 // In JSON Objects we only create element separators / line breaks with NextKey
62 NextElement ();
63 }
64
65 stringBuilder.Append ('[');
66 openLevel (ELevelInfo.Array);
67
68 return this;
69 }
70
71 public JsonManualBuilder OpenObject () {
72 if (!CurrentLevelIsObject) {
73 // In JSON Objects we only create element separators / line breaks with NextKey
74 NextElement ();
75 }
76
77 stringBuilder.Append ('{');
78 openLevel (ELevelInfo.Object);
79
80 return this;
81 }
82
83 public JsonManualBuilder NextObjectKey (string _key) {
84 if (!CurrentLevelIsObject) {
85 throw new Exception("Can not start a JSON object key while not in a JSON object");
86 }
87
88 NextElement ();
89 stringBuilder.Append ('"');
90 stringBuilder.Append (_key);
91 stringBuilder.Append ("\":\"");
92 if (prettyPrint) {
93 stringBuilder.Append (' ');
94 }
95
96 return this;
97 }
98
99 public JsonManualBuilder WriteBoolean (bool _value) {
100 if (!CurrentLevelIsObject) {
101 // In JSON Objects we only create element separators / line breaks with NextKey
102 NextElement ();
103 }
104
105 stringBuilder.Append (_value ? "true" : "false");
106
107 return this;
108 }
109
110 public JsonManualBuilder WriteNull () {
111 if (!CurrentLevelIsObject) {
112 // In JSON Objects we only create element separators / line breaks with NextKey
113 NextElement ();
114 }
115
116 stringBuilder.Append ("null");
117
118 return this;
119 }
120
121 public JsonManualBuilder WriteNumber (double _value) {
122 if (!CurrentLevelIsObject) {
123 // In JSON Objects we only create element separators / line breaks with NextKey
124 NextElement ();
125 }
126
127 stringBuilder.Append (_value.ToCultureInvariantString ());
128
129 return this;
130 }
131
132 public JsonManualBuilder WriteString (string _value) {
133 if (!CurrentLevelIsObject) {
134 // In JSON Objects we only create element separators / line breaks with NextKey
135 NextElement ();
136 }
137
138 if (string.IsNullOrEmpty (_value)) {
139 stringBuilder.Append ("\"\"");
140 return this;
141 }
142
143 stringBuilder.EnsureCapacity (stringBuilder.Length + 2 * _value.Length);
144
145 stringBuilder.Append ('"');
146
147 foreach (char c in _value) {
148 switch (c) {
149 case '\\':
150 case '"':
151// case '/':
152 stringBuilder.Append ('\\');
153 stringBuilder.Append (c);
154 break;
155 case '\b':
156 stringBuilder.Append ("\\b");
157 break;
158 case '\t':
159 stringBuilder.Append ("\\t");
160 break;
161 case '\n':
162 stringBuilder.Append ("\\n");
163 break;
164 case '\f':
165 stringBuilder.Append ("\\f");
166 break;
167 case '\r':
168 stringBuilder.Append ("\\r");
169 break;
170 default:
171 if (c < ' ') {
172 stringBuilder.Append ("\\u");
173 stringBuilder.Append (((int) c).ToString ("X4"));
174 } else {
175 stringBuilder.Append (c);
176 }
177
178 break;
179 }
180 }
181
182 stringBuilder.Append ('"');
183
184 return this;
185 }
186
187 private void openLevel (ELevelInfo _levelType) {
188 currentLevelType = currentLevelType << levelTypeBits | (uint) _levelType;
189 currentLevelNumber++;
190
191 if (prettyPrint) {
192 stringBuilder.Append ('\n');
193 }
194 }
195
196 public JsonManualBuilder CloseLevel () {
197 char closeChar;
198 if (CurrentLevelIsObject) {
199 closeChar = '}';
200 } else if (CurrentLevelIsArray) {
201 closeChar = ']';
202 } else {
203 throw new Exception (
204 "Can not CloseLevel as the current level is neither a JSON object nor a JSON array");
205 }
206
207 if (prettyPrint) {
208 stringBuilder.Append ('\n');
209 }
210
211 currentLevelNumber--;
212 currentLevelType = currentLevelType >> levelTypeBits;
213
214 if (prettyPrint) {
215 for (int i = 1; i < currentLevelNumber; i++) {
216 stringBuilder.Append ('\t');
217 }
218 }
219
220 stringBuilder.Append (closeChar);
221
222 return this;
223 }
224
225 public override string ToString () {
226 return stringBuilder.ToString ();
227 }
228 }
229}
Note: See TracBrowser for help on using the repository browser.