main
cs 999 lines 48.7 KB
Raw
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;
7 using System.Collections.Generic;
8 using System.Diagnostics;
9 using System.Globalization;
10 using System.Reflection;
11 using System.Text;
12 using System.Xml.Linq;
13 using WixToolset.Data;
14 using WixToolset.Data.Symbols;
15 using WixToolset.Data.WindowsInstaller;
16 using WixToolset.Extensibility;
17 using WixToolset.Extensibility.Data;
18 using WixToolset.Extensibility.Services;
19
20 /// <summary>
21 /// Core class for the compiler.
22 /// </summary>
23 internal class CompilerCore
24 {
25 internal static readonly XNamespace W3SchemaPrefix = "http://www.w3.org/";
26 internal static readonly XNamespace WixNamespace = "http://wixtoolset.org/schemas/v4/wxs";
27
28 private readonly Dictionary<XNamespace, ICompilerExtension> extensions;
29 private readonly IBundleValidator bundleValidator;
30 private readonly IParseHelper parseHelper;
31 private readonly Intermediate intermediate;
32 private readonly IMessaging messaging;
33 private Dictionary<string, string> activeSectionCachedInlinedDirectoryIds;
34 private HashSet<string> activeSectionSimpleReferences;
35
36 /// <summary>
37 /// Constructor for all compiler core.
38 /// </summary>
39 /// <param name="intermediate">The Intermediate object representing compiled source document.</param>
40 /// <param name="messaging"></param>
41 /// <param name="bundleValidator"></param>
42 /// <param name="parseHelper"></param>
43 /// <param name="extensions">The WiX extensions collection.</param>
44 internal CompilerCore(Intermediate intermediate, IMessaging messaging, IBundleValidator bundleValidator, IParseHelper parseHelper, Dictionary<XNamespace, ICompilerExtension> extensions)
45 {
46 this.extensions = extensions;
47 this.bundleValidator = bundleValidator;
48 this.parseHelper = parseHelper;
49 this.intermediate = intermediate;
50 this.messaging = messaging;
51 }
52
53 /// <summary>
54 /// Gets the section the compiler is currently emitting symbols into.
55 /// </summary>
56 /// <value>The section the compiler is currently emitting symbols into.</value>
57 public IntermediateSection ActiveSection { get; private set; }
58
59 /// <summary>
60 /// Gets whether the compiler core encountered an error while processing.
61 /// </summary>
62 /// <value>Flag if core encountered an error during processing.</value>
63 public bool EncounteredError => this.messaging.EncounteredError;
64
65 /// <summary>
66 /// Gets or sets the option to show pedantic messages.
67 /// </summary>
68 /// <value>The option to show pedantic messages.</value>
69 public bool ShowPedanticMessages { get; set; }
70
71 /// <summary>
72 /// Add a symbol to the active section.
73 /// </summary>
74 /// <param name="symbol">Symbol to add.</param>
75 public T AddSymbol<T>(T symbol)
76 where T : IntermediateSymbol
77 {
78 return this.ActiveSection.AddSymbol(symbol);
79 }
80
81 /// <summary>
82 /// Convert a bit array into an int value.
83 /// </summary>
84 /// <param name="bits">The bit array to convert.</param>
85 /// <returns>The converted int value.</returns>
86 public int CreateIntegerFromBitArray(BitArray bits)
87 {
88 if (32 != bits.Length)
89 {
90 throw new ArgumentException(String.Format("Can only convert a bit array with 32-bits to integer. Actual number of bits in array: {0}", bits.Length), "bits");
91 }
92
93 int[] intArray = new int[1];
94 bits.CopyTo(intArray, 0);
95
96 return intArray[0];
97 }
98
99 /// <summary>
100 /// Sets a bit in a bit array based on the index at which an attribute name was found in a string array.
101 /// </summary>
102 /// <param name="attributeNames">Array of attributes that map to bits.</param>
103 /// <param name="attributeName">Name of attribute to check.</param>
104 /// <param name="attributeValue">Value of attribute to check.</param>
105 /// <param name="bits">The bit array in which the bit will be set if found.</param>
106 /// <param name="offset">The offset into the bit array.</param>
107 /// <returns>true if the bit was set; false otherwise.</returns>
108 public bool TrySetBitFromName(string[] attributeNames, string attributeName, YesNoType attributeValue, BitArray bits, int offset)
109 {
110 for (int i = 0; i < attributeNames.Length; i++)
111 {
112 if (attributeName.Equals(attributeNames[i], StringComparison.Ordinal))
113 {
114 bits.Set(i + offset, YesNoType.Yes == attributeValue);
115 return true;
116 }
117 }
118
119 return false;
120 }
121
122 internal void InnerTextDisallowed(XElement element)
123 {
124 this.parseHelper.InnerTextDisallowed(element);
125 }
126
127 internal void InnerTextDisallowed(XElement element, string attributeName)
128 {
129 this.parseHelper.InnerTextDisallowed(element, attributeName);
130 }
131
132 /// <summary>
133 /// Verifies that a filename is ambiguous.
134 /// </summary>
135 /// <param name="filename">Filename to verify.</param>
136 /// <returns>true if the filename is ambiguous; false otherwise.</returns>
137 public static bool IsAmbiguousFilename(string filename)
138 {
139 if (String.IsNullOrEmpty(filename))
140 {
141 return false;
142 }
143
144 var tilde = filename.IndexOf('~');
145 return (tilde > 0 && tilde < filename.Length) && Char.IsNumber(filename[tilde + 1]);
146 }
147
148 /// <summary>
149 /// Verifies that a value is a legal identifier.
150 /// </summary>
151 /// <param name="value">The value to verify.</param>
152 /// <returns>true if the value is an identifier; false otherwise.</returns>
153 public bool IsValidIdentifier(string value)
154 {
155 return this.parseHelper.IsValidIdentifier(value);
156 }
157
158 /// <summary>
159 /// Verifies if an identifier is a valid loc identifier.
160 /// </summary>
161 /// <param name="identifier">Identifier to verify.</param>
162 /// <returns>True if the identifier is a valid loc identifier.</returns>
163 public bool IsValidLocIdentifier(string identifier)
164 {
165 return this.parseHelper.IsValidLocIdentifier(identifier);
166 }
167
168 /// <summary>
169 /// Verifies if a filename is a valid long filename.
170 /// </summary>
171 /// <param name="filename">Filename to verify.</param>
172 /// <param name="allowWildcards">true if wildcards are allowed in the filename.</param>
173 /// <param name="allowRelative">true if relative paths are allowed in the filename.</param>
174 /// <returns>True if the filename is a valid long filename</returns>
175 public bool IsValidLongFilename(string filename, bool allowWildcards = false, bool allowRelative = false)
176 {
177 return this.parseHelper.IsValidLongFilename(filename, allowWildcards, allowRelative);
178 }
179
180 /// <summary>
181 /// Verifies if a filename is a valid short filename.
182 /// </summary>
183 /// <param name="filename">Filename to verify.</param>
184 /// <param name="allowWildcards">true if wildcards are allowed in the filename.</param>
185 /// <returns>True if the filename is a valid short filename</returns>
186 public bool IsValidShortFilename(string filename, bool allowWildcards)
187 {
188 return this.parseHelper.IsValidShortFilename(filename, allowWildcards);
189 }
190
191 /// <summary>
192 /// Replaces the illegal filename characters to create a legal name.
193 /// </summary>
194 /// <param name="filename">Filename to make valid.</param>
195 /// <param name="replace">Replacement string for invalid characters in filename.</param>
196 /// <returns>Valid filename.</returns>
197 public static string MakeValidLongFileName(string filename, char replace)
198 {
199 if (String.IsNullOrEmpty(filename))
200 {
201 return filename;
202 }
203
204 StringBuilder sb = null;
205
206 var found = filename.IndexOfAny(Common.IllegalLongFilenameCharacters);
207 while (found != -1)
208 {
209 if (sb == null)
210 {
211 sb = new StringBuilder(filename);
212 }
213
214 sb[found] = replace;
215
216 found = (found + 1 < filename.Length) ? filename.IndexOfAny(Common.IllegalLongFilenameCharacters, found + 1) : -1;
217 }
218
219 return sb?.ToString() ?? filename;
220 }
221
222 /// <summary>
223 /// Verifies the given string is a valid product version.
224 /// </summary>
225 /// <param name="version">The product version to verify.</param>
226 /// <returns>True if version is a valid product version</returns>
227 public static bool IsValidProductVersion(string version)
228 {
229 return Common.IsValidBinderVariable(version) || Common.IsValidMsiProductVersion(version);
230 }
231
232 /// <summary>
233 /// Creates group and ordering information.
234 /// </summary>
235 /// <param name="sourceLineNumbers">Source line numbers.</param>
236 /// <param name="parentType">Type of parent group, if known.</param>
237 /// <param name="parentId">Identifier of parent group, if known.</param>
238 /// <param name="type">Type of this item.</param>
239 /// <param name="id">Identifier for this item.</param>
240 /// <param name="previousType">Type of previous item, if known.</param>
241 /// <param name="previousId">Identifier of previous item, if known</param>
242 public void CreateGroupAndOrderingRows(SourceLineNumber sourceLineNumbers,
243 ComplexReferenceParentType parentType, string parentId,
244 ComplexReferenceChildType type, string id,
245 ComplexReferenceChildType previousType, string previousId)
246 {
247 if (this.EncounteredError)
248 {
249 return;
250 }
251
252 if (parentType != ComplexReferenceParentType.Unknown && parentId != null)
253 {
254 this.CreateWixGroupRow(sourceLineNumbers, parentType, parentId, type, id);
255 }
256
257 if (previousType != ComplexReferenceChildType.Unknown && previousId != null)
258 {
259 // TODO: Should we define our own enum for this, just to ensure there's no "cross-contamination"?
260 // TODO: Also, we could potentially include an 'Attributes' field to track things like
261 // 'before' vs. 'after', and explicit vs. inferred dependencies.
262 this.AddSymbol(new WixOrderingSymbol(sourceLineNumbers)
263 {
264 ItemType = type,
265 ItemIdRef = id,
266 DependsOnType = previousType,
267 DependsOnIdRef = previousId,
268 });
269 }
270 }
271
272 /// <summary>
273 /// Creates a version 3 name-based UUID.
274 /// </summary>
275 /// <param name="namespaceGuid">The namespace UUID.</param>
276 /// <param name="value">The value.</param>
277 /// <returns>The generated GUID for the given namespace and value.</returns>
278 public string CreateGuid(Guid namespaceGuid, string value)
279 {
280 return this.parseHelper.CreateGuid(namespaceGuid, value);
281 }
282
283 /// <summary>
284 /// Creates directories using the inline directory syntax.
285 /// </summary>
286 /// <param name="sourceLineNumbers">Source line information.</param>
287 /// <param name="parentId">Optional identifier of parent directory.</param>
288 /// <param name="inlineSyntax">Optional inline syntax to override attribute's value.</param>
289 /// <returns>Identifier of the leaf directory created.</returns>
290 public string CreateDirectoryReferenceFromInlineSyntax(SourceLineNumber sourceLineNumbers, string parentId, string inlineSyntax = null)
291 {
292 return this.parseHelper.CreateDirectoryReferenceFromInlineSyntax(this.ActiveSection, sourceLineNumbers, attribute: null, parentId, inlineSyntax, this.activeSectionCachedInlinedDirectoryIds);
293 }
294
295 /// <summary>
296 /// Creates a Registry row in the active section.
297 /// </summary>
298 /// <param name="sourceLineNumbers">Source and line number of the current row.</param>
299 /// <param name="root">The registry entry root.</param>
300 /// <param name="key">The registry entry key.</param>
301 /// <param name="name">The registry entry name.</param>
302 /// <param name="value">The registry entry value.</param>
303 /// <param name="componentId">The component which will control installation/uninstallation of the registry entry.</param>
304 public Identifier CreateRegistryStringSymbol(SourceLineNumber sourceLineNumbers, RegistryRootType root, string key, string name, string value, string componentId)
305 {
306 return this.parseHelper.CreateRegistrySymbol(this.ActiveSection, sourceLineNumbers, root, key, name, value, componentId);
307 }
308
309 /// <summary>
310 /// Create a WixSimpleReferenceSymbol in the active section.
311 /// </summary>
312 /// <param name="sourceLineNumbers">Source line information for the row.</param>
313 /// <param name="symbolName">The symbol name of the simple reference.</param>
314 /// <param name="primaryKey">The primary key of the simple reference.</param>
315 public void CreateSimpleReference(SourceLineNumber sourceLineNumbers, string symbolName, string primaryKey)
316 {
317 if (!this.EncounteredError)
318 {
319 var id = String.Concat(symbolName, ":", primaryKey);
320
321 // If this simple reference hasn't been added to the active section already, add it.
322 if (this.activeSectionSimpleReferences.Add(id))
323 {
324 this.parseHelper.CreateSimpleReference(this.ActiveSection, sourceLineNumbers, symbolName, primaryKey);
325 }
326 }
327 }
328
329 /// <summary>
330 /// Create a WixSimpleReferenceSymbol in the active section.
331 /// </summary>
332 /// <param name="sourceLineNumbers">Source line information for the row.</param>
333 /// <param name="symbolName">The symbol name of the simple reference.</param>
334 /// <param name="primaryKeys">The primary keys of the simple reference.</param>
335 public void CreateSimpleReference(SourceLineNumber sourceLineNumbers, string symbolName, params string[] primaryKeys)
336 {
337 if (!this.EncounteredError)
338 {
339 var joinedKeys = String.Join("/", primaryKeys);
340 var id = String.Concat(symbolName, ":", joinedKeys);
341
342 // If this simple reference hasn't been added to the active section already, add it.
343 if (this.activeSectionSimpleReferences.Add(id))
344 {
345 this.parseHelper.CreateSimpleReference(this.ActiveSection, sourceLineNumbers, symbolName, primaryKeys);
346 }
347 }
348 }
349
350 /// <summary>
351 /// Create a WixSimpleReferenceSymbol in the active section.
352 /// </summary>
353 /// <param name="sourceLineNumbers">Source line information for the row.</param>
354 /// <param name="symbolDefinition">The symbol definition of the simple reference.</param>
355 /// <param name="primaryKey">The primary key of the simple reference.</param>
356 public void CreateSimpleReference(SourceLineNumber sourceLineNumbers, IntermediateSymbolDefinition symbolDefinition, string primaryKey)
357 {
358 this.CreateSimpleReference(sourceLineNumbers, symbolDefinition.Name, primaryKey);
359 }
360
361 /// <summary>
362 /// Create a WixSimpleReferenceSymbol in the active section.
363 /// </summary>
364 /// <param name="sourceLineNumbers">Source line information for the row.</param>
365 /// <param name="symbolDefinition">The symbol definition of the simple reference.</param>
366 /// <param name="primaryKeys">The primary keys of the simple reference.</param>
367 public void CreateSimpleReference(SourceLineNumber sourceLineNumbers, IntermediateSymbolDefinition symbolDefinition, params string[] primaryKeys)
368 {
369 this.CreateSimpleReference(sourceLineNumbers, symbolDefinition.Name, primaryKeys);
370 }
371
372 /// <summary>
373 /// A row in the WixGroup table is added for this child node and its parent node.
374 /// </summary>
375 /// <param name="sourceLineNumbers">Source line information for the row.</param>
376 /// <param name="parentType">Type of child's complex reference parent.</param>
377 /// <param name="parentId">Id of the parenet node.</param>
378 /// <param name="childType">Complex reference type of child</param>
379 /// <param name="childId">Id of the Child Node.</param>
380 public void CreateWixGroupRow(SourceLineNumber sourceLineNumbers, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType childType, string childId)
381 {
382 if (!this.EncounteredError)
383 {
384 this.parseHelper.CreateWixGroupSymbol(this.ActiveSection, sourceLineNumbers, parentType, parentId, childType, childId);
385 }
386 }
387
388 /// <summary>
389 /// Add the appropriate symbols to make sure that the given table shows up
390 /// in the resulting output.
391 /// </summary>
392 /// <param name="sourceLineNumbers">Source line numbers.</param>
393 /// <param name="tableName">Name of the table to ensure existance of.</param>
394 public void EnsureTable(SourceLineNumber sourceLineNumbers, string tableName)
395 {
396 if (!this.EncounteredError)
397 {
398 this.parseHelper.EnsureTable(this.ActiveSection, sourceLineNumbers, tableName);
399 }
400 }
401
402 /// <summary>
403 /// Add the appropriate symbols to make sure that the given table shows up
404 /// in the resulting output.
405 /// </summary>
406 /// <param name="sourceLineNumbers">Source line numbers.</param>
407 /// <param name="tableDefinition">Definition of the table to ensure existance of.</param>
408 public void EnsureTable(SourceLineNumber sourceLineNumbers, TableDefinition tableDefinition)
409 {
410 if (!this.EncounteredError)
411 {
412 this.parseHelper.EnsureTable(this.ActiveSection, sourceLineNumbers, tableDefinition);
413 }
414 }
415
416 /// <summary>
417 /// Get an attribute value.
418 /// </summary>
419 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
420 /// <param name="attribute">The attribute containing the value to get.</param>
421 /// <param name="emptyRule">A rule for the contents of the value. If the contents do not follow the rule, an error is thrown.</param>
422 /// <returns>The attribute's value.</returns>
423 public string GetAttributeValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, EmptyRule emptyRule = EmptyRule.CanBeWhitespaceOnly)
424 {
425 return this.parseHelper.GetAttributeValue(sourceLineNumbers, attribute, emptyRule);
426 }
427
428 /// <summary>
429 /// Get a valid code page by web name or number from a string attribute.
430 /// </summary>
431 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
432 /// <param name="attribute">The attribute containing the value to get.</param>
433 /// <returns>A valid code page integer value.</returns>
434 public int GetAttributeCodePageValue(SourceLineNumber sourceLineNumbers, XAttribute attribute)
435 {
436 if (null == attribute)
437 {
438 throw new ArgumentNullException(nameof(attribute));
439 }
440
441 var value = this.GetAttributeValue(sourceLineNumbers, attribute);
442
443 try
444 {
445 return Common.GetValidCodePage(value);
446 }
447 catch (NotSupportedException)
448 {
449 this.Write(ErrorMessages.IllegalCodepageAttribute(sourceLineNumbers, value, attribute.Parent.Name.LocalName, attribute.Name.LocalName));
450 }
451
452 return CompilerConstants.IllegalInteger;
453 }
454
455 /// <summary>
456 /// Get a valid code page by web name or number from a string attribute.
457 /// </summary>
458 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
459 /// <param name="attribute">The attribute containing the value to get.</param>
460 /// <param name="onlyAnsi">Whether to allow Unicode (UCS) or UTF code pages.</param>
461 /// <returns>A valid code page integer value or variable expression.</returns>
462 public string GetAttributeLocalizableCodePageValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool onlyAnsi = false)
463 {
464 if (null == attribute)
465 {
466 throw new ArgumentNullException(nameof(attribute));
467 }
468
469 var value = this.GetAttributeValue(sourceLineNumbers, attribute);
470
471 // Allow for localization of code page names and values.
472 if (this.IsValidLocIdentifier(value))
473 {
474 return value;
475 }
476
477 try
478 {
479 var codePage = Common.GetValidCodePage(value, false, onlyAnsi, sourceLineNumbers);
480 return codePage.ToString(CultureInfo.InvariantCulture);
481 }
482 catch (NotSupportedException)
483 {
484 // Not a valid windows code page.
485 this.messaging.Write(ErrorMessages.IllegalCodepageAttribute(sourceLineNumbers, value, attribute.Parent.Name.LocalName, attribute.Name.LocalName));
486 }
487 catch (WixException e)
488 {
489 this.messaging.Write(e.Error);
490 }
491
492 return null;
493 }
494
495 /// <summary>
496 /// Get an integer attribute value and displays an error for an illegal integer value.
497 /// </summary>
498 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
499 /// <param name="attribute">The attribute containing the value to get.</param>
500 /// <param name="minimum">The minimum legal value.</param>
501 /// <param name="maximum">The maximum legal value.</param>
502 /// <returns>The attribute's integer value or a special value if an error occurred during conversion.</returns>
503 public int GetAttributeIntegerValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, int minimum, int maximum)
504 {
505 return this.parseHelper.GetAttributeIntegerValue(sourceLineNumbers, attribute, minimum, maximum);
506 }
507
508 /// <summary>
509 /// Get an integer attribute value and displays an error for an illegal integer value.
510 /// </summary>
511 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
512 /// <param name="attribute">The attribute containing the value to get.</param>
513 /// <returns>The attribute's integer value or null if an error occurred during conversion.</returns>
514 public int? GetAttributeRawIntegerValue(SourceLineNumber sourceLineNumbers, XAttribute attribute)
515 {
516 return Common.GetAttributeRawIntegerValue(this.messaging, sourceLineNumbers, attribute);
517 }
518
519 /// <summary>
520 /// Get a long integral attribute value and displays an error for an illegal long value.
521 /// </summary>
522 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
523 /// <param name="attribute">The attribute containing the value to get.</param>
524 /// <param name="minimum">The minimum legal value.</param>
525 /// <param name="maximum">The maximum legal value.</param>
526 /// <returns>The attribute's long value or a special value if an error occurred during conversion.</returns>
527 public long GetAttributeLongValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, long minimum, long maximum)
528 {
529 return this.parseHelper.GetAttributeLongValue(sourceLineNumbers, attribute, minimum, maximum);
530 }
531
532 /// <summary>
533 /// Get a date time attribute value and display errors for illegal values.
534 /// </summary>
535 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
536 /// <param name="attribute">The attribute containing the value to get.</param>
537 /// <returns>Int representation of the date time.</returns>
538 public int GetAttributeDateTimeValue(SourceLineNumber sourceLineNumbers, XAttribute attribute)
539 {
540 if (null == attribute)
541 {
542 throw new ArgumentNullException("attribute");
543 }
544
545 string value = this.GetAttributeValue(sourceLineNumbers, attribute);
546
547 if (0 < value.Length)
548 {
549 try
550 {
551 DateTime date = DateTime.Parse(value, CultureInfo.InvariantCulture.DateTimeFormat);
552
553 return ((((date.Year - 1980) * 512) + (date.Month * 32 + date.Day)) * 65536) +
554 (date.Hour * 2048) + (date.Minute * 32) + (date.Second / 2);
555 }
556 catch (ArgumentOutOfRangeException)
557 {
558 this.Write(ErrorMessages.InvalidDateTimeFormat(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
559 }
560 catch (FormatException)
561 {
562 this.Write(ErrorMessages.InvalidDateTimeFormat(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
563 }
564 catch (OverflowException)
565 {
566 this.Write(ErrorMessages.InvalidDateTimeFormat(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
567 }
568 }
569
570 return CompilerConstants.IllegalInteger;
571 }
572
573 /// <summary>
574 /// Get an integer attribute value or localize variable and displays an error for
575 /// an illegal value.
576 /// </summary>
577 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
578 /// <param name="attribute">The attribute containing the value to get.</param>
579 /// <param name="minimum">The minimum legal value.</param>
580 /// <param name="maximum">The maximum legal value.</param>
581 /// <returns>The attribute's integer value or localize variable as a string or a special value if an error occurred during conversion.</returns>
582 public string GetAttributeLocalizableIntegerValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, int minimum, int maximum)
583 {
584 if (null == attribute)
585 {
586 throw new ArgumentNullException("attribute");
587 }
588
589 Debug.Assert(minimum > CompilerConstants.IntegerNotSet && minimum > CompilerConstants.IllegalInteger, "The legal values for this attribute collide with at least one sentinel used during parsing.");
590
591 var value = this.GetAttributeValue(sourceLineNumbers, attribute);
592
593 if (0 < value.Length)
594 {
595 if (this.IsValidLocIdentifier(value) || Common.IsValidBinderVariable(value))
596 {
597 return value;
598 }
599 else
600 {
601 try
602 {
603 var integer = Convert.ToInt32(value, CultureInfo.InvariantCulture.NumberFormat);
604
605 if (CompilerConstants.IntegerNotSet == integer || CompilerConstants.IllegalInteger == integer)
606 {
607 this.Write(ErrorMessages.IntegralValueSentinelCollision(sourceLineNumbers, integer));
608 }
609 else if (minimum > integer || maximum < integer)
610 {
611 this.Write(ErrorMessages.IntegralValueOutOfRange(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, integer, minimum, maximum));
612 integer = CompilerConstants.IllegalInteger;
613 }
614
615 return value;
616 }
617 catch (FormatException)
618 {
619 this.Write(ErrorMessages.IllegalIntegerValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
620 }
621 catch (OverflowException)
622 {
623 this.Write(ErrorMessages.IllegalIntegerValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
624 }
625 }
626 }
627
628 return null;
629 }
630
631 /// <summary>
632 /// Get a guid attribute value and displays an error for an illegal guid value.
633 /// </summary>
634 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
635 /// <param name="attribute">The attribute containing the value to get.</param>
636 /// <param name="generatable">Determines whether the guid can be automatically generated.</param>
637 /// <param name="canBeEmpty">If true, no error is raised on empty value. If false, an error is raised.</param>
638 /// <returns>The attribute's guid value or a special value if an error occurred.</returns>
639 public string GetAttributeGuidValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool generatable = false, bool canBeEmpty = false)
640 {
641 return this.parseHelper.GetAttributeGuidValue(sourceLineNumbers, attribute, generatable, canBeEmpty);
642 }
643
644 /// <summary>
645 /// Get an identifier attribute value and displays an error for an illegal identifier value.
646 /// </summary>
647 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
648 /// <param name="attribute">The attribute containing the value to get.</param>
649 /// <returns>The attribute's identifier value or a special value if an error occurred.</returns>
650 public Identifier GetAttributeIdentifier(SourceLineNumber sourceLineNumbers, XAttribute attribute)
651 {
652 return this.parseHelper.GetAttributeIdentifier(sourceLineNumbers, attribute);
653 }
654
655 /// <summary>
656 /// Get an identifier attribute value and displays an error for an illegal identifier value.
657 /// </summary>
658 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
659 /// <param name="attribute">The attribute containing the value to get.</param>
660 /// <returns>The attribute's identifier value or a special value if an error occurred.</returns>
661 public string GetAttributeIdentifierValue(SourceLineNumber sourceLineNumbers, XAttribute attribute)
662 {
663 return this.parseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attribute);
664 }
665
666 /// <summary>
667 /// Gets a yes/no value and displays an error for an illegal yes/no value.
668 /// </summary>
669 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
670 /// <param name="attribute">The attribute containing the value to get.</param>
671 /// <returns>The attribute's YesNoType value.</returns>
672 public YesNoType GetAttributeYesNoValue(SourceLineNumber sourceLineNumbers, XAttribute attribute)
673 {
674 return this.parseHelper.GetAttributeYesNoValue(sourceLineNumbers, attribute);
675 }
676
677 /// <summary>
678 /// Gets a yes/no/default value and displays an error for an illegal yes/no value.
679 /// </summary>
680 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
681 /// <param name="attribute">The attribute containing the value to get.</param>
682 /// <returns>The attribute's YesNoDefaultType value.</returns>
683 public YesNoDefaultType GetAttributeYesNoDefaultValue(SourceLineNumber sourceLineNumbers, XAttribute attribute)
684 {
685 return this.parseHelper.GetAttributeYesNoDefaultValue(sourceLineNumbers, attribute);
686 }
687
688 /// <summary>
689 /// Gets a short filename value and displays an error for an illegal short filename value.
690 /// </summary>
691 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
692 /// <param name="attribute">The attribute containing the value to get.</param>
693 /// <param name="allowWildcards">true if wildcards are allowed in the filename.</param>
694 /// <returns>The attribute's short filename value.</returns>
695 public string GetAttributeShortFilename(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool allowWildcards = false)
696 {
697 if (null == attribute)
698 {
699 throw new ArgumentNullException("attribute");
700 }
701
702 var value = this.GetAttributeValue(sourceLineNumbers, attribute);
703
704 if (0 < value.Length)
705 {
706 if (!this.parseHelper.IsValidShortFilename(value, allowWildcards)
707 && !Common.ContainsValidBinderVariable(value)
708 && !this.IsValidLocIdentifier(value))
709 {
710 this.Write(ErrorMessages.IllegalShortFilename(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
711 }
712 else if (CompilerCore.IsAmbiguousFilename(value))
713 {
714 this.Write(WarningMessages.AmbiguousFileOrDirectoryName(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
715 }
716 }
717
718 return value;
719 }
720
721 /// <summary>
722 /// Gets a long filename value and displays an error for an illegal long filename value.
723 /// </summary>
724 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
725 /// <param name="attribute">The attribute containing the value to get.</param>
726 /// <param name="allowWildcards">true if wildcards are allowed in the filename.</param>
727 /// <param name="allowRelative">true if relative paths are allowed in the filename.</param>
728 /// <returns>The attribute's long filename value.</returns>
729 public string GetAttributeLongFilename(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool allowWildcards = false, bool allowRelative = false)
730 {
731 return this.parseHelper.GetAttributeLongFilename(sourceLineNumbers, attribute, allowWildcards, allowRelative);
732 }
733
734 /// <summary>
735 /// Gets a version value or possibly a binder variable and displays an error for an illegal version value.
736 /// </summary>
737 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
738 /// <param name="attribute">The attribute containing the value to get.</param>
739 /// <returns>The attribute's version value.</returns>
740 public string GetAttributeVersionValue(SourceLineNumber sourceLineNumbers, XAttribute attribute)
741 {
742 return this.parseHelper.GetAttributeVersionValue(sourceLineNumbers, attribute);
743 }
744
745 /// <summary>
746 /// Gets a RegistryRoot as a MsiInterop.MsidbRegistryRoot value and displays an error for an illegal value.
747 /// </summary>
748 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
749 /// <param name="attribute">The attribute containing the value to get.</param>
750 /// <param name="allowHkmu">Whether HKMU is returned as -1 (true), or treated as an error (false).</param>
751 /// <returns>The attribute's RegisitryRootType value.</returns>
752 public RegistryRootType? GetAttributeRegistryRootValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool allowHkmu)
753 {
754 return this.parseHelper.GetAttributeRegistryRootValue(sourceLineNumbers, attribute, allowHkmu);
755 }
756
757 /// <summary>
758 /// Gets a bundle variable value and displays an error for an illegal value.
759 /// </summary>
760 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
761 /// <param name="attribute">The attribute containing the value to get.</param>
762 /// <returns>The attribute's value.</returns>
763 public Identifier GetAttributeBundleVariableNameIdentifier(SourceLineNumber sourceLineNumbers, XAttribute attribute)
764 {
765 return this.parseHelper.GetAttributeBundleVariableNameIdentifier(sourceLineNumbers, attribute);
766 }
767
768 public string GetAttributeBundleVariableNameValue(SourceLineNumber sourceLineNumbers, XAttribute attribute)
769 {
770 return this.parseHelper.GetAttributeBundleVariableNameValue(sourceLineNumbers, attribute);
771 }
772
773 /// <summary>
774 /// Gets an MsiProperty name value and displays an error for an illegal value.
775 /// </summary>
776 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
777 /// <param name="attribute">The attribute containing the value to get.</param>
778 /// <returns>The attribute's value.</returns>
779 public string GetAttributeMsiPropertyNameValue(SourceLineNumber sourceLineNumbers, XAttribute attribute)
780 {
781 string value = this.GetAttributeValue(sourceLineNumbers, attribute);
782
783 if (0 < value.Length)
784 {
785 this.bundleValidator.ValidateBundleMsiPropertyName(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value);
786 }
787
788 return value;
789 }
790
791 /// <summary>
792 /// Checks if the string contains a property (i.e. "foo[Property]bar")
793 /// </summary>
794 /// <param name="possibleProperty">String to evaluate for properties.</param>
795 /// <returns>True if a property is found in the string.</returns>
796 public bool ContainsProperty(string possibleProperty)
797 {
798 return this.parseHelper.ContainsProperty(possibleProperty);
799 }
800
801 /// <summary>
802 /// Generate an identifier by hashing data from the row.
803 /// </summary>
804 /// <param name="prefix">Three letter or less prefix for generated row identifier.</param>
805 /// <param name="args">Information to hash.</param>
806 /// <returns>The generated identifier.</returns>
807 public Identifier CreateIdentifier(string prefix, params string[] args)
808 {
809 return this.parseHelper.CreateIdentifier(prefix, args);
810 }
811
812 /// <summary>
813 /// Create an identifier based on passed file name
814 /// </summary>
815 /// <param name="filename">File name to generate identifer from</param>
816 /// <returns></returns>
817 public Identifier CreateIdentifierFromFilename(string filename)
818 {
819 return this.parseHelper.CreateIdentifierFromFilename(filename);
820 }
821
822 /// <summary>
823 /// Attempts to use an extension to parse the attribute.
824 /// </summary>
825 /// <param name="element">Element containing attribute to be parsed.</param>
826 /// <param name="attribute">Attribute to be parsed.</param>
827 /// <param name="context">Extra information about the context in which this element is being parsed.</param>
828 public void ParseExtensionAttribute(XElement element, XAttribute attribute, IDictionary<string, string> context = null)
829 {
830 this.parseHelper.ParseExtensionAttribute(this.extensions.Values, this.intermediate, this.ActiveSection, element, attribute, context);
831 }
832
833 /// <summary>
834 /// Attempts to use an extension to parse the element.
835 /// </summary>
836 /// <param name="parentElement">Element containing element to be parsed.</param>
837 /// <param name="element">Element to be parsed.</param>
838 /// <param name="context">Extra information about the context in which this element is being parsed.</param>
839 public void ParseExtensionElement(XElement parentElement, XElement element, IDictionary<string, string> context = null)
840 {
841 this.parseHelper.ParseExtensionElement(this.extensions.Values, this.intermediate, this.ActiveSection, parentElement, element, context);
842 }
843
844 /// <summary>
845 /// Process all children of the element looking for extensions and erroring on the unexpected.
846 /// </summary>
847 /// <param name="element">Element to parse children.</param>
848 /// <param name="context">Extra information about the context in which this element is being parsed.</param>
849 public void ParseForExtensionElements(XElement element, IDictionary<string, string> context = null)
850 {
851 this.parseHelper.ParseForExtensionElements(this.extensions.Values, this.intermediate, this.ActiveSection, element, context);
852 }
853
854 /// <summary>
855 /// Attempts to use an extension to parse the element, with support for setting component keypath.
856 /// </summary>
857 /// <param name="parentElement">Element containing element to be parsed.</param>
858 /// <param name="element">Element to be parsed.</param>
859 /// <param name="context">Extra information about the context in which this element is being parsed.</param>
860 public IComponentKeyPath ParsePossibleKeyPathExtensionElement(XElement parentElement, XElement element, IDictionary<string, string> context)
861 {
862 return this.parseHelper.ParsePossibleKeyPathExtensionElement(this.extensions.Values, this.intermediate, this.ActiveSection, parentElement, element, context);
863 }
864
865 /// <summary>
866 /// Displays an unexpected attribute error if the attribute is not the namespace attribute.
867 /// </summary>
868 /// <param name="element">Element containing unexpected attribute.</param>
869 /// <param name="attribute">The unexpected attribute.</param>
870 public void UnexpectedAttribute(XElement element, XAttribute attribute)
871 {
872 this.parseHelper.UnexpectedAttribute(element, attribute);
873 }
874
875 /// <summary>
876 /// Display an unexepected element error.
877 /// </summary>
878 /// <param name="parentElement">The parent element.</param>
879 /// <param name="childElement">The unexpected child element.</param>
880 public void UnexpectedElement(XElement parentElement, XElement childElement)
881 {
882 this.parseHelper.UnexpectedElement(parentElement, childElement);
883 }
884
885 /// <summary>
886 /// Sends a message.
887 /// </summary>
888 /// <param name="message">Message to write.</param>
889 public void Write(Message message)
890 {
891 this.messaging.Write(message);
892 }
893
894 /// <summary>
895 /// Verifies that the calling assembly version is equal to or newer than the given <paramref name="requiredVersion"/>.
896 /// </summary>
897 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
898 /// <param name="requiredVersion">The version required of the calling assembly.</param>
899 internal void VerifyRequiredVersion(SourceLineNumber sourceLineNumbers, string requiredVersion)
900 {
901 // an null or empty string means any version will work
902 if (!String.IsNullOrEmpty(requiredVersion))
903 {
904 Assembly caller = Assembly.GetCallingAssembly();
905 AssemblyName name = caller.GetName();
906 FileVersionInfo fv = FileVersionInfo.GetVersionInfo(caller.Location);
907
908 Version versionRequired = new Version(requiredVersion);
909 Version versionCurrent = new Version(fv.FileVersion);
910
911 if (versionRequired > versionCurrent)
912 {
913 if (this.GetType().Assembly.Equals(caller))
914 {
915 this.Write(ErrorMessages.InsufficientVersion(sourceLineNumbers, versionCurrent, versionRequired));
916 }
917 else
918 {
919 this.Write(ErrorMessages.InsufficientVersion(sourceLineNumbers, versionCurrent, versionRequired, name.Name));
920 }
921 }
922 }
923 }
924
925 /// <summary>
926 /// Creates a new section and makes it the active section in the core.
927 /// </summary>
928 /// <param name="id">Unique identifier for the section.</param>
929 /// <param name="type">Type of section to create.</param>
930 /// <param name="compilationId">Unique identifier for the compilation.</param>
931 /// <returns>New section.</returns>
932 internal IntermediateSection CreateActiveSection(string id, SectionType type, string compilationId)
933 {
934 this.ActiveSection = this.CreateSection(id, type, compilationId);
935
936 this.activeSectionCachedInlinedDirectoryIds = new Dictionary<string, string>();
937 this.activeSectionSimpleReferences = new HashSet<string>();
938
939 return this.ActiveSection;
940 }
941
942 /// <summary>
943 /// Creates a new section.
944 /// </summary>
945 /// <param name="id">Unique identifier for the section.</param>
946 /// <param name="type">Type of section to create.</param>
947 /// <param name="compilationId">Unique identifier for the compilation.</param>
948 /// <returns>New section.</returns>
949 internal IntermediateSection CreateSection(string id, SectionType type, string compilationId)
950 {
951 var section = new IntermediateSection(id, type, compilationId);
952
953 this.intermediate.AddSection(section);
954
955 return section;
956 }
957
958 /// <summary>
959 /// Creates WixComplexReference and WixGroup rows in the active section.
960 /// </summary>
961 /// <param name="sourceLineNumbers">Source line information.</param>
962 /// <param name="parentType">The parent type.</param>
963 /// <param name="parentId">The parent id.</param>
964 /// <param name="parentLanguage">The parent language.</param>
965 /// <param name="childType">The child type.</param>
966 /// <param name="childId">The child id.</param>
967 /// <param name="isPrimary">Whether the child is primary.</param>
968 public void CreateComplexReference(SourceLineNumber sourceLineNumbers, ComplexReferenceParentType parentType, string parentId, string parentLanguage, ComplexReferenceChildType childType, string childId, bool isPrimary)
969 {
970 this.parseHelper.CreateComplexReference(this.ActiveSection, sourceLineNumbers, parentType, parentId, parentLanguage, childType, childId, isPrimary);
971 }
972
973 /// <summary>
974 /// Creates a directory row from a name.
975 /// </summary>
976 /// <param name="sourceLineNumbers">Source line information.</param>
977 /// <param name="id">Optional identifier for the new row.</param>
978 /// <param name="parentId">Optional identifier for the parent row.</param>
979 /// <param name="name">Long name of the directory.</param>
980 /// <param name="shortName">Optional short name of the directory.</param>
981 /// <param name="sourceName">Optional source name for the directory.</param>
982 /// <param name="shortSourceName">Optional short source name for the directory.</param>
983 /// <returns>Identifier for the newly created row.</returns>
984 internal Identifier CreateDirectorySymbol(SourceLineNumber sourceLineNumbers, Identifier id, string parentId, string name, string shortName = null, string sourceName = null, string shortSourceName = null)
985 {
986 return this.parseHelper.CreateDirectorySymbol(this.ActiveSection, sourceLineNumbers, id, parentId, name, shortName, sourceName, shortSourceName);
987 }
988
989 public void CreateWixSearchSymbol(SourceLineNumber sourceLineNumbers, string elementName, Identifier id, string variable, string condition, string after)
990 {
991 this.parseHelper.CreateWixSearchSymbol(this.ActiveSection, sourceLineNumbers, elementName, id, variable, condition, after, null);
992 }
993
994 internal WixActionSymbol ScheduleActionSymbol(SourceLineNumber sourceLineNumbers, AccessModifier access, SequenceTable sequence, string actionName, string condition = null, string beforeAction = null, string afterAction = null, bool overridable = false)
995 {
996 return this.parseHelper.ScheduleActionSymbol(this.ActiveSection, sourceLineNumbers, access, sequence, actionName, condition, beforeAction, afterAction, overridable);
997 }
998 }
999 }