@joebigelow / wix-1 / commits / 7e87a6b7

Support tagging tuples and definitions

Rob Mensching committed Oct 7, 2019 at 07:33 UTC 7e87a6b75d0570c3ebfbf0e537fcf04e1cf84224
4 files changed +519 -4
src/WixToolset.Data/IntermediateTuple.cs
+159
@@ -9,6 +9,8 @@ namespace WixToolset.Data
9 [DebuggerDisplay("{DebuggerDisplay,nq}")]
10 public class IntermediateTuple
11 {
12 + private object tags;
13 +
14 public IntermediateTuple(IntermediateTupleDefinition definition) : this(definition, null, null)
15 {
16 }
@@ -33,12 +35,132 @@ namespace WixToolset.Data
35
36 private string DebuggerDisplay => $"{this.Definition?.Name} {this.Id?.Id}";
37
38 + public bool AddTag(string add)
39 + {
40 + if (this.tags == null)
41 + {
42 + this.tags = add;
43 + }
44 + else if (this.tags is string tag)
45 + {
46 + if (tag == add)
47 + {
48 + return false;
49 + }
50 +
51 + this.tags = new[] { tag, add };
52 + }
53 + else
54 + {
55 + var tagsArray = (string[])this.tags;
56 + var array = new string[tagsArray.Length + 1];
57 +
58 + for (var i = 0; i < tagsArray.Length; ++i)
59 + {
60 + if (tagsArray[i] == add)
61 + {
62 + return false;
63 + }
64 +
65 + array[i] = tagsArray[i];
66 + }
67 +
68 + array[tagsArray.Length] = add;
69 +
70 + this.tags = array;
71 + }
72 +
73 + return true;
74 + }
75 +
76 + public bool HasTag(string has)
77 + {
78 + if (this.tags == null)
79 + {
80 + return false;
81 + }
82 + else if (this.tags is string tag)
83 + {
84 + return tag == has;
85 + }
86 + else
87 + {
88 + foreach (var element in (string[])this.tags)
89 + {
90 + if (element == has)
91 + {
92 + return true;
93 + }
94 + }
95 + }
96 +
97 + return false;
98 + }
99 +
100 + public bool RemoveTag(string remove)
101 + {
102 + if (this.tags is string tag)
103 + {
104 + if (tag == remove)
105 + {
106 + this.tags = null;
107 + return true;
108 + }
109 + }
110 + else if (this.tags is string[] tagsArray)
111 + {
112 + if (tagsArray.Length == 2)
113 + {
114 + if (tagsArray[0] == remove)
115 + {
116 + this.tags = tagsArray[1];
117 + return true;
118 + }
119 + else if (tagsArray[1] == remove)
120 + {
121 + this.tags = tagsArray[0];
122 + return true;
123 + }
124 + }
125 + else
126 + {
127 + var array = new string[tagsArray.Length - 1];
128 + var arrayIndex = 0;
129 + var found = false;
130 +
131 + for (var i = 0; i < tagsArray.Length; ++i)
132 + {
133 + if (tagsArray[i] == remove)
134 + {
135 + found = true;
136 + continue;
137 + }
138 + else if (arrayIndex == array.Length)
139 + {
140 + break;
141 + }
142 +
143 + array[arrayIndex++] = tagsArray[i];
144 + }
145 +
146 + if (found)
147 + {
148 + this.tags = array;
149 + return true;
150 + }
151 + }
152 + }
153 +
154 + return false;
155 + }
156 +
157 internal static IntermediateTuple Deserialize(ITupleDefinitionCreator creator, Uri baseUri, JsonObject jsonObject)
158 {
159 var definitionName = jsonObject.GetValueOrDefault<string>("type");
160 var idJson = jsonObject.GetValueOrDefault<JsonObject>("id");
161 var sourceLineNumbersJson = jsonObject.GetValueOrDefault<JsonObject>("ln");
162 var fieldsJson = jsonObject.GetValueOrDefault<JsonArray>("fields");
163 + var tagsJson = jsonObject.GetValueOrDefault<JsonArray>("tags");
164
165 var id = (idJson == null) ? null : Identifier.Deserialize(idJson);
166 var sourceLineNumbers = (sourceLineNumbersJson == null) ? null : SourceLineNumber.Deserialize(sourceLineNumbersJson);
@@ -54,6 +176,25 @@ namespace WixToolset.Data
176 }
177 }
178
179 + if (tagsJson == null || tagsJson.Count == 0)
180 + {
181 + }
182 + else if (tagsJson.Count == 1)
183 + {
184 + tuple.tags = (string)tagsJson[0];
185 + }
186 + else
187 + {
188 + var tags = new string[tagsJson.Count];
189 +
190 + for (var i = 0; i < tagsJson.Count; ++i)
191 + {
192 + tags[i] = (string)tagsJson[i];
193 + }
194 +
195 + tuple.tags = tags;
196 + }
197 +
198 return tuple;
199 }
200
@@ -86,6 +227,24 @@ namespace WixToolset.Data
227
228 jsonObject.Add("fields", fieldsJson);
229
230 + if (this.tags is string || this.tags is string[])
231 + {
232 + JsonArray tagsJson;
233 +
234 + if (this.tags is string tag)
235 + {
236 + tagsJson = new JsonArray(1) { tag };
237 + }
238 + else
239 + {
240 + var array = (string[])this.tags;
241 + tagsJson = new JsonArray(array.Length);
242 + tagsJson.AddRange(array);
243 + }
244 +
245 + jsonObject.Add("tags", tagsJson);
246 + }
247 +
248 return jsonObject;
249 }
250 }
src/WixToolset.Data/IntermediateTupleDefinition.cs
+162 -1
@@ -7,6 +7,8 @@ namespace WixToolset.Data
7
8 public class IntermediateTupleDefinition
9 {
10 + private object tags;
11 +
12 public IntermediateTupleDefinition(string name, IntermediateFieldDefinition[] fieldDefinitions, Type strongTupleType)
13 : this(TupleDefinitionType.MustBeFromAnExtension, name, 0, fieldDefinitions, strongTupleType)
14 {
@@ -53,11 +55,131 @@ namespace WixToolset.Data
55 return result;
56 }
57
58 + public bool AddTag(string add)
59 + {
60 + if (this.tags == null)
61 + {
62 + this.tags = add;
63 + }
64 + else if (this.tags is string tag)
65 + {
66 + if (tag == add)
67 + {
68 + return false;
69 + }
70 +
71 + this.tags = new[] { tag, add };
72 + }
73 + else
74 + {
75 + var tagsArray = (string[])this.tags;
76 + var array = new string[tagsArray.Length + 1];
77 +
78 + for (var i = 0; i < tagsArray.Length; ++i)
79 + {
80 + if (tagsArray[i] == add)
81 + {
82 + return false;
83 + }
84 +
85 + array[i] = tagsArray[i];
86 + }
87 +
88 + array[tagsArray.Length] = add;
89 +
90 + this.tags = array;
91 + }
92 +
93 + return true;
94 + }
95 +
96 + public bool HasTag(string has)
97 + {
98 + if (this.tags == null)
99 + {
100 + return false;
101 + }
102 + else if (this.tags is string tag)
103 + {
104 + return tag == has;
105 + }
106 + else
107 + {
108 + foreach (var element in (string[])this.tags)
109 + {
110 + if (element == has)
111 + {
112 + return true;
113 + }
114 + }
115 + }
116 +
117 + return false;
118 + }
119 +
120 + public bool RemoveTag(string remove)
121 + {
122 + if (this.tags is string tag)
123 + {
124 + if (tag == remove)
125 + {
126 + this.tags = null;
127 + return true;
128 + }
129 + }
130 + else if (this.tags is string[] tagsArray)
131 + {
132 + if (tagsArray.Length == 2)
133 + {
134 + if (tagsArray[0] == remove)
135 + {
136 + this.tags = tagsArray[1];
137 + return true;
138 + }
139 + else if (tagsArray[1] == remove)
140 + {
141 + this.tags = tagsArray[0];
142 + return true;
143 + }
144 + }
145 + else
146 + {
147 + var array = new string[tagsArray.Length - 1];
148 + var arrayIndex = 0;
149 + var found = false;
150 +
151 + for (var i = 0; i < tagsArray.Length; ++i)
152 + {
153 + if (tagsArray[i] == remove)
154 + {
155 + found = true;
156 + continue;
157 + }
158 + else if (arrayIndex == array.Length)
159 + {
160 + break;
161 + }
162 +
163 + array[arrayIndex++] = tagsArray[i];
164 + }
165 +
166 + if (found)
167 + {
168 + this.tags = array;
169 + return true;
170 + }
171 + }
172 + }
173 +
174 + return false;
175 + }
176 +
177 internal static IntermediateTupleDefinition Deserialize(JsonObject jsonObject)
178 {
179 var name = jsonObject.GetValueOrDefault<string>("name");
180 var revision = jsonObject.GetValueOrDefault("rev", 0);
181 var definitionsJson = jsonObject.GetValueOrDefault<JsonArray>("fields");
182 + var tagsJson = jsonObject.GetValueOrDefault<JsonArray>("tags");
183
184 var fieldDefinitions = new IntermediateFieldDefinition[definitionsJson.Count];
185
@@ -69,7 +191,28 @@ namespace WixToolset.Data
191 fieldDefinitions[i] = new IntermediateFieldDefinition(fieldName, fieldType);
192 }
193
72 - return new IntermediateTupleDefinition(name, revision, fieldDefinitions, null);
194 + var definition = new IntermediateTupleDefinition(name, revision, fieldDefinitions, null);
195 +
196 + if (tagsJson == null || tagsJson.Count == 0)
197 + {
198 + }
199 + else if (tagsJson.Count == 1)
200 + {
201 + definition.tags = (string)tagsJson[0];
202 + }
203 + else
204 + {
205 + var tags = new string[tagsJson.Count];
206 +
207 + for (var i = 0; i < tagsJson.Count; ++i)
208 + {
209 + tags[i] = (string)tagsJson[i];
210 + }
211 +
212 + definition.tags = tags;
213 + }
214 +
215 + return definition;
216 }
217
218 internal JsonObject Serialize()
@@ -103,6 +246,24 @@ namespace WixToolset.Data
246
247 jsonObject.Add("fields", fieldsJson);
248
249 + if (this.tags is string || this.tags is string[])
250 + {
251 + JsonArray tagsJson;
252 +
253 + if (this.tags is string tag)
254 + {
255 + tagsJson = new JsonArray(1) { tag };
256 + }
257 + else
258 + {
259 + var array = (string[])this.tags;
260 + tagsJson = new JsonArray(array.Length);
261 + tagsJson.AddRange(array);
262 + }
263 +
264 + jsonObject.Add("tags", tagsJson);
265 + }
266 +
267 return jsonObject;
268 }
269 }
src/test/WixToolsetTest.Data/SerializeFixture.cs
+97 -3
@@ -55,7 +55,7 @@ namespace WixToolsetTest.Data
55 new IntermediateFieldDefinition("C", IntermediateFieldType.Bool),
56 };
57
58 - var tupleDef = new IntermediateTupleDefinition("CustomDef", fieldDefs, null);
58 + var tupleDef = new IntermediateTupleDefinition("CustomDef2", fieldDefs, null);
59
60 var tuple = tupleDef.CreateTuple(sln, new Identifier(AccessModifier.Public, "customT"));
61 tuple.Set(0, "foo");
@@ -119,7 +119,93 @@ namespace WixToolsetTest.Data
119 new IntermediateFieldDefinition("D", IntermediateFieldType.String),
120 };
121
122 - var tupleDef2 = new IntermediateTupleDefinition("CustomDef", 1, fieldDefs2, null);
122 + var tupleDef2 = new IntermediateTupleDefinition("CustomDef2", 1, fieldDefs2, null);
123 +
124 + var tuple2 = tupleDef2.CreateTuple(sln, new Identifier(AccessModifier.Public, "customT2"));
125 + tuple2.Set(0, "bar");
126 + tuple2.Set(1, 3);
127 + tuple2.Set(2, false);
128 + tuple2.Set(3, "baz");
129 +
130 + var section2 = new IntermediateSection("test2", SectionType.Fragment, 65001);
131 + section2.Tuples.Add(tuple2);
132 +
133 + var intermediate2 = new Intermediate("TestIntermediate2", new[] { section2 }, null, null);
134 +
135 + // Save
136 + var path1 = Path.GetTempFileName();
137 + var path2 = Path.GetTempFileName();
138 + try
139 + {
140 + intermediate1.Save(path1);
141 + intermediate2.Save(path2);
142 +
143 + var loaded = Intermediate.Load(new[] { path1, path2 });
144 +
145 + var loaded1 = loaded.First();
146 + var loaded2 = loaded.Skip(1).Single();
147 +
148 + var loadedTuple1 = loaded1.Sections.Single().Tuples.Single();
149 + var loadedTuple2 = loaded2.Sections.Single().Tuples.Single();
150 +
151 + Assert.Equal("foo", loadedTuple1.AsString(0));
152 + Assert.Equal(2, loadedTuple1[1].AsNumber());
153 + Assert.True(loadedTuple1[2].AsBool());
154 +
155 + Assert.Equal("bar", loadedTuple2.AsString(0));
156 + Assert.Equal(3, loadedTuple2[1].AsNumber());
157 + Assert.False(loadedTuple2[2].AsBool());
158 + Assert.Equal("baz", loadedTuple2.AsString(3));
159 + }
160 + finally
161 + {
162 + File.Delete(path2);
163 + File.Delete(path1);
164 + }
165 + }
166 +
167 + [Fact]
168 + public void CanSaveAndLoadMultipleIntermediateWithCustomDefinitionsAndTags()
169 + {
170 + var sln = new SourceLineNumber("test.wxs", 1);
171 +
172 + // Intermediate #1
173 + var fieldDefs = new[]
174 + {
175 + new IntermediateFieldDefinition("A", IntermediateFieldType.String),
176 + new IntermediateFieldDefinition("B", IntermediateFieldType.Number),
177 + new IntermediateFieldDefinition("C", IntermediateFieldType.Bool),
178 + };
179 +
180 + var tupleDef = new IntermediateTupleDefinition("CustomDef", fieldDefs, null);
181 +
182 + tupleDef.AddTag("customDef");
183 +
184 + var tuple = tupleDef.CreateTuple(sln, new Identifier(AccessModifier.Public, "customT"));
185 + tuple.Set(0, "foo");
186 + tuple.Set(1, 2);
187 + tuple.Set(2, true);
188 +
189 + tuple.AddTag("tuple1tag");
190 +
191 + var section = new IntermediateSection("test", SectionType.Product, 65001);
192 + section.Tuples.Add(tuple);
193 +
194 + var intermediate1 = new Intermediate("TestIntermediate", new[] { section }, null, null);
195 +
196 + // Intermediate #2
197 + var fieldDefs2 = new[]
198 + {
199 + new IntermediateFieldDefinition("A", IntermediateFieldType.String),
200 + new IntermediateFieldDefinition("B", IntermediateFieldType.Number),
201 + new IntermediateFieldDefinition("C", IntermediateFieldType.Bool),
202 + new IntermediateFieldDefinition("D", IntermediateFieldType.String),
203 + };
204 +
205 + var tupleDef2 = new IntermediateTupleDefinition("CustomDef2", 1, fieldDefs2, null);
206 +
207 + tupleDef2.AddTag("customDef2");
208 + tupleDef2.AddTag("customDef2 tag2");
209
210 var tuple2 = tupleDef2.CreateTuple(sln, new Identifier(AccessModifier.Public, "customT2"));
211 tuple2.Set(0, "bar");
@@ -127,6 +213,9 @@ namespace WixToolsetTest.Data
213 tuple2.Set(2, false);
214 tuple2.Set(3, "baz");
215
216 + tuple2.AddTag("tuple2tag1");
217 + tuple2.AddTag("tuple2tag2");
218 +
219 var section2 = new IntermediateSection("test2", SectionType.Fragment, 65001);
220 section2.Tuples.Add(tuple2);
221
@@ -148,15 +237,20 @@ namespace WixToolsetTest.Data
237 var loadedTuple1 = loaded1.Sections.Single().Tuples.Single();
238 var loadedTuple2 = loaded2.Sections.Single().Tuples.Single();
239
240 + Assert.True(loadedTuple1.Definition.HasTag("customDef"));
241 Assert.Equal("foo", loadedTuple1.AsString(0));
242 Assert.Equal(2, loadedTuple1[1].AsNumber());
243 Assert.True(loadedTuple1[2].AsBool());
154 - Assert.Null(loadedTuple1.AsString(3));
244 + Assert.True(loadedTuple1.HasTag("tuple1tag"));
245
246 + Assert.True(loadedTuple2.Definition.HasTag("customDef2"));
247 + Assert.True(loadedTuple2.Definition.HasTag("customDef2 tag2"));
248 Assert.Equal("bar", loadedTuple2.AsString(0));
249 Assert.Equal(3, loadedTuple2[1].AsNumber());
250 Assert.False(loadedTuple2[2].AsBool());
251 Assert.Equal("baz", loadedTuple2.AsString(3));
252 + Assert.True(loadedTuple2.HasTag("tuple2tag1"));
253 + Assert.True(loadedTuple2.HasTag("tuple2tag2"));
254 }
255 finally
256 {
src/test/WixToolsetTest.Data/TagFixture.cs new
+101
@@ -0,0 +1,101 @@
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 WixToolsetTest.Data
4 +{
5 + using WixToolset.Data;
6 + using Xunit;
7 +
8 + public class TagFixture
9 + {
10 + [Fact]
11 + public void CanAddSingleTag()
12 + {
13 + var tuple = TupleDefinitions.File.CreateTuple();
14 + Assert.True(tuple.AddTag("test"));
15 + Assert.True(tuple.HasTag("test"));
16 + }
17 +
18 + [Fact]
19 + public void CanAddDuplicateTag()
20 + {
21 + var tuple = TupleDefinitions.File.CreateTuple();
22 + Assert.True(tuple.AddTag("test"));
23 + Assert.False(tuple.AddTag("test"));
24 + }
25 +
26 + [Fact]
27 + public void CanAddRemoveSingleTag()
28 + {
29 + var tuple = TupleDefinitions.File.CreateTuple();
30 + Assert.True(tuple.AddTag("test"));
31 + Assert.True(tuple.RemoveTag("test"));
32 + Assert.False(tuple.HasTag("test"));
33 + }
34 +
35 + [Fact]
36 + public void CanAddMultipleTags()
37 + {
38 + var tuple = TupleDefinitions.File.CreateTuple();
39 + Assert.True(tuple.AddTag("test1"));
40 + Assert.True(tuple.AddTag("test2"));
41 + Assert.True(tuple.HasTag("test1"));
42 + Assert.True(tuple.HasTag("test2"));
43 + }
44 +
45 + [Fact]
46 + public void CanAddRemoveMultipleTags()
47 + {
48 + var tuple = TupleDefinitions.File.CreateTuple();
49 + Assert.True(tuple.AddTag("test1"));
50 + Assert.True(tuple.AddTag("test2"));
51 + Assert.True(tuple.RemoveTag("test2"));
52 + Assert.False(tuple.HasTag("test2"));
53 + Assert.True(tuple.RemoveTag("test1"));
54 + Assert.False(tuple.HasTag("test1"));
55 + }
56 +
57 + [Fact]
58 + public void CanAddRemoveMissingTags()
59 + {
60 + var tuple = TupleDefinitions.File.CreateTuple();
61 + Assert.True(tuple.AddTag("test1"));
62 + Assert.True(tuple.AddTag("test2"));
63 + Assert.False(tuple.RemoveTag("test3"));
64 + }
65 +
66 + [Fact]
67 + public void CanAdd2AndRemoveAllTags()
68 + {
69 + var tuple = TupleDefinitions.File.CreateTuple();
70 + Assert.True(tuple.AddTag("test1"));
71 + Assert.True(tuple.AddTag("test2"));
72 + Assert.True(tuple.RemoveTag("test1"));
73 + Assert.True(tuple.RemoveTag("test2"));
74 + }
75 +
76 + [Fact]
77 + public void CanAdd3AndRemoveAllTags()
78 + {
79 + var tuple = TupleDefinitions.File.CreateTuple();
80 + Assert.True(tuple.AddTag("test1"));
81 + Assert.True(tuple.AddTag("test2"));
82 + Assert.True(tuple.AddTag("test3"));
83 + Assert.True(tuple.RemoveTag("test1"));
84 + Assert.True(tuple.RemoveTag("test3"));
85 + Assert.True(tuple.RemoveTag("test2"));
86 + }
87 +
88 + [Fact]
89 + public void CanAdd3AndRemoveMissingTags()
90 + {
91 + var tuple = TupleDefinitions.File.CreateTuple();
92 + Assert.True(tuple.AddTag("test1"));
93 + Assert.True(tuple.AddTag("test2"));
94 + Assert.True(tuple.AddTag("test3"));
95 + Assert.False(tuple.RemoveTag("test4"));
96 + Assert.True(tuple.RemoveTag("test1"));
97 + Assert.True(tuple.RemoveTag("test3"));
98 + Assert.True(tuple.RemoveTag("test2"));
99 + }
100 + }
101 +}