@joebigelow / wix-1 / commits / 3e6d208d

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>