main
cs 300 lines 9.38 KB
Raw
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 namespace WixToolset.Data
4 {
5 using System;
6 using System.Diagnostics;
7 using SimpleJson;
8
9 /// <summary>
10 /// Intermediate symbol.
11 /// </summary>
12 [DebuggerDisplay("{DebuggerDisplay,nq}")]
13 public class IntermediateSymbol
14 {
15 private object tags;
16
17 /// <summary>
18 /// Creates an intermediate symbol.
19 /// </summary>
20 /// <param name="definition">Symbol definition.</param>
21 public IntermediateSymbol(IntermediateSymbolDefinition definition) : this(definition, null, null)
22 {
23 }
24
25 /// <summary>
26 /// Creates an intermediate symbol with source line number and identifier.
27 /// </summary>
28 /// <param name="definition">Symbol definition.</param>
29 /// <param name="sourceLineNumber">Source line number.</param>
30 /// <param name="id">Symbol identifier.</param>
31 public IntermediateSymbol(IntermediateSymbolDefinition definition, SourceLineNumber sourceLineNumber, Identifier id = null)
32 {
33 this.Definition = definition;
34 this.Fields = new IntermediateField[definition.FieldDefinitions.Length];
35 this.SourceLineNumbers = sourceLineNumber;
36 this.Id = id;
37 }
38
39 /// <summary>
40 /// Gets the symbol's definition.
41 /// </summary>
42 public IntermediateSymbolDefinition Definition { get; }
43
44 /// <summary>
45 /// Gets the symbol's fields.
46 /// </summary>
47 public IntermediateField[] Fields { get; }
48
49 /// <summary>
50 /// Gets the optional source line number of the symbol.
51 /// </summary>
52 public SourceLineNumber SourceLineNumbers { get; internal set; }
53
54 /// <summary>
55 /// Gets the optional identifier for the symbol.
56 /// </summary>
57 public Identifier Id { get; internal set; }
58
59 /// <summary>
60 /// Direct access by index to the symbol's fields.
61 /// </summary>
62 /// <param name="index">Index of the field to access.</param>
63 /// <returns>Symbol's field.</returns>
64 public IntermediateField this[int index] => this.Fields[index];
65
66 private string DebuggerDisplay => $"{this.Definition?.Name} {this.Id?.Id}";
67
68 /// <summary>
69 /// Add a custom tag to the symbol.
70 /// </summary>
71 /// <param name="add">String tag to add to the symbol.</param>
72 /// <returns>True if the tag was added; otherwise false if th tag was already present.</returns>
73 public bool AddTag(string add)
74 {
75 if (this.tags == null)
76 {
77 this.tags = add;
78 }
79 else if (this.tags is string tag)
80 {
81 if (tag == add)
82 {
83 return false;
84 }
85
86 this.tags = new[] { tag, add };
87 }
88 else
89 {
90 var tagsArray = (string[])this.tags;
91 var array = new string[tagsArray.Length + 1];
92
93 for (var i = 0; i < tagsArray.Length; ++i)
94 {
95 if (tagsArray[i] == add)
96 {
97 return false;
98 }
99
100 array[i] = tagsArray[i];
101 }
102
103 array[tagsArray.Length] = add;
104
105 this.tags = array;
106 }
107
108 return true;
109 }
110
111 /// <summary>
112 /// Tests whether a symbol has a tag.
113 /// </summary>
114 /// <param name="has">String tag to find.</param>
115 /// <returns>True if the symbol has the tag; otherwise false.</returns>
116 public bool HasTag(string has)
117 {
118 if (this.tags == null)
119 {
120 return false;
121 }
122 else if (this.tags is string tag)
123 {
124 return tag == has;
125 }
126 else
127 {
128 foreach (var element in (string[])this.tags)
129 {
130 if (element == has)
131 {
132 return true;
133 }
134 }
135 }
136
137 return false;
138 }
139
140 /// <summary>
141 /// Removes a tag from the symbol.
142 /// </summary>
143 /// <param name="remove">String tag to remove.</param>
144 /// <returns>True if the tag was removed; otherwise false if the tag was not present.</returns>
145 public bool RemoveTag(string remove)
146 {
147 if (this.tags is string tag)
148 {
149 if (tag == remove)
150 {
151 this.tags = null;
152 return true;
153 }
154 }
155 else if (this.tags is string[] tagsArray)
156 {
157 if (tagsArray.Length == 2)
158 {
159 if (tagsArray[0] == remove)
160 {
161 this.tags = tagsArray[1];
162 return true;
163 }
164 else if (tagsArray[1] == remove)
165 {
166 this.tags = tagsArray[0];
167 return true;
168 }
169 }
170 else
171 {
172 var array = new string[tagsArray.Length - 1];
173 var arrayIndex = 0;
174 var found = false;
175
176 for (var i = 0; i < tagsArray.Length; ++i)
177 {
178 if (tagsArray[i] == remove)
179 {
180 found = true;
181 continue;
182 }
183 else if (arrayIndex == array.Length)
184 {
185 break;
186 }
187
188 array[arrayIndex++] = tagsArray[i];
189 }
190
191 if (found)
192 {
193 this.tags = array;
194 return true;
195 }
196 }
197 }
198
199 return false;
200 }
201
202 internal static IntermediateSymbol Deserialize(ISymbolDefinitionCreator creator, Uri baseUri, JsonObject jsonObject)
203 {
204 var definitionName = jsonObject.GetValueOrDefault<string>("type");
205 var idJson = jsonObject.GetValueOrDefault<JsonObject>("id");
206 var sourceLineNumbersJson = jsonObject.GetValueOrDefault<JsonObject>("ln");
207 var fieldsJson = jsonObject.GetValueOrDefault<JsonArray>("fields");
208 var tagsJson = jsonObject.GetValueOrDefault<JsonArray>("tags");
209
210 var id = (idJson == null) ? null : Identifier.Deserialize(idJson);
211 var sourceLineNumbers = (sourceLineNumbersJson == null) ? null : SourceLineNumber.Deserialize(sourceLineNumbersJson);
212
213 if (!creator.TryGetSymbolDefinitionByName(definitionName, out var definition))
214 {
215 throw new WixException(ErrorMessages.UnknownSymbolType(definitionName));
216 }
217
218 var symbol = definition.CreateSymbol(sourceLineNumbers, id);
219
220 for (var i = 0; i < fieldsJson.Count && i < symbol.Fields.Length; ++i)
221 {
222 if (fieldsJson[i] is JsonObject fieldJson)
223 {
224 symbol.Fields[i] = IntermediateField.Deserialize(symbol.Definition.FieldDefinitions[i], baseUri, fieldJson);
225 }
226 }
227
228 if (tagsJson == null || tagsJson.Count == 0)
229 {
230 }
231 else if (tagsJson.Count == 1)
232 {
233 symbol.tags = (string)tagsJson[0];
234 }
235 else
236 {
237 var tags = new string[tagsJson.Count];
238
239 for (var i = 0; i < tagsJson.Count; ++i)
240 {
241 tags[i] = (string)tagsJson[i];
242 }
243
244 symbol.tags = tags;
245 }
246
247 return symbol;
248 }
249
250 internal JsonObject Serialize()
251 {
252 var jsonObject = new JsonObject
253 {
254 { "type", this.Definition.Name }
255 };
256
257 var idJson = this.Id?.Serialize();
258 if (idJson != null)
259 {
260 jsonObject.Add("id", idJson);
261 }
262
263 var lnJson = this.SourceLineNumbers?.Serialize();
264 if (lnJson != null)
265 {
266 jsonObject.Add("ln", lnJson);
267 }
268
269 var fieldsJson = new JsonArray(this.Fields.Length);
270
271 foreach (var field in this.Fields)
272 {
273 var fieldJson = field?.Serialize();
274 fieldsJson.Add(fieldJson);
275 }
276
277 jsonObject.Add("fields", fieldsJson);
278
279 if (this.tags is string || this.tags is string[])
280 {
281 JsonArray tagsJson;
282
283 if (this.tags is string tag)
284 {
285 tagsJson = new JsonArray(1) { tag };
286 }
287 else
288 {
289 var array = (string[])this.tags;
290 tagsJson = new JsonArray(array.Length);
291 tagsJson.AddRange(array);
292 }
293
294 jsonObject.Add("tags", tagsJson);
295 }
296
297 return jsonObject;
298 }
299 }
300 }