aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2022-03-19 11:11:02 -0700
committerRob Mensching <rob@firegiant.com>2022-03-19 11:54:45 -0700
commit3e6d208dc34615d2e6428eb91ce16583eb331f7b (patch)
tree132080599eda8c14f13d5b73129ef387bd36195f
parentdc634c5c0fa8d6c646d75be144cc546abc4b72c7 (diff)
downloadwix-3e6d208dc34615d2e6428eb91ce16583eb331f7b.tar.gz
wix-3e6d208dc34615d2e6428eb91ce16583eb331f7b.tar.bz2
wix-3e6d208dc34615d2e6428eb91ce16583eb331f7b.zip
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.
-rw-r--r--src/wix/WixToolset.Core/Compiler.cs507
-rw-r--r--src/wix/WixToolset.Core/Compiler_CustomTable.cs520
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/MsiQueryFixture.cs32
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/EnsureTable/EnsureCustomTable.wxs16
4 files changed, 568 insertions, 507 deletions
diff --git a/src/wix/WixToolset.Core/Compiler.cs b/src/wix/WixToolset.Core/Compiler.cs
index 5e2b3342..82faa9bb 100644
--- a/src/wix/WixToolset.Core/Compiler.cs
+++ b/src/wix/WixToolset.Core/Compiler.cs
@@ -3674,513 +3674,6 @@ namespace WixToolset.Core
3674 } 3674 }
3675 3675
3676 /// <summary> 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
4183 /// <summary>
4184 /// Parses a directory element. 3677 /// Parses a directory element.
4185 /// </summary> 3678 /// </summary>
4186 /// <param name="node">Element to parse.</param> 3679 /// <param name="node">Element to parse.</param>
diff --git a/src/wix/WixToolset.Core/Compiler_CustomTable.cs b/src/wix/WixToolset.Core/Compiler_CustomTable.cs
new file mode 100644
index 00000000..cdb1c99a
--- /dev/null
+++ b/src/wix/WixToolset.Core/Compiler_CustomTable.cs
@@ -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
3namespace 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}
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/MsiQueryFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/MsiQueryFixture.cs
index e94114bb..11dc9240 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/MsiQueryFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/MsiQueryFixture.cs
@@ -335,6 +335,38 @@ namespace WixToolsetTest.CoreIntegration
335 } 335 }
336 336
337 [Fact] 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]
338 public void CanBuildMsiWithEmptyStandardTableBecauseOfEnsureTable() 370 public void CanBuildMsiWithEmptyStandardTableBecauseOfEnsureTable()
339 { 371 {
340 var folder = TestData.Get(@"TestData"); 372 var folder = TestData.Get(@"TestData");
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/EnsureTable/EnsureCustomTable.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/EnsureTable/EnsureCustomTable.wxs
new file mode 100644
index 00000000..36151cf5
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/EnsureTable/EnsureCustomTable.wxs
@@ -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>