Refactor CustomTable parsing into separate partial and add test
Add a test for referencing custom tables and make the gigantic Compiler.cs a bit smaller by extracting all the CustomTable handling to separate partial.
Rob Mensching committed
Mar 19, 2022 at 11:11 UTC
3e6d208dc34615d2e6428eb91ce16583eb331f7b
4 files changed
+568
-507
src/wix/WixToolset.Core/Compiler.cs
-507
@@ -3673,513 +3673,6 @@ namespace WixToolset.Core
3673
this.Core.EnsureTable(sourceLineNumbers, id);
3674
}
3675
3676
- /// <summary>
3677
- /// Parses a custom table element.
3678
- /// </summary>
3679
- /// <param name="node">Element to parse.</param>
3680
- /// <remarks>not cleaned</remarks>
3681
- private void ParseCustomTableElement(XElement node)
3682
- {
3683
- var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
3684
- string tableId = null;
3685
- var unreal = false;
3686
- var columns = new List<WixCustomTableColumnSymbol>();
3687
- var foundColumns = false;
3688
-
3689
- foreach (var attrib in node.Attributes())
3690
- {
3691
- if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
3692
- {
3693
- switch (attrib.Name.LocalName)
3694
- {
3695
- case "Id":
3696
- tableId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
3697
- break;
3698
- case "Unreal":
3699
- unreal = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
3700
- break;
3701
- default:
3702
- this.Core.UnexpectedAttribute(node, attrib);
3703
- break;
3704
- }
3705
- }
3706
- else
3707
- {
3708
- this.Core.ParseExtensionAttribute(node, attrib);
3709
- }
3710
- }
3711
-
3712
- if (null == tableId)
3713
- {
3714
- this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
3715
- }
3716
- else if (31 < tableId.Length)
3717
- {
3718
- this.Core.Write(ErrorMessages.CustomTableNameTooLong(sourceLineNumbers, node.Name.LocalName, "Id", tableId));
3719
- }
3720
-
3721
- foreach (var child in node.Elements())
3722
- {
3723
- if (CompilerCore.WixNamespace == child.Name.Namespace)
3724
- {
3725
- var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
3726
- switch (child.Name.LocalName)
3727
- {
3728
- case "Column":
3729
- foundColumns = true;
3730
-
3731
- var column = this.ParseColumnElement(child, childSourceLineNumbers, tableId);
3732
- if (column != null)
3733
- {
3734
- columns.Add(column);
3735
- }
3736
- break;
3737
- case "Row":
3738
- this.ParseRowElement(child, childSourceLineNumbers, tableId);
3739
- break;
3740
- default:
3741
- this.Core.UnexpectedElement(node, child);
3742
- break;
3743
- }
3744
- }
3745
- else
3746
- {
3747
- this.Core.ParseExtensionElement(node, child);
3748
- }
3749
- }
3750
-
3751
- if (columns.Count > 0)
3752
- {
3753
- if (!columns.Where(c => c.PrimaryKey).Any())
3754
- {
3755
- this.Core.Write(ErrorMessages.CustomTableMissingPrimaryKey(sourceLineNumbers));
3756
- }
3757
-
3758
- if (!this.Core.EncounteredError)
3759
- {
3760
- var columnNames = String.Join(new string(WixCustomTableSymbol.ColumnNamesSeparator, 1), columns.Select(c => c.Name));
3761
-
3762
- this.Core.AddSymbol(new WixCustomTableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, tableId))
3763
- {
3764
- ColumnNames = columnNames,
3765
- Unreal = unreal,
3766
- });
3767
- }
3768
- else if (!foundColumns)
3769
- {
3770
- this.Core.Write(ErrorMessages.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "Column"));
3771
- }
3772
- }
3773
- }
3774
-
3775
- /// <summary>
3776
- /// Parses a CustomTableRef element.
3777
- /// </summary>
3778
- /// <param name="node">Element to parse.</param>
3779
- private void ParseCustomTableRefElement(XElement node)
3780
- {
3781
- var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
3782
- string tableId = null;
3783
-
3784
- foreach (var attrib in node.Attributes())
3785
- {
3786
- if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
3787
- {
3788
- switch (attrib.Name.LocalName)
3789
- {
3790
- case "Id":
3791
- tableId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
3792
- this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.WixCustomTable, tableId);
3793
- break;
3794
- default:
3795
- this.Core.UnexpectedAttribute(node, attrib);
3796
- break;
3797
- }
3798
- }
3799
- else
3800
- {
3801
- this.Core.ParseExtensionAttribute(node, attrib);
3802
- }
3803
- }
3804
-
3805
- if (null == tableId)
3806
- {
3807
- this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
3808
- }
3809
-
3810
- foreach (var child in node.Elements())
3811
- {
3812
- if (CompilerCore.WixNamespace == child.Name.Namespace)
3813
- {
3814
- var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
3815
- switch (child.Name.LocalName)
3816
- {
3817
- case "Row":
3818
- this.ParseRowElement(child, childSourceLineNumbers, tableId);
3819
- break;
3820
- default:
3821
- this.Core.UnexpectedElement(node, child);
3822
- break;
3823
- }
3824
- }
3825
- else
3826
- {
3827
- this.Core.ParseExtensionElement(node, child);
3828
- }
3829
- }
3830
- }
3831
-
3832
- /// <summary>
3833
- /// Parses a Column element.
3834
- /// </summary>
3835
- /// <param name="child">Element to parse.</param>
3836
- /// <param name="childSourceLineNumbers">Element's SourceLineNumbers.</param>
3837
- /// <param name="tableId">Table Id.</param>
3838
- private WixCustomTableColumnSymbol ParseColumnElement(XElement child, SourceLineNumber childSourceLineNumbers, string tableId)
3839
- {
3840
- string columnName = null;
3841
- IntermediateFieldType? columnType = null;
3842
- var description = String.Empty;
3843
- int? keyColumn = null;
3844
- var keyTable = String.Empty;
3845
- var localizable = false;
3846
- long? maxValue = null;
3847
- long? minValue = null;
3848
- WixCustomTableColumnCategoryType? category = null;
3849
- var modularization = WixCustomTableColumnModularizeType.None;
3850
- var nullable = false;
3851
- var primaryKey = false;
3852
- var setValues = String.Empty;
3853
- var columnUnreal = false;
3854
- var width = 0;
3855
-
3856
- foreach (var childAttrib in child.Attributes())
3857
- {
3858
- switch (childAttrib.Name.LocalName)
3859
- {
3860
- case "Id":
3861
- columnName = this.Core.GetAttributeIdentifierValue(childSourceLineNumbers, childAttrib);
3862
- break;
3863
- case "Category":
3864
- var categoryValue = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib);
3865
- switch (categoryValue)
3866
- {
3867
- case "text":
3868
- category = WixCustomTableColumnCategoryType.Text;
3869
- break;
3870
- case "upperCase":
3871
- category = WixCustomTableColumnCategoryType.UpperCase;
3872
- break;
3873
- case "lowerCase":
3874
- category = WixCustomTableColumnCategoryType.LowerCase;
3875
- break;
3876
- case "integer":
3877
- category = WixCustomTableColumnCategoryType.Integer;
3878
- break;
3879
- case "doubleInteger":
3880
- category = WixCustomTableColumnCategoryType.DoubleInteger;
3881
- break;
3882
- case "timeDate":
3883
- category = WixCustomTableColumnCategoryType.TimeDate;
3884
- break;
3885
- case "identifier":
3886
- category = WixCustomTableColumnCategoryType.Identifier;
3887
- break;
3888
- case "property":
3889
- category = WixCustomTableColumnCategoryType.Property;
3890
- break;
3891
- case "filename":
3892
- category = WixCustomTableColumnCategoryType.Filename;
3893
- break;
3894
- case "wildCardFilename":
3895
- category = WixCustomTableColumnCategoryType.WildCardFilename;
3896
- break;
3897
- case "path":
3898
- category = WixCustomTableColumnCategoryType.Path;
3899
- break;
3900
- case "paths":
3901
- category = WixCustomTableColumnCategoryType.Paths;
3902
- break;
3903
- case "anyPath":
3904
- category = WixCustomTableColumnCategoryType.AnyPath;
3905
- break;
3906
- case "defaultDir":
3907
- category = WixCustomTableColumnCategoryType.DefaultDir;
3908
- break;
3909
- case "regPath":
3910
- category = WixCustomTableColumnCategoryType.RegPath;
3911
- break;
3912
- case "formatted":
3913
- category = WixCustomTableColumnCategoryType.Formatted;
3914
- break;
3915
- case "formattedSddl":
3916
- category = WixCustomTableColumnCategoryType.FormattedSddl;
3917
- break;
3918
- case "template":
3919
- category = WixCustomTableColumnCategoryType.Template;
3920
- break;
3921
- case "condition":
3922
- category = WixCustomTableColumnCategoryType.Condition;
3923
- break;
3924
- case "guid":
3925
- category = WixCustomTableColumnCategoryType.Guid;
3926
- break;
3927
- case "version":
3928
- category = WixCustomTableColumnCategoryType.Version;
3929
- break;
3930
- case "language":
3931
- category = WixCustomTableColumnCategoryType.Language;
3932
- break;
3933
- case "binary":
3934
- category = WixCustomTableColumnCategoryType.Binary;
3935
- break;
3936
- case "customSource":
3937
- category = WixCustomTableColumnCategoryType.CustomSource;
3938
- break;
3939
- case "cabinet":
3940
- category = WixCustomTableColumnCategoryType.Cabinet;
3941
- break;
3942
- case "shortcut":
3943
- category = WixCustomTableColumnCategoryType.Shortcut;
3944
- break;
3945
- case "":
3946
- break;
3947
- default:
3948
- this.Core.Write(ErrorMessages.IllegalAttributeValue(childSourceLineNumbers, child.Name.LocalName, "Category", categoryValue,
3949
- "text", "upperCase", "lowerCase", "integer", "doubleInteger", "timeDate", "identifier", "property", "filename",
3950
- "wildCardFilename", "path", "paths", "anyPath", "defaultDir", "regPath", "formatted", "formattedSddl", "template",
3951
- "condition", "guid", "version", "language", "binary", "customSource", "cabinet", "shortcut"));
3952
- columnType = IntermediateFieldType.String; // set a value to prevent expected attribute error below.
3953
- break;
3954
- }
3955
- break;
3956
- case "Description":
3957
- description = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib);
3958
- break;
3959
- case "KeyColumn":
3960
- keyColumn = this.Core.GetAttributeIntegerValue(childSourceLineNumbers, childAttrib, 1, 32);
3961
- break;
3962
- case "KeyTable":
3963
- keyTable = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib);
3964
- break;
3965
- case "Localizable":
3966
- localizable = YesNoType.Yes == this.Core.GetAttributeYesNoValue(childSourceLineNumbers, childAttrib);
3967
- break;
3968
- case "MaxValue":
3969
- maxValue = this.Core.GetAttributeLongValue(childSourceLineNumbers, childAttrib, Int32.MinValue + 1, Int32.MaxValue);
3970
- break;
3971
- case "MinValue":
3972
- minValue = this.Core.GetAttributeLongValue(childSourceLineNumbers, childAttrib, Int32.MinValue + 1, Int32.MaxValue);
3973
- break;
3974
- case "Modularize":
3975
- var modularizeValue = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib);
3976
- switch (modularizeValue)
3977
- {
3978
- case "column":
3979
- modularization = WixCustomTableColumnModularizeType.Column;
3980
- break;
3981
- case "companionFile":
3982
- modularization = WixCustomTableColumnModularizeType.CompanionFile;
3983
- break;
3984
- case "condition":
3985
- modularization = WixCustomTableColumnModularizeType.Condition;
3986
- break;
3987
- case "controlEventArgument":
3988
- modularization = WixCustomTableColumnModularizeType.ControlEventArgument;
3989
- break;
3990
- case "controlText":
3991
- modularization = WixCustomTableColumnModularizeType.ControlText;
3992
- break;
3993
- case "icon":
3994
- modularization = WixCustomTableColumnModularizeType.Icon;
3995
- break;
3996
- case "none":
3997
- modularization = WixCustomTableColumnModularizeType.None;
3998
- break;
3999
- case "property":
4000
- modularization = WixCustomTableColumnModularizeType.Property;
4001
- break;
4002
- case "semicolonDelimited":
4003
- modularization = WixCustomTableColumnModularizeType.SemicolonDelimited;
4004
- break;
4005
- case "":
4006
- break;
4007
- default:
4008
- this.Core.Write(ErrorMessages.IllegalAttributeValue(childSourceLineNumbers, child.Name.LocalName, "Modularize", modularizeValue, "column", "companionFile", "condition", "controlEventArgument", "controlText", "icon", "property", "semicolonDelimited"));
4009
- columnType = IntermediateFieldType.String; // set a value to prevent expected attribute error below.
4010
- break;
4011
- }
4012
- break;
4013
- case "Nullable":
4014
- nullable = YesNoType.Yes == this.Core.GetAttributeYesNoValue(childSourceLineNumbers, childAttrib);
4015
- break;
4016
- case "PrimaryKey":
4017
- primaryKey = YesNoType.Yes == this.Core.GetAttributeYesNoValue(childSourceLineNumbers, childAttrib);
4018
- break;
4019
- case "Set":
4020
- setValues = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib);
4021
- break;
4022
- case "Type":
4023
- var typeValue = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib);
4024
- switch (typeValue)
4025
- {
4026
- case "binary":
4027
- columnType = IntermediateFieldType.Path;
4028
- break;
4029
- case "int":
4030
- columnType = IntermediateFieldType.Number;
4031
- break;
4032
- case "string":
4033
- columnType = IntermediateFieldType.String;
4034
- break;
4035
- case "":
4036
- break;
4037
- default:
4038
- this.Core.Write(ErrorMessages.IllegalAttributeValue(childSourceLineNumbers, child.Name.LocalName, "Type", typeValue, "binary", "int", "string"));
4039
- columnType = IntermediateFieldType.String; // set a value to prevent expected attribute error below.
4040
- break;
4041
- }
4042
- break;
4043
- case "Width":
4044
- width = this.Core.GetAttributeIntegerValue(childSourceLineNumbers, childAttrib, 0, Int32.MaxValue);
4045
- break;
4046
- case "Unreal":
4047
- columnUnreal = YesNoType.Yes == this.Core.GetAttributeYesNoValue(childSourceLineNumbers, childAttrib);
4048
- break;
4049
- default:
4050
- this.Core.UnexpectedAttribute(child, childAttrib);
4051
- break;
4052
- }
4053
- }
4054
-
4055
- if (null == columnName)
4056
- {
4057
- this.Core.Write(ErrorMessages.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Id"));
4058
- }
4059
-
4060
- if (!columnType.HasValue)
4061
- {
4062
- this.Core.Write(ErrorMessages.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Type"));
4063
- }
4064
- else if (columnType == IntermediateFieldType.Number)
4065
- {
4066
- if (2 != width && 4 != width)
4067
- {
4068
- this.Core.Write(ErrorMessages.CustomTableIllegalColumnWidth(childSourceLineNumbers, child.Name.LocalName, "Width", width));
4069
- }
4070
- }
4071
- else if (columnType == IntermediateFieldType.Path)
4072
- {
4073
- if (!category.HasValue)
4074
- {
4075
- category = WixCustomTableColumnCategoryType.Binary;
4076
- }
4077
- else if (category != WixCustomTableColumnCategoryType.Binary)
4078
- {
4079
- this.Core.Write(ErrorMessages.ExpectedBinaryCategory(childSourceLineNumbers));
4080
- }
4081
- }
4082
-
4083
- this.Core.ParseForExtensionElements(child);
4084
-
4085
- if (this.Core.EncounteredError)
4086
- {
4087
- return null;
4088
- }
4089
-
4090
- var attributes = primaryKey ? WixCustomTableColumnSymbolAttributes.PrimaryKey : WixCustomTableColumnSymbolAttributes.None;
4091
- attributes |= localizable ? WixCustomTableColumnSymbolAttributes.Localizable : WixCustomTableColumnSymbolAttributes.None;
4092
- attributes |= nullable ? WixCustomTableColumnSymbolAttributes.Nullable : WixCustomTableColumnSymbolAttributes.None;
4093
- attributes |= columnUnreal ? WixCustomTableColumnSymbolAttributes.Unreal : WixCustomTableColumnSymbolAttributes.None;
4094
-
4095
- var column = this.Core.AddSymbol(new WixCustomTableColumnSymbol(childSourceLineNumbers, new Identifier(AccessModifier.Section, tableId, columnName))
4096
- {
4097
- TableRef = tableId,
4098
- Name = columnName,
4099
- Type = columnType.Value,
4100
- Attributes = attributes,
4101
- Width = width,
4102
- Category = category,
4103
- Description = description,
4104
- KeyColumn = keyColumn,
4105
- KeyTable = keyTable,
4106
- MaxValue = maxValue,
4107
- MinValue = minValue,
4108
- Modularize = modularization,
4109
- Set = setValues,
4110
- });
4111
- return column;
4112
- }
4113
-
4114
- /// <summary>
4115
- /// Parses a Row element.
4116
- /// </summary>
4117
- /// <param name="node">Element to parse.</param>
4118
- /// <param name="sourceLineNumbers">Element's SourceLineNumbers.</param>
4119
- /// <param name="tableId">Table Id.</param>
4120
- private void ParseRowElement(XElement node, SourceLineNumber sourceLineNumbers, string tableId)
4121
- {
4122
- var rowId = Guid.NewGuid().ToString("N").ToUpperInvariant();
4123
-
4124
- foreach (var attrib in node.Attributes())
4125
- {
4126
- this.Core.ParseExtensionAttribute(node, attrib);
4127
- }
4128
-
4129
- foreach (var child in node.Elements())
4130
- {
4131
- var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
4132
- switch (child.Name.LocalName)
4133
- {
4134
- case "Data":
4135
- string columnName = null;
4136
- string data = null;
4137
- foreach (var attrib in child.Attributes())
4138
- {
4139
- switch (attrib.Name.LocalName)
4140
- {
4141
- case "Column":
4142
- columnName = this.Core.GetAttributeValue(childSourceLineNumbers, attrib);
4143
- break;
4144
- case "Value":
4145
- data = this.Core.GetAttributeValue(childSourceLineNumbers, attrib);
4146
- break;
4147
- default:
4148
- this.Core.ParseExtensionAttribute(child, attrib);
4149
- break;
4150
- }
4151
- }
4152
-
4153
- this.Core.InnerTextDisallowed(node);
4154
-
4155
- if (null == columnName)
4156
- {
4157
- this.Core.Write(ErrorMessages.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Column"));
4158
- }
4159
-
4160
- if (!this.Core.EncounteredError)
4161
- {
4162
- this.Core.AddSymbol(new WixCustomTableCellSymbol(childSourceLineNumbers, new Identifier(AccessModifier.Section, tableId, rowId, columnName))
4163
- {
4164
- RowId = rowId,
4165
- ColumnRef = columnName,
4166
- TableRef = tableId,
4167
- Data = data
4168
- });
4169
- }
4170
- break;
4171
- default:
4172
- this.Core.UnexpectedElement(node, child);
4173
- break;
4174
- }
4175
- }
4176
-
4177
- if (!this.Core.EncounteredError)
4178
- {
4179
- this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.WixCustomTable, tableId);
4180
- }
4181
- }
4182
-
3676
/// <summary>
3677
/// Parses a directory element.
3678
/// </summary>
src/wix/WixToolset.Core/Compiler_CustomTable.cs
new
+520
@@ -0,0 +1,520 @@
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.Core
4
+{
5
+ using System;
6
+ using System.Collections.Generic;
7
+ using System.Linq;
8
+ using System.Xml.Linq;
9
+ using WixToolset.Data;
10
+ using WixToolset.Data.Symbols;
11
+
12
+ /// <summary>
13
+ /// Compiler of the WiX toolset.
14
+ /// </summary>
15
+ internal partial class Compiler : ICompiler
16
+ {
17
+ /// <summary>
18
+ /// Parses a custom table element.
19
+ /// </summary>
20
+ /// <param name="node">Element to parse.</param>
21
+ /// <remarks>not cleaned</remarks>
22
+ private void ParseCustomTableElement(XElement node)
23
+ {
24
+ var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
25
+ string tableId = null;
26
+ var unreal = false;
27
+ var columns = new List<WixCustomTableColumnSymbol>();
28
+ var foundColumns = false;
29
+
30
+ foreach (var attrib in node.Attributes())
31
+ {
32
+ if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
33
+ {
34
+ switch (attrib.Name.LocalName)
35
+ {
36
+ case "Id":
37
+ tableId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
38
+ break;
39
+ case "Unreal":
40
+ unreal = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
41
+ break;
42
+ default:
43
+ this.Core.UnexpectedAttribute(node, attrib);
44
+ break;
45
+ }
46
+ }
47
+ else
48
+ {
49
+ this.Core.ParseExtensionAttribute(node, attrib);
50
+ }
51
+ }
52
+
53
+ if (null == tableId)
54
+ {
55
+ this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
56
+ }
57
+ else if (31 < tableId.Length)
58
+ {
59
+ this.Core.Write(ErrorMessages.CustomTableNameTooLong(sourceLineNumbers, node.Name.LocalName, "Id", tableId));
60
+ }
61
+
62
+ foreach (var child in node.Elements())
63
+ {
64
+ if (CompilerCore.WixNamespace == child.Name.Namespace)
65
+ {
66
+ var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
67
+ switch (child.Name.LocalName)
68
+ {
69
+ case "Column":
70
+ foundColumns = true;
71
+
72
+ var column = this.ParseColumnElement(child, childSourceLineNumbers, tableId);
73
+ if (column != null)
74
+ {
75
+ columns.Add(column);
76
+ }
77
+ break;
78
+ case "Row":
79
+ this.ParseRowElement(child, childSourceLineNumbers, tableId);
80
+ break;
81
+ default:
82
+ this.Core.UnexpectedElement(node, child);
83
+ break;
84
+ }
85
+ }
86
+ else
87
+ {
88
+ this.Core.ParseExtensionElement(node, child);
89
+ }
90
+ }
91
+
92
+ if (columns.Count > 0)
93
+ {
94
+ if (!columns.Where(c => c.PrimaryKey).Any())
95
+ {
96
+ this.Core.Write(ErrorMessages.CustomTableMissingPrimaryKey(sourceLineNumbers));
97
+ }
98
+
99
+ if (!this.Core.EncounteredError)
100
+ {
101
+ var columnNames = String.Join(new string(WixCustomTableSymbol.ColumnNamesSeparator, 1), columns.Select(c => c.Name));
102
+
103
+ this.Core.AddSymbol(new WixCustomTableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, tableId))
104
+ {
105
+ ColumnNames = columnNames,
106
+ Unreal = unreal,
107
+ });
108
+ }
109
+ else if (!foundColumns)
110
+ {
111
+ this.Core.Write(ErrorMessages.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "Column"));
112
+ }
113
+ }
114
+ }
115
+
116
+ /// <summary>
117
+ /// Parses a CustomTableRef element.
118
+ /// </summary>
119
+ /// <param name="node">Element to parse.</param>
120
+ private void ParseCustomTableRefElement(XElement node)
121
+ {
122
+ var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
123
+ string tableId = null;
124
+
125
+ foreach (var attrib in node.Attributes())
126
+ {
127
+ if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
128
+ {
129
+ switch (attrib.Name.LocalName)
130
+ {
131
+ case "Id":
132
+ tableId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
133
+ this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.WixCustomTable, tableId);
134
+ this.Core.EnsureTable(sourceLineNumbers, tableId);
135
+ break;
136
+ default:
137
+ this.Core.UnexpectedAttribute(node, attrib);
138
+ break;
139
+ }
140
+ }
141
+ else
142
+ {
143
+ this.Core.ParseExtensionAttribute(node, attrib);
144
+ }
145
+ }
146
+
147
+ if (null == tableId)
148
+ {
149
+ this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
150
+ }
151
+
152
+ foreach (var child in node.Elements())
153
+ {
154
+ if (CompilerCore.WixNamespace == child.Name.Namespace)
155
+ {
156
+ var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
157
+ switch (child.Name.LocalName)
158
+ {
159
+ case "Row":
160
+ this.ParseRowElement(child, childSourceLineNumbers, tableId);
161
+ break;
162
+ default:
163
+ this.Core.UnexpectedElement(node, child);
164
+ break;
165
+ }
166
+ }
167
+ else
168
+ {
169
+ this.Core.ParseExtensionElement(node, child);
170
+ }
171
+ }
172
+ }
173
+
174
+ /// <summary>
175
+ /// Parses a Column element.
176
+ /// </summary>
177
+ /// <param name="child">Element to parse.</param>
178
+ /// <param name="childSourceLineNumbers">Element's SourceLineNumbers.</param>
179
+ /// <param name="tableId">Table Id.</param>
180
+ private WixCustomTableColumnSymbol ParseColumnElement(XElement child, SourceLineNumber childSourceLineNumbers, string tableId)
181
+ {
182
+ string columnName = null;
183
+ IntermediateFieldType? columnType = null;
184
+ var description = String.Empty;
185
+ int? keyColumn = null;
186
+ var keyTable = String.Empty;
187
+ var localizable = false;
188
+ long? maxValue = null;
189
+ long? minValue = null;
190
+ WixCustomTableColumnCategoryType? category = null;
191
+ var modularization = WixCustomTableColumnModularizeType.None;
192
+ var nullable = false;
193
+ var primaryKey = false;
194
+ var setValues = String.Empty;
195
+ var columnUnreal = false;
196
+ var width = 0;
197
+
198
+ foreach (var childAttrib in child.Attributes())
199
+ {
200
+ switch (childAttrib.Name.LocalName)
201
+ {
202
+ case "Id":
203
+ columnName = this.Core.GetAttributeIdentifierValue(childSourceLineNumbers, childAttrib);
204
+ break;
205
+ case "Category":
206
+ var categoryValue = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib);
207
+ switch (categoryValue)
208
+ {
209
+ case "text":
210
+ category = WixCustomTableColumnCategoryType.Text;
211
+ break;
212
+ case "upperCase":
213
+ category = WixCustomTableColumnCategoryType.UpperCase;
214
+ break;
215
+ case "lowerCase":
216
+ category = WixCustomTableColumnCategoryType.LowerCase;
217
+ break;
218
+ case "integer":
219
+ category = WixCustomTableColumnCategoryType.Integer;
220
+ break;
221
+ case "doubleInteger":
222
+ category = WixCustomTableColumnCategoryType.DoubleInteger;
223
+ break;
224
+ case "timeDate":
225
+ category = WixCustomTableColumnCategoryType.TimeDate;
226
+ break;
227
+ case "identifier":
228
+ category = WixCustomTableColumnCategoryType.Identifier;
229
+ break;
230
+ case "property":
231
+ category = WixCustomTableColumnCategoryType.Property;
232
+ break;
233
+ case "filename":
234
+ category = WixCustomTableColumnCategoryType.Filename;
235
+ break;
236
+ case "wildCardFilename":
237
+ category = WixCustomTableColumnCategoryType.WildCardFilename;
238
+ break;
239
+ case "path":
240
+ category = WixCustomTableColumnCategoryType.Path;
241
+ break;
242
+ case "paths":
243
+ category = WixCustomTableColumnCategoryType.Paths;
244
+ break;
245
+ case "anyPath":
246
+ category = WixCustomTableColumnCategoryType.AnyPath;
247
+ break;
248
+ case "defaultDir":
249
+ category = WixCustomTableColumnCategoryType.DefaultDir;
250
+ break;
251
+ case "regPath":
252
+ category = WixCustomTableColumnCategoryType.RegPath;
253
+ break;
254
+ case "formatted":
255
+ category = WixCustomTableColumnCategoryType.Formatted;
256
+ break;
257
+ case "formattedSddl":
258
+ category = WixCustomTableColumnCategoryType.FormattedSddl;
259
+ break;
260
+ case "template":
261
+ category = WixCustomTableColumnCategoryType.Template;
262
+ break;
263
+ case "condition":
264
+ category = WixCustomTableColumnCategoryType.Condition;
265
+ break;
266
+ case "guid":
267
+ category = WixCustomTableColumnCategoryType.Guid;
268
+ break;
269
+ case "version":
270
+ category = WixCustomTableColumnCategoryType.Version;
271
+ break;
272
+ case "language":
273
+ category = WixCustomTableColumnCategoryType.Language;
274
+ break;
275
+ case "binary":
276
+ category = WixCustomTableColumnCategoryType.Binary;
277
+ break;
278
+ case "customSource":
279
+ category = WixCustomTableColumnCategoryType.CustomSource;
280
+ break;
281
+ case "cabinet":
282
+ category = WixCustomTableColumnCategoryType.Cabinet;
283
+ break;
284
+ case "shortcut":
285
+ category = WixCustomTableColumnCategoryType.Shortcut;
286
+ break;
287
+ case "":
288
+ break;
289
+ default:
290
+ this.Core.Write(ErrorMessages.IllegalAttributeValue(childSourceLineNumbers, child.Name.LocalName, "Category", categoryValue,
291
+ "text", "upperCase", "lowerCase", "integer", "doubleInteger", "timeDate", "identifier", "property", "filename",
292
+ "wildCardFilename", "path", "paths", "anyPath", "defaultDir", "regPath", "formatted", "formattedSddl", "template",
293
+ "condition", "guid", "version", "language", "binary", "customSource", "cabinet", "shortcut"));
294
+ columnType = IntermediateFieldType.String; // set a value to prevent expected attribute error below.
295
+ break;
296
+ }
297
+ break;
298
+ case "Description":
299
+ description = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib);
300
+ break;
301
+ case "KeyColumn":
302
+ keyColumn = this.Core.GetAttributeIntegerValue(childSourceLineNumbers, childAttrib, 1, 32);
303
+ break;
304
+ case "KeyTable":
305
+ keyTable = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib);
306
+ break;
307
+ case "Localizable":
308
+ localizable = YesNoType.Yes == this.Core.GetAttributeYesNoValue(childSourceLineNumbers, childAttrib);
309
+ break;
310
+ case "MaxValue":
311
+ maxValue = this.Core.GetAttributeLongValue(childSourceLineNumbers, childAttrib, Int32.MinValue + 1, Int32.MaxValue);
312
+ break;
313
+ case "MinValue":
314
+ minValue = this.Core.GetAttributeLongValue(childSourceLineNumbers, childAttrib, Int32.MinValue + 1, Int32.MaxValue);
315
+ break;
316
+ case "Modularize":
317
+ var modularizeValue = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib);
318
+ switch (modularizeValue)
319
+ {
320
+ case "column":
321
+ modularization = WixCustomTableColumnModularizeType.Column;
322
+ break;
323
+ case "companionFile":
324
+ modularization = WixCustomTableColumnModularizeType.CompanionFile;
325
+ break;
326
+ case "condition":
327
+ modularization = WixCustomTableColumnModularizeType.Condition;
328
+ break;
329
+ case "controlEventArgument":
330
+ modularization = WixCustomTableColumnModularizeType.ControlEventArgument;
331
+ break;
332
+ case "controlText":
333
+ modularization = WixCustomTableColumnModularizeType.ControlText;
334
+ break;
335
+ case "icon":
336
+ modularization = WixCustomTableColumnModularizeType.Icon;
337
+ break;
338
+ case "none":
339
+ modularization = WixCustomTableColumnModularizeType.None;
340
+ break;
341
+ case "property":
342
+ modularization = WixCustomTableColumnModularizeType.Property;
343
+ break;
344
+ case "semicolonDelimited":
345
+ modularization = WixCustomTableColumnModularizeType.SemicolonDelimited;
346
+ break;
347
+ case "":
348
+ break;
349
+ default:
350
+ this.Core.Write(ErrorMessages.IllegalAttributeValue(childSourceLineNumbers, child.Name.LocalName, "Modularize", modularizeValue, "column", "companionFile", "condition", "controlEventArgument", "controlText", "icon", "property", "semicolonDelimited"));
351
+ columnType = IntermediateFieldType.String; // set a value to prevent expected attribute error below.
352
+ break;
353
+ }
354
+ break;
355
+ case "Nullable":
356
+ nullable = YesNoType.Yes == this.Core.GetAttributeYesNoValue(childSourceLineNumbers, childAttrib);
357
+ break;
358
+ case "PrimaryKey":
359
+ primaryKey = YesNoType.Yes == this.Core.GetAttributeYesNoValue(childSourceLineNumbers, childAttrib);
360
+ break;
361
+ case "Set":
362
+ setValues = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib);
363
+ break;
364
+ case "Type":
365
+ var typeValue = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib);
366
+ switch (typeValue)
367
+ {
368
+ case "binary":
369
+ columnType = IntermediateFieldType.Path;
370
+ break;
371
+ case "int":
372
+ columnType = IntermediateFieldType.Number;
373
+ break;
374
+ case "string":
375
+ columnType = IntermediateFieldType.String;
376
+ break;
377
+ case "":
378
+ break;
379
+ default:
380
+ this.Core.Write(ErrorMessages.IllegalAttributeValue(childSourceLineNumbers, child.Name.LocalName, "Type", typeValue, "binary", "int", "string"));
381
+ columnType = IntermediateFieldType.String; // set a value to prevent expected attribute error below.
382
+ break;
383
+ }
384
+ break;
385
+ case "Width":
386
+ width = this.Core.GetAttributeIntegerValue(childSourceLineNumbers, childAttrib, 0, Int32.MaxValue);
387
+ break;
388
+ case "Unreal":
389
+ columnUnreal = YesNoType.Yes == this.Core.GetAttributeYesNoValue(childSourceLineNumbers, childAttrib);
390
+ break;
391
+ default:
392
+ this.Core.UnexpectedAttribute(child, childAttrib);
393
+ break;
394
+ }
395
+ }
396
+
397
+ if (null == columnName)
398
+ {
399
+ this.Core.Write(ErrorMessages.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Id"));
400
+ }
401
+
402
+ if (!columnType.HasValue)
403
+ {
404
+ this.Core.Write(ErrorMessages.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Type"));
405
+ }
406
+ else if (columnType == IntermediateFieldType.Number)
407
+ {
408
+ if (2 != width && 4 != width)
409
+ {
410
+ this.Core.Write(ErrorMessages.CustomTableIllegalColumnWidth(childSourceLineNumbers, child.Name.LocalName, "Width", width));
411
+ }
412
+ }
413
+ else if (columnType == IntermediateFieldType.Path)
414
+ {
415
+ if (!category.HasValue)
416
+ {
417
+ category = WixCustomTableColumnCategoryType.Binary;
418
+ }
419
+ else if (category != WixCustomTableColumnCategoryType.Binary)
420
+ {
421
+ this.Core.Write(ErrorMessages.ExpectedBinaryCategory(childSourceLineNumbers));
422
+ }
423
+ }
424
+
425
+ this.Core.ParseForExtensionElements(child);
426
+
427
+ if (this.Core.EncounteredError)
428
+ {
429
+ return null;
430
+ }
431
+
432
+ var attributes = primaryKey ? WixCustomTableColumnSymbolAttributes.PrimaryKey : WixCustomTableColumnSymbolAttributes.None;
433
+ attributes |= localizable ? WixCustomTableColumnSymbolAttributes.Localizable : WixCustomTableColumnSymbolAttributes.None;
434
+ attributes |= nullable ? WixCustomTableColumnSymbolAttributes.Nullable : WixCustomTableColumnSymbolAttributes.None;
435
+ attributes |= columnUnreal ? WixCustomTableColumnSymbolAttributes.Unreal : WixCustomTableColumnSymbolAttributes.None;
436
+
437
+ var column = this.Core.AddSymbol(new WixCustomTableColumnSymbol(childSourceLineNumbers, new Identifier(AccessModifier.Section, tableId, columnName))
438
+ {
439
+ TableRef = tableId,
440
+ Name = columnName,
441
+ Type = columnType.Value,
442
+ Attributes = attributes,
443
+ Width = width,
444
+ Category = category,
445
+ Description = description,
446
+ KeyColumn = keyColumn,
447
+ KeyTable = keyTable,
448
+ MaxValue = maxValue,
449
+ MinValue = minValue,
450
+ Modularize = modularization,
451
+ Set = setValues,
452
+ });
453
+ return column;
454
+ }
455
+
456
+ /// <summary>
457
+ /// Parses a Row element.
458
+ /// </summary>
459
+ /// <param name="node">Element to parse.</param>
460
+ /// <param name="sourceLineNumbers">Element's SourceLineNumbers.</param>
461
+ /// <param name="tableId">Table Id.</param>
462
+ private void ParseRowElement(XElement node, SourceLineNumber sourceLineNumbers, string tableId)
463
+ {
464
+ var rowId = Guid.NewGuid().ToString("N").ToUpperInvariant();
465
+
466
+ foreach (var attrib in node.Attributes())
467
+ {
468
+ this.Core.ParseExtensionAttribute(node, attrib);
469
+ }
470
+
471
+ foreach (var child in node.Elements())
472
+ {
473
+ var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
474
+ switch (child.Name.LocalName)
475
+ {
476
+ case "Data":
477
+ string columnName = null;
478
+ string data = null;
479
+ foreach (var attrib in child.Attributes())
480
+ {
481
+ switch (attrib.Name.LocalName)
482
+ {
483
+ case "Column":
484
+ columnName = this.Core.GetAttributeValue(childSourceLineNumbers, attrib);
485
+ break;
486
+ case "Value":
487
+ data = this.Core.GetAttributeValue(childSourceLineNumbers, attrib);
488
+ break;
489
+ default:
490
+ this.Core.ParseExtensionAttribute(child, attrib);
491
+ break;
492
+ }
493
+ }
494
+
495
+ this.Core.InnerTextDisallowed(node);
496
+
497
+ if (null == columnName)
498
+ {
499
+ this.Core.Write(ErrorMessages.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Column"));
500
+ }
501
+
502
+ if (!this.Core.EncounteredError)
503
+ {
504
+ this.Core.AddSymbol(new WixCustomTableCellSymbol(childSourceLineNumbers, new Identifier(AccessModifier.Section, tableId, rowId, columnName))
505
+ {
506
+ RowId = rowId,
507
+ ColumnRef = columnName,
508
+ TableRef = tableId,
509
+ Data = data
510
+ });
511
+ }
512
+ break;
513
+ default:
514
+ this.Core.UnexpectedElement(node, child);
515
+ break;
516
+ }
517
+ }
518
+ }
519
+ }
520
+}
src/wix/test/WixToolsetTest.CoreIntegration/MsiQueryFixture.cs
+32
@@ -334,6 +334,38 @@ namespace WixToolsetTest.CoreIntegration
334
}
335
}
336
337
+ [Fact]
338
+ public void CanBuildMsiWithEmptyCustomTableBecauseOfCustomTableRef()
339
+ {
340
+ var folder = TestData.Get(@"TestData");
341
+ var extensionPath = Path.GetFullPath(new Uri(typeof(ExampleExtensionFactory).Assembly.CodeBase).LocalPath);
342
+
343
+ using (var fs = new DisposableFileSystem())
344
+ {
345
+ var baseFolder = fs.GetFolder();
346
+ var intermediateFolder = Path.Combine(baseFolder, "obj");
347
+ var msiPath = Path.Combine(baseFolder, @"bin\test.msi");
348
+
349
+ var result = WixRunner.Execute(new[]
350
+ {
351
+ "build",
352
+ Path.Combine(folder, "EnsureTable", "EnsureCustomTable.wxs"),
353
+ Path.Combine(folder, "ProductWithComponentGroupRef", "MinimalComponentGroup.wxs"),
354
+ Path.Combine(folder, "ProductWithComponentGroupRef", "Product.wxs"),
355
+ "-ext", extensionPath,
356
+ "-bindpath", Path.Combine(folder, "SingleFile", "data"),
357
+ "-intermediateFolder", intermediateFolder,
358
+ "-o", msiPath
359
+ });
360
+
361
+ result.AssertSuccess();
362
+
363
+ Assert.True(File.Exists(msiPath));
364
+ var results = Query.QueryDatabaseByTable(msiPath, new[] { "SomeCustomTable" });
365
+ WixAssert.StringCollectionEmpty(results["SomeCustomTable"]);
366
+ }
367
+ }
368
+
369
[Fact]
370
public void CanBuildMsiWithEmptyStandardTableBecauseOfEnsureTable()
371
{
src/wix/test/WixToolsetTest.CoreIntegration/TestData/EnsureTable/EnsureCustomTable.wxs
new
+16
@@ -0,0 +1,16 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
3
+ <Fragment>
4
+ <ComponentGroup Id="ProductComponents">
5
+ <ComponentGroupRef Id="MinimalComponentGroup" />
6
+ </ComponentGroup>
7
+
8
+ <CustomTableRef Id="SomeCustomTable" />
9
+ </Fragment>
10
+
11
+ <Fragment>
12
+ <CustomTable Id="SomeCustomTable">
13
+ <Column Id="SomeRow" Type="string" Width="72" PrimaryKey="yes" Category="identifier" />
14
+ </CustomTable>
15
+ </Fragment>
16
+</Wix>