From 7e87a6b75d0570c3ebfbf0e537fcf04e1cf84224 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Mon, 7 Oct 2019 07:33:18 -0700 Subject: Support tagging tuples and definitions --- src/WixToolset.Data/IntermediateTuple.cs | 159 ++++++++++++++++++++ src/WixToolset.Data/IntermediateTupleDefinition.cs | 163 ++++++++++++++++++++- src/test/WixToolsetTest.Data/SerializeFixture.cs | 100 ++++++++++++- src/test/WixToolsetTest.Data/TagFixture.cs | 101 +++++++++++++ 4 files changed, 519 insertions(+), 4 deletions(-) create mode 100644 src/test/WixToolsetTest.Data/TagFixture.cs diff --git a/src/WixToolset.Data/IntermediateTuple.cs b/src/WixToolset.Data/IntermediateTuple.cs index bc46e3b4..8a5858ee 100644 --- a/src/WixToolset.Data/IntermediateTuple.cs +++ b/src/WixToolset.Data/IntermediateTuple.cs @@ -9,6 +9,8 @@ namespace WixToolset.Data [DebuggerDisplay("{DebuggerDisplay,nq}")] public class IntermediateTuple { + private object tags; + public IntermediateTuple(IntermediateTupleDefinition definition) : this(definition, null, null) { } @@ -33,12 +35,132 @@ namespace WixToolset.Data private string DebuggerDisplay => $"{this.Definition?.Name} {this.Id?.Id}"; + public bool AddTag(string add) + { + if (this.tags == null) + { + this.tags = add; + } + else if (this.tags is string tag) + { + if (tag == add) + { + return false; + } + + this.tags = new[] { tag, add }; + } + else + { + var tagsArray = (string[])this.tags; + var array = new string[tagsArray.Length + 1]; + + for (var i = 0; i < tagsArray.Length; ++i) + { + if (tagsArray[i] == add) + { + return false; + } + + array[i] = tagsArray[i]; + } + + array[tagsArray.Length] = add; + + this.tags = array; + } + + return true; + } + + public bool HasTag(string has) + { + if (this.tags == null) + { + return false; + } + else if (this.tags is string tag) + { + return tag == has; + } + else + { + foreach (var element in (string[])this.tags) + { + if (element == has) + { + return true; + } + } + } + + return false; + } + + public bool RemoveTag(string remove) + { + if (this.tags is string tag) + { + if (tag == remove) + { + this.tags = null; + return true; + } + } + else if (this.tags is string[] tagsArray) + { + if (tagsArray.Length == 2) + { + if (tagsArray[0] == remove) + { + this.tags = tagsArray[1]; + return true; + } + else if (tagsArray[1] == remove) + { + this.tags = tagsArray[0]; + return true; + } + } + else + { + var array = new string[tagsArray.Length - 1]; + var arrayIndex = 0; + var found = false; + + for (var i = 0; i < tagsArray.Length; ++i) + { + if (tagsArray[i] == remove) + { + found = true; + continue; + } + else if (arrayIndex == array.Length) + { + break; + } + + array[arrayIndex++] = tagsArray[i]; + } + + if (found) + { + this.tags = array; + return true; + } + } + } + + return false; + } + internal static IntermediateTuple Deserialize(ITupleDefinitionCreator creator, Uri baseUri, JsonObject jsonObject) { var definitionName = jsonObject.GetValueOrDefault("type"); var idJson = jsonObject.GetValueOrDefault("id"); var sourceLineNumbersJson = jsonObject.GetValueOrDefault("ln"); var fieldsJson = jsonObject.GetValueOrDefault("fields"); + var tagsJson = jsonObject.GetValueOrDefault("tags"); var id = (idJson == null) ? null : Identifier.Deserialize(idJson); var sourceLineNumbers = (sourceLineNumbersJson == null) ? null : SourceLineNumber.Deserialize(sourceLineNumbersJson); @@ -54,6 +176,25 @@ namespace WixToolset.Data } } + if (tagsJson == null || tagsJson.Count == 0) + { + } + else if (tagsJson.Count == 1) + { + tuple.tags = (string)tagsJson[0]; + } + else + { + var tags = new string[tagsJson.Count]; + + for (var i = 0; i < tagsJson.Count; ++i) + { + tags[i] = (string)tagsJson[i]; + } + + tuple.tags = tags; + } + return tuple; } @@ -86,6 +227,24 @@ namespace WixToolset.Data jsonObject.Add("fields", fieldsJson); + if (this.tags is string || this.tags is string[]) + { + JsonArray tagsJson; + + if (this.tags is string tag) + { + tagsJson = new JsonArray(1) { tag }; + } + else + { + var array = (string[])this.tags; + tagsJson = new JsonArray(array.Length); + tagsJson.AddRange(array); + } + + jsonObject.Add("tags", tagsJson); + } + return jsonObject; } } diff --git a/src/WixToolset.Data/IntermediateTupleDefinition.cs b/src/WixToolset.Data/IntermediateTupleDefinition.cs index bc49bc7b..eb05c28b 100644 --- a/src/WixToolset.Data/IntermediateTupleDefinition.cs +++ b/src/WixToolset.Data/IntermediateTupleDefinition.cs @@ -7,6 +7,8 @@ namespace WixToolset.Data public class IntermediateTupleDefinition { + private object tags; + public IntermediateTupleDefinition(string name, IntermediateFieldDefinition[] fieldDefinitions, Type strongTupleType) : this(TupleDefinitionType.MustBeFromAnExtension, name, 0, fieldDefinitions, strongTupleType) { @@ -53,11 +55,131 @@ namespace WixToolset.Data return result; } + public bool AddTag(string add) + { + if (this.tags == null) + { + this.tags = add; + } + else if (this.tags is string tag) + { + if (tag == add) + { + return false; + } + + this.tags = new[] { tag, add }; + } + else + { + var tagsArray = (string[])this.tags; + var array = new string[tagsArray.Length + 1]; + + for (var i = 0; i < tagsArray.Length; ++i) + { + if (tagsArray[i] == add) + { + return false; + } + + array[i] = tagsArray[i]; + } + + array[tagsArray.Length] = add; + + this.tags = array; + } + + return true; + } + + public bool HasTag(string has) + { + if (this.tags == null) + { + return false; + } + else if (this.tags is string tag) + { + return tag == has; + } + else + { + foreach (var element in (string[])this.tags) + { + if (element == has) + { + return true; + } + } + } + + return false; + } + + public bool RemoveTag(string remove) + { + if (this.tags is string tag) + { + if (tag == remove) + { + this.tags = null; + return true; + } + } + else if (this.tags is string[] tagsArray) + { + if (tagsArray.Length == 2) + { + if (tagsArray[0] == remove) + { + this.tags = tagsArray[1]; + return true; + } + else if (tagsArray[1] == remove) + { + this.tags = tagsArray[0]; + return true; + } + } + else + { + var array = new string[tagsArray.Length - 1]; + var arrayIndex = 0; + var found = false; + + for (var i = 0; i < tagsArray.Length; ++i) + { + if (tagsArray[i] == remove) + { + found = true; + continue; + } + else if (arrayIndex == array.Length) + { + break; + } + + array[arrayIndex++] = tagsArray[i]; + } + + if (found) + { + this.tags = array; + return true; + } + } + } + + return false; + } + internal static IntermediateTupleDefinition Deserialize(JsonObject jsonObject) { var name = jsonObject.GetValueOrDefault("name"); var revision = jsonObject.GetValueOrDefault("rev", 0); var definitionsJson = jsonObject.GetValueOrDefault("fields"); + var tagsJson = jsonObject.GetValueOrDefault("tags"); var fieldDefinitions = new IntermediateFieldDefinition[definitionsJson.Count]; @@ -69,7 +191,28 @@ namespace WixToolset.Data fieldDefinitions[i] = new IntermediateFieldDefinition(fieldName, fieldType); } - return new IntermediateTupleDefinition(name, revision, fieldDefinitions, null); + var definition = new IntermediateTupleDefinition(name, revision, fieldDefinitions, null); + + if (tagsJson == null || tagsJson.Count == 0) + { + } + else if (tagsJson.Count == 1) + { + definition.tags = (string)tagsJson[0]; + } + else + { + var tags = new string[tagsJson.Count]; + + for (var i = 0; i < tagsJson.Count; ++i) + { + tags[i] = (string)tagsJson[i]; + } + + definition.tags = tags; + } + + return definition; } internal JsonObject Serialize() @@ -103,6 +246,24 @@ namespace WixToolset.Data jsonObject.Add("fields", fieldsJson); + if (this.tags is string || this.tags is string[]) + { + JsonArray tagsJson; + + if (this.tags is string tag) + { + tagsJson = new JsonArray(1) { tag }; + } + else + { + var array = (string[])this.tags; + tagsJson = new JsonArray(array.Length); + tagsJson.AddRange(array); + } + + jsonObject.Add("tags", tagsJson); + } + return jsonObject; } } diff --git a/src/test/WixToolsetTest.Data/SerializeFixture.cs b/src/test/WixToolsetTest.Data/SerializeFixture.cs index 527359fe..221ca19c 100644 --- a/src/test/WixToolsetTest.Data/SerializeFixture.cs +++ b/src/test/WixToolsetTest.Data/SerializeFixture.cs @@ -55,7 +55,7 @@ namespace WixToolsetTest.Data new IntermediateFieldDefinition("C", IntermediateFieldType.Bool), }; - var tupleDef = new IntermediateTupleDefinition("CustomDef", fieldDefs, null); + var tupleDef = new IntermediateTupleDefinition("CustomDef2", fieldDefs, null); var tuple = tupleDef.CreateTuple(sln, new Identifier(AccessModifier.Public, "customT")); tuple.Set(0, "foo"); @@ -119,7 +119,93 @@ namespace WixToolsetTest.Data new IntermediateFieldDefinition("D", IntermediateFieldType.String), }; - var tupleDef2 = new IntermediateTupleDefinition("CustomDef", 1, fieldDefs2, null); + var tupleDef2 = new IntermediateTupleDefinition("CustomDef2", 1, fieldDefs2, null); + + var tuple2 = tupleDef2.CreateTuple(sln, new Identifier(AccessModifier.Public, "customT2")); + tuple2.Set(0, "bar"); + tuple2.Set(1, 3); + tuple2.Set(2, false); + tuple2.Set(3, "baz"); + + var section2 = new IntermediateSection("test2", SectionType.Fragment, 65001); + section2.Tuples.Add(tuple2); + + var intermediate2 = new Intermediate("TestIntermediate2", new[] { section2 }, null, null); + + // Save + var path1 = Path.GetTempFileName(); + var path2 = Path.GetTempFileName(); + try + { + intermediate1.Save(path1); + intermediate2.Save(path2); + + var loaded = Intermediate.Load(new[] { path1, path2 }); + + var loaded1 = loaded.First(); + var loaded2 = loaded.Skip(1).Single(); + + var loadedTuple1 = loaded1.Sections.Single().Tuples.Single(); + var loadedTuple2 = loaded2.Sections.Single().Tuples.Single(); + + Assert.Equal("foo", loadedTuple1.AsString(0)); + Assert.Equal(2, loadedTuple1[1].AsNumber()); + Assert.True(loadedTuple1[2].AsBool()); + + Assert.Equal("bar", loadedTuple2.AsString(0)); + Assert.Equal(3, loadedTuple2[1].AsNumber()); + Assert.False(loadedTuple2[2].AsBool()); + Assert.Equal("baz", loadedTuple2.AsString(3)); + } + finally + { + File.Delete(path2); + File.Delete(path1); + } + } + + [Fact] + public void CanSaveAndLoadMultipleIntermediateWithCustomDefinitionsAndTags() + { + var sln = new SourceLineNumber("test.wxs", 1); + + // Intermediate #1 + var fieldDefs = new[] + { + new IntermediateFieldDefinition("A", IntermediateFieldType.String), + new IntermediateFieldDefinition("B", IntermediateFieldType.Number), + new IntermediateFieldDefinition("C", IntermediateFieldType.Bool), + }; + + var tupleDef = new IntermediateTupleDefinition("CustomDef", fieldDefs, null); + + tupleDef.AddTag("customDef"); + + var tuple = tupleDef.CreateTuple(sln, new Identifier(AccessModifier.Public, "customT")); + tuple.Set(0, "foo"); + tuple.Set(1, 2); + tuple.Set(2, true); + + tuple.AddTag("tuple1tag"); + + var section = new IntermediateSection("test", SectionType.Product, 65001); + section.Tuples.Add(tuple); + + var intermediate1 = new Intermediate("TestIntermediate", new[] { section }, null, null); + + // Intermediate #2 + var fieldDefs2 = new[] + { + new IntermediateFieldDefinition("A", IntermediateFieldType.String), + new IntermediateFieldDefinition("B", IntermediateFieldType.Number), + new IntermediateFieldDefinition("C", IntermediateFieldType.Bool), + new IntermediateFieldDefinition("D", IntermediateFieldType.String), + }; + + var tupleDef2 = new IntermediateTupleDefinition("CustomDef2", 1, fieldDefs2, null); + + tupleDef2.AddTag("customDef2"); + tupleDef2.AddTag("customDef2 tag2"); var tuple2 = tupleDef2.CreateTuple(sln, new Identifier(AccessModifier.Public, "customT2")); tuple2.Set(0, "bar"); @@ -127,6 +213,9 @@ namespace WixToolsetTest.Data tuple2.Set(2, false); tuple2.Set(3, "baz"); + tuple2.AddTag("tuple2tag1"); + tuple2.AddTag("tuple2tag2"); + var section2 = new IntermediateSection("test2", SectionType.Fragment, 65001); section2.Tuples.Add(tuple2); @@ -148,15 +237,20 @@ namespace WixToolsetTest.Data var loadedTuple1 = loaded1.Sections.Single().Tuples.Single(); var loadedTuple2 = loaded2.Sections.Single().Tuples.Single(); + Assert.True(loadedTuple1.Definition.HasTag("customDef")); Assert.Equal("foo", loadedTuple1.AsString(0)); Assert.Equal(2, loadedTuple1[1].AsNumber()); Assert.True(loadedTuple1[2].AsBool()); - Assert.Null(loadedTuple1.AsString(3)); + Assert.True(loadedTuple1.HasTag("tuple1tag")); + Assert.True(loadedTuple2.Definition.HasTag("customDef2")); + Assert.True(loadedTuple2.Definition.HasTag("customDef2 tag2")); Assert.Equal("bar", loadedTuple2.AsString(0)); Assert.Equal(3, loadedTuple2[1].AsNumber()); Assert.False(loadedTuple2[2].AsBool()); Assert.Equal("baz", loadedTuple2.AsString(3)); + Assert.True(loadedTuple2.HasTag("tuple2tag1")); + Assert.True(loadedTuple2.HasTag("tuple2tag2")); } finally { diff --git a/src/test/WixToolsetTest.Data/TagFixture.cs b/src/test/WixToolsetTest.Data/TagFixture.cs new file mode 100644 index 00000000..4c0426a0 --- /dev/null +++ b/src/test/WixToolsetTest.Data/TagFixture.cs @@ -0,0 +1,101 @@ +// 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. + +namespace WixToolsetTest.Data +{ + using WixToolset.Data; + using Xunit; + + public class TagFixture + { + [Fact] + public void CanAddSingleTag() + { + var tuple = TupleDefinitions.File.CreateTuple(); + Assert.True(tuple.AddTag("test")); + Assert.True(tuple.HasTag("test")); + } + + [Fact] + public void CanAddDuplicateTag() + { + var tuple = TupleDefinitions.File.CreateTuple(); + Assert.True(tuple.AddTag("test")); + Assert.False(tuple.AddTag("test")); + } + + [Fact] + public void CanAddRemoveSingleTag() + { + var tuple = TupleDefinitions.File.CreateTuple(); + Assert.True(tuple.AddTag("test")); + Assert.True(tuple.RemoveTag("test")); + Assert.False(tuple.HasTag("test")); + } + + [Fact] + public void CanAddMultipleTags() + { + var tuple = TupleDefinitions.File.CreateTuple(); + Assert.True(tuple.AddTag("test1")); + Assert.True(tuple.AddTag("test2")); + Assert.True(tuple.HasTag("test1")); + Assert.True(tuple.HasTag("test2")); + } + + [Fact] + public void CanAddRemoveMultipleTags() + { + var tuple = TupleDefinitions.File.CreateTuple(); + Assert.True(tuple.AddTag("test1")); + Assert.True(tuple.AddTag("test2")); + Assert.True(tuple.RemoveTag("test2")); + Assert.False(tuple.HasTag("test2")); + Assert.True(tuple.RemoveTag("test1")); + Assert.False(tuple.HasTag("test1")); + } + + [Fact] + public void CanAddRemoveMissingTags() + { + var tuple = TupleDefinitions.File.CreateTuple(); + Assert.True(tuple.AddTag("test1")); + Assert.True(tuple.AddTag("test2")); + Assert.False(tuple.RemoveTag("test3")); + } + + [Fact] + public void CanAdd2AndRemoveAllTags() + { + var tuple = TupleDefinitions.File.CreateTuple(); + Assert.True(tuple.AddTag("test1")); + Assert.True(tuple.AddTag("test2")); + Assert.True(tuple.RemoveTag("test1")); + Assert.True(tuple.RemoveTag("test2")); + } + + [Fact] + public void CanAdd3AndRemoveAllTags() + { + var tuple = TupleDefinitions.File.CreateTuple(); + Assert.True(tuple.AddTag("test1")); + Assert.True(tuple.AddTag("test2")); + Assert.True(tuple.AddTag("test3")); + Assert.True(tuple.RemoveTag("test1")); + Assert.True(tuple.RemoveTag("test3")); + Assert.True(tuple.RemoveTag("test2")); + } + + [Fact] + public void CanAdd3AndRemoveMissingTags() + { + var tuple = TupleDefinitions.File.CreateTuple(); + Assert.True(tuple.AddTag("test1")); + Assert.True(tuple.AddTag("test2")); + Assert.True(tuple.AddTag("test3")); + Assert.False(tuple.RemoveTag("test4")); + Assert.True(tuple.RemoveTag("test1")); + Assert.True(tuple.RemoveTag("test3")); + Assert.True(tuple.RemoveTag("test2")); + } + } +} -- cgit v1.2.3-55-g6feb