aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core/Link
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Core/Link')
-rw-r--r--src/WixToolset.Core/Link/ConnectToFeature.cs95
-rw-r--r--src/WixToolset.Core/Link/ConnectToFeatureCollection.cs92
-rw-r--r--src/WixToolset.Core/Link/ConnectToModule.cs54
-rw-r--r--src/WixToolset.Core/Link/ConnectToModuleCollection.cs92
-rw-r--r--src/WixToolset.Core/Link/FindEntrySectionAndLoadSymbolsCommand.cs109
-rw-r--r--src/WixToolset.Core/Link/ReportConflictingSymbolsCommand.cs49
-rw-r--r--src/WixToolset.Core/Link/ResolveReferencesCommand.cs178
-rw-r--r--src/WixToolset.Core/Link/WixGroupingOrdering.cs726
8 files changed, 1395 insertions, 0 deletions
diff --git a/src/WixToolset.Core/Link/ConnectToFeature.cs b/src/WixToolset.Core/Link/ConnectToFeature.cs
new file mode 100644
index 00000000..6e046b89
--- /dev/null
+++ b/src/WixToolset.Core/Link/ConnectToFeature.cs
@@ -0,0 +1,95 @@
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.Link
4{
5 using System.Collections.Specialized;
6 using WixToolset.Data;
7
8 /// <summary>
9 /// Object that connects things (components/modules) to features.
10 /// </summary>
11 public sealed class ConnectToFeature
12 {
13 private Section section;
14 private string childId;
15
16 private string primaryFeature;
17 private bool explicitPrimaryFeature;
18 private StringCollection connectFeatures;
19
20 /// <summary>
21 /// Creates a new connect to feature.
22 /// </summary>
23 /// <param name="section">Section this connect belongs to.</param>
24 /// <param name="childId">Id of the child.</param>
25 public ConnectToFeature(Section section, string childId) :
26 this(section, childId, null, false)
27 {
28 }
29
30 /// <summary>
31 /// Creates a new connect to feature.
32 /// </summary>
33 /// <param name="section">Section this connect belongs to.</param>
34 /// <param name="childId">Id of the child.</param>
35 /// <param name="primaryFeature">Sets the primary feature for the connection.</param>
36 /// <param name="explicitPrimaryFeature">Sets if this is explicit primary.</param>
37 public ConnectToFeature(Section section, string childId, string primaryFeature, bool explicitPrimaryFeature)
38 {
39 this.section = section;
40 this.childId = childId;
41
42 this.primaryFeature = primaryFeature;
43 this.explicitPrimaryFeature = explicitPrimaryFeature;
44
45 this.connectFeatures = new StringCollection();
46 }
47
48 /// <summary>
49 /// Gets the section.
50 /// </summary>
51 /// <value>Section.</value>
52 public Section Section
53 {
54 get { return this.section; }
55 }
56
57 /// <summary>
58 /// Gets the child identifier.
59 /// </summary>
60 /// <value>The child identifier.</value>
61 public string ChildId
62 {
63 get { return this.childId; }
64 }
65
66 /// <summary>
67 /// Gets or sets if the flag for if the primary feature was set explicitly.
68 /// </summary>
69 /// <value>The flag for if the primary feature was set explicitly.</value>
70 public bool IsExplicitPrimaryFeature
71 {
72 get { return this.explicitPrimaryFeature; }
73 set { this.explicitPrimaryFeature = value; }
74 }
75
76 /// <summary>
77 /// Gets or sets the primary feature.
78 /// </summary>
79 /// <value>The primary feature.</value>
80 public string PrimaryFeature
81 {
82 get { return this.primaryFeature; }
83 set { this.primaryFeature = value; }
84 }
85
86 /// <summary>
87 /// Gets the features connected to.
88 /// </summary>
89 /// <value>Features connected to.</value>
90 public StringCollection ConnectFeatures
91 {
92 get { return this.connectFeatures; }
93 }
94 }
95}
diff --git a/src/WixToolset.Core/Link/ConnectToFeatureCollection.cs b/src/WixToolset.Core/Link/ConnectToFeatureCollection.cs
new file mode 100644
index 00000000..8dd0d22c
--- /dev/null
+++ b/src/WixToolset.Core/Link/ConnectToFeatureCollection.cs
@@ -0,0 +1,92 @@
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.Link
4{
5 using System;
6 using System.Collections;
7
8 /// <summary>
9 /// Hash collection of connect to feature objects.
10 /// </summary>
11 public sealed class ConnectToFeatureCollection : ICollection
12 {
13 private Hashtable collection;
14
15 /// <summary>
16 /// Instantiate a new ConnectToFeatureCollection class.
17 /// </summary>
18 public ConnectToFeatureCollection()
19 {
20 this.collection = new Hashtable();
21 }
22
23 /// <summary>
24 /// Gets the number of items in the collection.
25 /// </summary>
26 /// <value>Number of items in collection.</value>
27 public int Count
28 {
29 get { return this.collection.Count; }
30 }
31
32 /// <summary>
33 /// Gets if the collection has been synchronized.
34 /// </summary>
35 /// <value>True if the collection has been synchronized.</value>
36 public bool IsSynchronized
37 {
38 get { return this.collection.IsSynchronized; }
39 }
40
41 /// <summary>
42 /// Gets the object used to synchronize the collection.
43 /// </summary>
44 /// <value>Oject used the synchronize the collection.</value>
45 public object SyncRoot
46 {
47 get { return this.collection.SyncRoot; }
48 }
49
50 /// <summary>
51 /// Gets a feature connection by child id.
52 /// </summary>
53 /// <param name="childId">Identifier of child to locate.</param>
54 public ConnectToFeature this[string childId]
55 {
56 get { return (ConnectToFeature)this.collection[childId]; }
57 }
58
59 /// <summary>
60 /// Adds a feature connection to the collection.
61 /// </summary>
62 /// <param name="connection">Feature connection to add.</param>
63 public void Add(ConnectToFeature connection)
64 {
65 if (null == connection)
66 {
67 throw new ArgumentNullException("connection");
68 }
69
70 this.collection.Add(connection.ChildId, connection);
71 }
72
73 /// <summary>
74 /// Copies the collection into an array.
75 /// </summary>
76 /// <param name="array">Array to copy the collection into.</param>
77 /// <param name="index">Index to start copying from.</param>
78 public void CopyTo(System.Array array, int index)
79 {
80 this.collection.CopyTo(array, index);
81 }
82
83 /// <summary>
84 /// Gets enumerator for the collection.
85 /// </summary>
86 /// <returns>Enumerator for the collection.</returns>
87 public IEnumerator GetEnumerator()
88 {
89 return this.collection.Values.GetEnumerator();
90 }
91 }
92}
diff --git a/src/WixToolset.Core/Link/ConnectToModule.cs b/src/WixToolset.Core/Link/ConnectToModule.cs
new file mode 100644
index 00000000..d6a8338e
--- /dev/null
+++ b/src/WixToolset.Core/Link/ConnectToModule.cs
@@ -0,0 +1,54 @@
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.Link
4{
5 /// <summary>
6 /// Object that connects things to modules.
7 /// </summary>
8 public sealed class ConnectToModule
9 {
10 private string childId;
11 private string module;
12 private string moduleLanguage;
13
14 /// <summary>
15 /// Creates a new connect to module.
16 /// </summary>
17 /// <param name="childId">Id of the child.</param>
18 /// <param name="module">Id of the module.</param>
19 /// <param name="moduleLanguage">Language of the module.</param>
20 public ConnectToModule(string childId, string module, string moduleLanguage)
21 {
22 this.childId = childId;
23 this.module = module;
24 this.moduleLanguage = moduleLanguage;
25 }
26
27 /// <summary>
28 /// Gets the id of the child.
29 /// </summary>
30 /// <value>Child identifier.</value>
31 public string ChildId
32 {
33 get { return this.childId; }
34 }
35
36 /// <summary>
37 /// Gets the id of the module.
38 /// </summary>
39 /// <value>The id of the module.</value>
40 public string Module
41 {
42 get { return this.module; }
43 }
44
45 /// <summary>
46 /// Gets the language of the module.
47 /// </summary>
48 /// <value>The language of the module.</value>
49 public string ModuleLanguage
50 {
51 get { return this.moduleLanguage; }
52 }
53 }
54}
diff --git a/src/WixToolset.Core/Link/ConnectToModuleCollection.cs b/src/WixToolset.Core/Link/ConnectToModuleCollection.cs
new file mode 100644
index 00000000..6595487f
--- /dev/null
+++ b/src/WixToolset.Core/Link/ConnectToModuleCollection.cs
@@ -0,0 +1,92 @@
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.Link
4{
5 using System;
6 using System.Collections;
7
8 /// <summary>
9 /// Hash collection of connect to module objects.
10 /// </summary>
11 public sealed class ConnectToModuleCollection : ICollection
12 {
13 private Hashtable collection;
14
15 /// <summary>
16 /// Instantiate a new ConnectToModuleCollection class.
17 /// </summary>
18 public ConnectToModuleCollection()
19 {
20 this.collection = new Hashtable();
21 }
22
23 /// <summary>
24 /// Gets the number of elements actually contained in the ConnectToModuleCollection.
25 /// </summary>
26 /// <value>The number of elements actually contained in the ConnectToModuleCollection.</value>
27 public int Count
28 {
29 get { return this.collection.Count; }
30 }
31
32 /// <summary>
33 /// Gets a value indicating whether access to the ConnectToModuleCollection is synchronized (thread-safe).
34 /// </summary>
35 /// <value>true if access to the ConnectToModuleCollection is synchronized (thread-safe); otherwise, false. The default is false.</value>
36 public bool IsSynchronized
37 {
38 get { return this.collection.IsSynchronized; }
39 }
40
41 /// <summary>
42 /// Gets an object that can be used to synchronize access to the ConnectToModuleCollection.
43 /// </summary>
44 /// <value>An object that can be used to synchronize access to the ConnectToModuleCollection.</value>
45 public object SyncRoot
46 {
47 get { return this.collection.SyncRoot; }
48 }
49
50 /// <summary>
51 /// Gets a module connection by child id.
52 /// </summary>
53 /// <param name="childId">Identifier of child to locate.</param>
54 public ConnectToModule this[string childId]
55 {
56 get { return (ConnectToModule)this.collection[childId]; }
57 }
58
59 /// <summary>
60 /// Adds a module connection to the collection.
61 /// </summary>
62 /// <param name="connection">Module connection to add.</param>
63 public void Add(ConnectToModule connection)
64 {
65 if (null == connection)
66 {
67 throw new ArgumentNullException("connection");
68 }
69
70 this.collection.Add(connection.ChildId, connection);
71 }
72
73 /// <summary>
74 /// Copies the entire ConnectToModuleCollection to a compatible one-dimensional Array, starting at the specified index of the target array.
75 /// </summary>
76 /// <param name="array">The one-dimensional Array that is the destination of the elements copied from this ConnectToModuleCollection. The Array must have zero-based indexing.</param>
77 /// <param name="index">The zero-based index in array at which copying begins.</param>
78 public void CopyTo(System.Array array, int index)
79 {
80 this.collection.Keys.CopyTo(array, index);
81 }
82
83 /// <summary>
84 /// Returns an enumerator for the entire ConnectToModuleCollection.
85 /// </summary>
86 /// <returns>An IEnumerator for the entire ConnectToModuleCollection.</returns>
87 public IEnumerator GetEnumerator()
88 {
89 return this.collection.Keys.GetEnumerator();
90 }
91 }
92}
diff --git a/src/WixToolset.Core/Link/FindEntrySectionAndLoadSymbolsCommand.cs b/src/WixToolset.Core/Link/FindEntrySectionAndLoadSymbolsCommand.cs
new file mode 100644
index 00000000..effb06e4
--- /dev/null
+++ b/src/WixToolset.Core/Link/FindEntrySectionAndLoadSymbolsCommand.cs
@@ -0,0 +1,109 @@
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.Link
4{
5 using System;
6 using System.Collections.Generic;
7 using System.Linq;
8 using WixToolset.Data;
9
10 internal class FindEntrySectionAndLoadSymbolsCommand : ICommand
11 {
12 private IEnumerable<Section> sections;
13
14 public FindEntrySectionAndLoadSymbolsCommand(IEnumerable<Section> sections)
15 {
16 this.sections = sections;
17 }
18
19 /// <summary>
20 /// Sets the expected entry output type, based on output file extension provided to the linker.
21 /// </summary>
22 public OutputType ExpectedOutputType { private get; set; }
23
24 /// <summary>
25 /// Gets the located entry section after the command is executed.
26 /// </summary>
27 public Section EntrySection { get; private set; }
28
29 /// <summary>
30 /// Gets the collection of loaded symbols.
31 /// </summary>
32 public IDictionary<string, Symbol> Symbols { get; private set; }
33
34 public IEnumerable<Symbol> PossiblyConflictingSymbols { get; private set; }
35
36 public void Execute()
37 {
38 Dictionary<string, Symbol> symbols = new Dictionary<string, Symbol>();
39 HashSet<Symbol> possibleConflicts = new HashSet<Symbol>();
40
41 SectionType expectedEntrySectionType;
42 if (!Enum.TryParse<SectionType>(this.ExpectedOutputType.ToString(), out expectedEntrySectionType))
43 {
44 expectedEntrySectionType = SectionType.Unknown;
45 }
46
47 foreach (Section section in this.sections)
48 {
49 // Try to find the one and only entry section.
50 if (SectionType.Product == section.Type || SectionType.Module == section.Type || SectionType.PatchCreation == section.Type || SectionType.Patch == section.Type || SectionType.Bundle == section.Type)
51 {
52 if (SectionType.Unknown != expectedEntrySectionType && section.Type != expectedEntrySectionType)
53 {
54 string outputExtension = Output.GetExtension(this.ExpectedOutputType);
55 Messaging.Instance.OnMessage(WixWarnings.UnexpectedEntrySection(section.SourceLineNumbers, section.Type.ToString(), expectedEntrySectionType.ToString(), outputExtension));
56 }
57
58 if (null == this.EntrySection)
59 {
60 this.EntrySection = section;
61 }
62 else
63 {
64 Messaging.Instance.OnMessage(WixErrors.MultipleEntrySections(this.EntrySection.SourceLineNumbers, this.EntrySection.Id, section.Id));
65 Messaging.Instance.OnMessage(WixErrors.MultipleEntrySections2(section.SourceLineNumbers));
66 }
67 }
68
69 // Load all the symbols from the section's tables that create symbols.
70 foreach (Table table in section.Tables.Where(t => t.Definition.CreateSymbols))
71 {
72 foreach (Row row in table.Rows)
73 {
74 Symbol symbol = new Symbol(row);
75
76 Symbol existingSymbol;
77 if (!symbols.TryGetValue(symbol.Name, out existingSymbol))
78 {
79 symbols.Add(symbol.Name, symbol);
80 }
81 else // uh-oh, duplicate symbols.
82 {
83 // If the duplicate symbols are both private directories, there is a chance that they
84 // point to identical rows. Identical directory rows are redundant and will not cause
85 // conflicts.
86 if (AccessModifier.Private == existingSymbol.Access && AccessModifier.Private == symbol.Access &&
87 "Directory" == existingSymbol.Row.Table.Name && existingSymbol.Row.IsIdentical(symbol.Row))
88 {
89 // Ensure identical symbol's row is marked redundant to ensure (should the row be
90 // referenced into the final output) it will not add duplicate primary keys during
91 // the .IDT importing.
92 symbol.Row.Redundant = true;
93 existingSymbol.AddRedundant(symbol);
94 }
95 else
96 {
97 existingSymbol.AddPossibleConflict(symbol);
98 possibleConflicts.Add(existingSymbol);
99 }
100 }
101 }
102 }
103 }
104
105 this.Symbols = symbols;
106 this.PossiblyConflictingSymbols = possibleConflicts;
107 }
108 }
109}
diff --git a/src/WixToolset.Core/Link/ReportConflictingSymbolsCommand.cs b/src/WixToolset.Core/Link/ReportConflictingSymbolsCommand.cs
new file mode 100644
index 00000000..39c3a5c2
--- /dev/null
+++ b/src/WixToolset.Core/Link/ReportConflictingSymbolsCommand.cs
@@ -0,0 +1,49 @@
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.Link
4{
5 using System.Collections.Generic;
6 using System.Linq;
7 using WixToolset.Data;
8
9 public class ReportConflictingSymbolsCommand : ICommand
10 {
11 private IEnumerable<Symbol> possibleConflicts;
12 private IEnumerable<Section> resolvedSections;
13
14 public ReportConflictingSymbolsCommand(IEnumerable<Symbol> possibleConflicts, IEnumerable<Section> resolvedSections)
15 {
16 this.possibleConflicts = possibleConflicts;
17 this.resolvedSections = resolvedSections;
18 }
19
20 public void Execute()
21 {
22 // Do a quick check if there are any possibly conflicting symbols that don't come from tables that allow
23 // overriding. Hopefully the symbols with possible conflicts list is usually very short list (empty should
24 // be the most common). If we find any matches, we'll do a more costly check to see if the possible conflicting
25 // symbols are in sections we actually referenced. From the resulting set, show an error for each duplicate
26 // (aka: conflicting) symbol. This should catch any rows with colliding primary keys (since symbols are based
27 // on the primary keys of rows).
28 List<Symbol> illegalDuplicates = possibleConflicts.Where(s => "WixAction" != s.Row.Table.Name && "WixVariable" != s.Row.Table.Name).ToList();
29 if (0 < illegalDuplicates.Count)
30 {
31 HashSet<Section> referencedSections = new HashSet<Section>(resolvedSections);
32 foreach (Symbol referencedDuplicateSymbol in illegalDuplicates.Where(s => referencedSections.Contains(s.Section)))
33 {
34 List<Symbol> actuallyReferencedDuplicateSymbols = referencedDuplicateSymbol.PossiblyConflictingSymbols.Where(s => referencedSections.Contains(s.Section)).ToList();
35
36 if (actuallyReferencedDuplicateSymbols.Any())
37 {
38 Messaging.Instance.OnMessage(WixErrors.DuplicateSymbol(referencedDuplicateSymbol.Row.SourceLineNumbers, referencedDuplicateSymbol.Name));
39
40 foreach (Symbol duplicate in actuallyReferencedDuplicateSymbols)
41 {
42 Messaging.Instance.OnMessage(WixErrors.DuplicateSymbol2(duplicate.Row.SourceLineNumbers));
43 }
44 }
45 }
46 }
47 }
48 }
49}
diff --git a/src/WixToolset.Core/Link/ResolveReferencesCommand.cs b/src/WixToolset.Core/Link/ResolveReferencesCommand.cs
new file mode 100644
index 00000000..5a985f3f
--- /dev/null
+++ b/src/WixToolset.Core/Link/ResolveReferencesCommand.cs
@@ -0,0 +1,178 @@
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.Link
4{
5 using System;
6 using System.Collections.Generic;
7 using System.Diagnostics;
8 using System.Linq;
9 using WixToolset.Data;
10 using WixToolset.Data.Rows;
11
12 /// <summary>
13 /// Resolves all the simple references in a section.
14 /// </summary>
15 internal class ResolveReferencesCommand : ICommand
16 {
17 private Section entrySection;
18 private IDictionary<string, Symbol> symbols;
19 private HashSet<Symbol> referencedSymbols;
20 private HashSet<Section> resolvedSections;
21
22 public ResolveReferencesCommand(Section entrySection, IDictionary<string, Symbol> symbols)
23 {
24 this.entrySection = entrySection;
25 this.symbols = symbols;
26 }
27
28 public bool BuildingMergeModule { private get; set; }
29
30 public IEnumerable<Symbol> ReferencedSymbols { get { return this.referencedSymbols; } }
31
32 public IEnumerable<Section> ResolvedSections { get { return this.resolvedSections; } }
33
34 /// <summary>
35 /// Resolves all the simple references in a section.
36 /// </summary>
37 public void Execute()
38 {
39 this.resolvedSections = new HashSet<Section>();
40 this.referencedSymbols = new HashSet<Symbol>();
41
42 this.RecursivelyResolveReferences(this.entrySection);
43 }
44
45 /// <summary>
46 /// Recursive helper function to resolve all references of passed in section.
47 /// </summary>
48 /// <param name="section">Section with references to resolve.</param>
49 /// <remarks>Note: recursive function.</remarks>
50 private void RecursivelyResolveReferences(Section section)
51 {
52 // If we already resolved this section, move on to the next.
53 if (!this.resolvedSections.Add(section))
54 {
55 return;
56 }
57
58 // Process all of the references contained in this section using the collection of
59 // symbols provided. Then recursively call this method to process the
60 // located symbol's section. All in all this is a very simple depth-first
61 // search of the references per-section.
62 Table wixSimpleReferenceTable;
63 if (section.Tables.TryGetTable("WixSimpleReference", out wixSimpleReferenceTable))
64 {
65 foreach (WixSimpleReferenceRow wixSimpleReferenceRow in wixSimpleReferenceTable.Rows)
66 {
67 Debug.Assert(wixSimpleReferenceRow.Section == section);
68
69 // If we're building a Merge Module, ignore all references to the Media table
70 // because Merge Modules don't have Media tables.
71 if (this.BuildingMergeModule && "Media" == wixSimpleReferenceRow.TableName)
72 {
73 continue;
74 }
75
76 Symbol symbol;
77 if (!this.symbols.TryGetValue(wixSimpleReferenceRow.SymbolicName, out symbol))
78 {
79 Messaging.Instance.OnMessage(WixErrors.UnresolvedReference(wixSimpleReferenceRow.SourceLineNumbers, wixSimpleReferenceRow.SymbolicName));
80 }
81 else // see if the symbol (and any of its duplicates) are appropriately accessible.
82 {
83 IList<Symbol> accessible = DetermineAccessibleSymbols(section, symbol);
84 if (!accessible.Any())
85 {
86 Messaging.Instance.OnMessage(WixErrors.UnresolvedReference(wixSimpleReferenceRow.SourceLineNumbers, wixSimpleReferenceRow.SymbolicName, symbol.Access));
87 }
88 else if (1 == accessible.Count)
89 {
90 Symbol accessibleSymbol = accessible[0];
91 this.referencedSymbols.Add(accessibleSymbol);
92
93 if (null != accessibleSymbol.Section)
94 {
95 RecursivelyResolveReferences(accessibleSymbol.Section);
96 }
97 }
98 else // display errors for the duplicate symbols.
99 {
100 Symbol accessibleSymbol = accessible[0];
101 string referencingSourceLineNumber = wixSimpleReferenceRow.SourceLineNumbers.ToString();
102 if (String.IsNullOrEmpty(referencingSourceLineNumber))
103 {
104 Messaging.Instance.OnMessage(WixErrors.DuplicateSymbol(accessibleSymbol.Row.SourceLineNumbers, accessibleSymbol.Name));
105 }
106 else
107 {
108 Messaging.Instance.OnMessage(WixErrors.DuplicateSymbol(accessibleSymbol.Row.SourceLineNumbers, accessibleSymbol.Name, referencingSourceLineNumber));
109 }
110
111 foreach (Symbol accessibleDuplicate in accessible.Skip(1))
112 {
113 Messaging.Instance.OnMessage(WixErrors.DuplicateSymbol2(accessibleDuplicate.Row.SourceLineNumbers));
114 }
115 }
116 }
117 }
118 }
119 }
120
121 /// <summary>
122 /// Determine if the symbol and any of its duplicates are accessbile by referencing section.
123 /// </summary>
124 /// <param name="referencingSection">Section referencing the symbol.</param>
125 /// <param name="symbol">Symbol being referenced.</param>
126 /// <returns>List of symbols accessible by referencing section.</returns>
127 private IList<Symbol> DetermineAccessibleSymbols(Section referencingSection, Symbol symbol)
128 {
129 List<Symbol> symbols = new List<Symbol>();
130
131 if (AccessibleSymbol(referencingSection, symbol))
132 {
133 symbols.Add(symbol);
134 }
135
136 foreach (Symbol dupe in symbol.PossiblyConflictingSymbols)
137 {
138 if (AccessibleSymbol(referencingSection, dupe))
139 {
140 symbols.Add(dupe);
141 }
142 }
143
144 foreach (Symbol dupe in symbol.RedundantSymbols)
145 {
146 if (AccessibleSymbol(referencingSection, dupe))
147 {
148 symbols.Add(dupe);
149 }
150 }
151
152 return symbols;
153 }
154
155 /// <summary>
156 /// Determine if a single symbol is accessible by the referencing section.
157 /// </summary>
158 /// <param name="referencingSection">Section referencing the symbol.</param>
159 /// <param name="symbol">Symbol being referenced.</param>
160 /// <returns>True if symbol is accessible.</returns>
161 private bool AccessibleSymbol(Section referencingSection, Symbol symbol)
162 {
163 switch (symbol.Access)
164 {
165 case AccessModifier.Public:
166 return true;
167 case AccessModifier.Internal:
168 return symbol.Row.Section.IntermediateId.Equals(referencingSection.IntermediateId) || (null != symbol.Row.Section.LibraryId && symbol.Row.Section.LibraryId.Equals(referencingSection.LibraryId));
169 case AccessModifier.Protected:
170 return symbol.Row.Section.IntermediateId.Equals(referencingSection.IntermediateId);
171 case AccessModifier.Private:
172 return referencingSection == symbol.Section;
173 default:
174 throw new InvalidOperationException();
175 }
176 }
177 }
178}
diff --git a/src/WixToolset.Core/Link/WixGroupingOrdering.cs b/src/WixToolset.Core/Link/WixGroupingOrdering.cs
new file mode 100644
index 00000000..fc0ce43b
--- /dev/null
+++ b/src/WixToolset.Core/Link/WixGroupingOrdering.cs
@@ -0,0 +1,726 @@
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.Link
4{
5 using System;
6 using System.Collections;
7 using System.Collections.ObjectModel;
8 using System.Collections.Generic;
9 using System.Diagnostics;
10 using System.Globalization;
11 using System.Linq;
12 using System.Text;
13 using WixToolset.Extensibility;
14 using WixToolset.Data;
15
16 /// <summary>
17 /// Grouping and Ordering class of the WiX toolset.
18 /// </summary>
19 internal sealed class WixGroupingOrdering : IMessageHandler
20 {
21 private Output output;
22 private IMessageHandler messageHandler;
23 private List<string> groupTypes;
24 private List<string> itemTypes;
25 private ItemCollection items;
26 private List<int> rowsUsed;
27 private bool loaded;
28 private bool encounteredError;
29
30 /// <summary>
31 /// Creates a WixGroupingOrdering object.
32 /// </summary>
33 /// <param name="output">Output from which to read the group and order information.</param>
34 /// <param name="messageHandler">Handler for any error messages.</param>
35 /// <param name="groupTypes">Group types to include.</param>
36 /// <param name="itemTypes">Item types to include.</param>
37 public WixGroupingOrdering(Output output, IMessageHandler messageHandler)
38 {
39 this.output = output;
40 this.messageHandler = messageHandler;
41
42 this.rowsUsed = new List<int>();
43 this.loaded = false;
44 this.encounteredError = false;
45 }
46
47 /// <summary>
48 /// Switches a WixGroupingOrdering object to operate on a new set of groups/items.
49 /// </summary>
50 /// <param name="groupTypes">Group types to include.</param>
51 /// <param name="itemTypes">Item types to include.</param>
52 public void UseTypes(IEnumerable<string> groupTypes, IEnumerable<string> itemTypes)
53 {
54 this.groupTypes = new List<string>(groupTypes);
55 this.itemTypes = new List<string>(itemTypes);
56
57 this.items = new ItemCollection();
58 this.loaded = false;
59 }
60
61 /// <summary>
62 /// Sends a message to the message handler if there is one.
63 /// </summary>
64 /// <param name="mea">Message event arguments.</param>
65 public void OnMessage(MessageEventArgs e)
66 {
67 WixErrorEventArgs errorEventArgs = e as WixErrorEventArgs;
68
69 if (null != errorEventArgs || MessageLevel.Error == e.Level)
70 {
71 this.encounteredError = true;
72 }
73
74 if (null != this.messageHandler)
75 {
76 this.messageHandler.OnMessage(e);
77 }
78 else if (null != errorEventArgs)
79 {
80 throw new WixException(errorEventArgs);
81 }
82 }
83
84 /// <summary>
85 /// Finds all nested items under a parent group and creates new WixGroup data for them.
86 /// </summary>
87 /// <param name="parentType">The group type for the parent group to flatten.</param>
88 /// <param name="parentId">The identifier of the parent group to flatten.</param>
89 /// <param name="removeUsedRows">Whether to remove used group rows before returning.</param>
90 public void FlattenAndRewriteRows(string parentType, string parentId, bool removeUsedRows)
91 {
92 Debug.Assert(this.groupTypes.Contains(parentType));
93
94 List<Item> orderedItems;
95 this.CreateOrderedList(parentType, parentId, out orderedItems);
96 if (this.encounteredError)
97 {
98 return;
99 }
100
101 this.CreateNewGroupRows(parentType, parentId, orderedItems);
102
103 if (removeUsedRows)
104 {
105 this.RemoveUsedGroupRows();
106 }
107 }
108
109 /// <summary>
110 /// Finds all items under a parent group type and creates new WixGroup data for them.
111 /// </summary>
112 /// <param name="parentType">The type of the parent group to flatten.</param>
113 /// <param name="removeUsedRows">Whether to remove used group rows before returning.</param>
114 public void FlattenAndRewriteGroups(string parentType, bool removeUsedRows)
115 {
116 Debug.Assert(this.groupTypes.Contains(parentType));
117
118 this.LoadFlattenOrderGroups();
119 if (this.encounteredError)
120 {
121 return;
122 }
123
124 foreach (Item item in this.items)
125 {
126 if (parentType == item.Type)
127 {
128 List<Item> orderedItems;
129 this.CreateOrderedList(item.Type, item.Id, out orderedItems);
130 this.CreateNewGroupRows(item.Type, item.Id, orderedItems);
131 }
132 }
133
134 if (removeUsedRows)
135 {
136 this.RemoveUsedGroupRows();
137 }
138 }
139
140
141 /// <summary>
142 /// Creates a flattened and ordered list of items for the given parent group.
143 /// </summary>
144 /// <param name="parentType">The group type for the parent group to flatten.</param>
145 /// <param name="parentId">The identifier of the parent group to flatten.</param>
146 /// <param name="orderedItems">The returned list of ordered items.</param>
147 private void CreateOrderedList(string parentType, string parentId, out List<Item> orderedItems)
148 {
149 orderedItems = null;
150
151 this.LoadFlattenOrderGroups();
152 if (this.encounteredError)
153 {
154 return;
155 }
156
157 Item parentItem;
158 if (!this.items.TryGetValue(parentType, parentId, out parentItem))
159 {
160 this.OnMessage(WixErrors.IdentifierNotFound(parentType, parentId));
161 return;
162 }
163
164 orderedItems = new List<Item>(parentItem.ChildItems);
165 orderedItems.Sort(new Item.AfterItemComparer());
166 }
167
168 /// <summary>
169 /// Removes rows from WixGroup that have been used by this object.
170 /// </summary>
171 public void RemoveUsedGroupRows()
172 {
173 List<int> sortedIndexes = this.rowsUsed.Distinct().OrderByDescending(i => i).ToList();
174
175 Table wixGroupTable = this.output.Tables["WixGroup"];
176 Debug.Assert(null != wixGroupTable);
177 Debug.Assert(sortedIndexes[0] < wixGroupTable.Rows.Count);
178
179 foreach (int rowIndex in sortedIndexes)
180 {
181 wixGroupTable.Rows.RemoveAt(rowIndex);
182 }
183 }
184
185 /// <summary>
186 /// Creates new WixGroup rows for a list of items.
187 /// </summary>
188 /// <param name="parentType">The group type for the parent group in the new rows.</param>
189 /// <param name="parentId">The identifier of the parent group in the new rows.</param>
190 /// <param name="orderedItems">The list of new items.</param>
191 private void CreateNewGroupRows(string parentType, string parentId, List<Item> orderedItems)
192 {
193 // TODO: MSIs don't guarantee that rows stay in the same order, and technically, neither
194 // does WiX (although they do, currently). We probably want to "upgrade" this to a new
195 // table that includes a sequence number, and then change the code that uses ordered
196 // groups to read from that table instead.
197 Table wixGroupTable = this.output.Tables["WixGroup"];
198 Debug.Assert(null != wixGroupTable);
199
200 foreach (Item item in orderedItems)
201 {
202 Row row = wixGroupTable.CreateRow(item.Row.SourceLineNumbers);
203 row[0] = parentId;
204 row[1] = parentType;
205 row[2] = item.Id;
206 row[3] = item.Type;
207 }
208 }
209
210 // Group/Ordering Flattening Logic
211 //
212 // What follows is potentially convoluted logic. Two somewhat orthogonal concepts are in
213 // play: grouping (parent/child relationships) and ordering (before/after relationships).
214 // Dealing with just one or the other is straghtforward. Groups can be flattened
215 // recursively. Ordering can be propagated in either direction. When the ordering also
216 // participates in the grouping constructions, however, things get trickier. For the
217 // purposes of this discussion, we're dealing with "items" and "groups", and an instance
218 // of either of them can be marked as coming "after" some other instance.
219 //
220 // For simple item-to-item ordering, the "after" values simply propagate: if A is after B,
221 // and B is after C, then we can say that A is after *both* B and C. If a group is involved,
222 // it acts as a proxy for all of its included items and any sub-groups.
223
224 /// <summary>
225 /// Internal workhorse for ensuring that group and ordering information has
226 /// been loaded and applied.
227 /// </summary>
228 private void LoadFlattenOrderGroups()
229 {
230 if (!this.loaded)
231 {
232 this.LoadGroups();
233 this.LoadOrdering();
234
235 // It would be really nice to have a "find circular after dependencies"
236 // function, but it gets much more complicated because of the way that
237 // the dependencies are propagated across group boundaries. For now, we
238 // just live with the dependency loop detection as we flatten the
239 // dependencies. Group references, however, we can check directly.
240 this.FindCircularGroupReferences();
241
242 if (!this.encounteredError)
243 {
244 this.FlattenGroups();
245 this.FlattenOrdering();
246 }
247
248 this.loaded = true;
249 }
250 }
251
252 /// <summary>
253 /// Loads data from the WixGroup table.
254 /// </summary>
255 private void LoadGroups()
256 {
257 Table wixGroupTable = this.output.Tables["WixGroup"];
258 if (null == wixGroupTable || 0 == wixGroupTable.Rows.Count)
259 {
260 // TODO: Change message name to make it *not* Bundle specific?
261 this.OnMessage(WixErrors.MissingBundleInformation("WixGroup"));
262 }
263
264 // Collect all of the groups
265 for (int rowIndex = 0; rowIndex < wixGroupTable.Rows.Count; ++rowIndex)
266 {
267 Row row = wixGroupTable.Rows[rowIndex];
268 string rowParentName = (string)row[0];
269 string rowParentType = (string)row[1];
270 string rowChildName = (string)row[2];
271 string rowChildType = (string)row[3];
272
273 // If this row specifies a parent or child type that's not in our
274 // lists, we assume it's not a row that we're concerned about.
275 if (!this.groupTypes.Contains(rowParentType) ||
276 !this.itemTypes.Contains(rowChildType))
277 {
278 continue;
279 }
280
281 this.rowsUsed.Add(rowIndex);
282
283 Item parentItem;
284 if (!this.items.TryGetValue(rowParentType, rowParentName, out parentItem))
285 {
286 parentItem = new Item(row, rowParentType, rowParentName);
287 this.items.Add(parentItem);
288 }
289
290 Item childItem;
291 if (!this.items.TryGetValue(rowChildType, rowChildName, out childItem))
292 {
293 childItem = new Item(row, rowChildType, rowChildName);
294 this.items.Add(childItem);
295 }
296
297 parentItem.ChildItems.Add(childItem);
298 }
299 }
300
301 /// <summary>
302 /// Flattens group/item information.
303 /// </summary>
304 private void FlattenGroups()
305 {
306 foreach (Item item in this.items)
307 {
308 item.FlattenChildItems();
309 }
310 }
311
312 /// <summary>
313 /// Finds and reports circular references in the group/item data.
314 /// </summary>
315 private void FindCircularGroupReferences()
316 {
317 ItemCollection itemsInKnownLoops = new ItemCollection();
318 foreach (Item item in this.items)
319 {
320 if (itemsInKnownLoops.Contains(item))
321 {
322 continue;
323 }
324
325 ItemCollection itemsSeen = new ItemCollection();
326 string circularReference;
327 if (this.FindCircularGroupReference(item, item, itemsSeen, out circularReference))
328 {
329 itemsInKnownLoops.Add(itemsSeen);
330 this.OnMessage(WixErrors.ReferenceLoopDetected(item.Row.SourceLineNumbers, circularReference));
331 }
332 }
333 }
334
335 /// <summary>
336 /// Recursive worker to find and report circular references in group/item data.
337 /// </summary>
338 /// <param name="checkItem">The sentinal item being checked.</param>
339 /// <param name="currentItem">The current item in the recursion.</param>
340 /// <param name="itemsSeen">A list of all items already visited (for performance).</param>
341 /// <param name="circularReference">A list of items in the current circular reference, if one was found; null otherwise.</param>
342 /// <returns>True if a circular reference was found; false otherwise.</returns>
343 private bool FindCircularGroupReference(Item checkItem, Item currentItem, ItemCollection itemsSeen, out string circularReference)
344 {
345 circularReference = null;
346 foreach (Item subitem in currentItem.ChildItems)
347 {
348 if (checkItem == subitem)
349 {
350 // TODO: Even better would be to include the source lines for each reference!
351 circularReference = String.Format(CultureInfo.InvariantCulture, "{0}:{1} -> {2}:{3}",
352 currentItem.Type, currentItem.Id, subitem.Type, subitem.Id);
353 return true;
354 }
355
356 if (!itemsSeen.Contains(subitem))
357 {
358 itemsSeen.Add(subitem);
359 if (this.FindCircularGroupReference(checkItem, subitem, itemsSeen, out circularReference))
360 {
361 // TODO: Even better would be to include the source lines for each reference!
362 circularReference = String.Format(CultureInfo.InvariantCulture, "{0}:{1} -> {2}",
363 currentItem.Type, currentItem.Id, circularReference);
364 return true;
365 }
366 }
367 }
368
369 return false;
370 }
371
372 /// <summary>
373 /// Loads ordering dependency data from the WixOrdering table.
374 /// </summary>
375 private void LoadOrdering()
376 {
377 Table wixOrderingTable = output.Tables["WixOrdering"];
378 if (null == wixOrderingTable || 0 == wixOrderingTable.Rows.Count)
379 {
380 // TODO: Do we need a message here?
381 return;
382 }
383
384 foreach (Row row in wixOrderingTable.Rows)
385 {
386 string rowItemType = (string)row[0];
387 string rowItemName = (string)row[1];
388 string rowDependsOnType = (string)row[2];
389 string rowDependsOnName = (string)row[3];
390
391 // If this row specifies some other (unknown) type in either
392 // position, we assume it's not a row that we're concerned about.
393 // For ordering, we allow group and item in either position.
394 if (!(this.groupTypes.Contains(rowItemType) || this.itemTypes.Contains(rowItemType)) ||
395 !(this.groupTypes.Contains(rowDependsOnType) || this.itemTypes.Contains(rowDependsOnType)))
396 {
397 continue;
398 }
399
400 Item item = null;
401 Item dependsOn = null;
402
403 if (!this.items.TryGetValue(rowItemType, rowItemName, out item))
404 {
405 this.OnMessage(WixErrors.IdentifierNotFound(rowItemType, rowItemName));
406 }
407
408 if (!this.items.TryGetValue(rowDependsOnType, rowDependsOnName, out dependsOn))
409 {
410 this.OnMessage(WixErrors.IdentifierNotFound(rowDependsOnType, rowDependsOnName));
411 }
412
413 if (null == item || null == dependsOn)
414 {
415 continue;
416 }
417
418 item.AddAfter(dependsOn, this);
419 }
420 }
421
422 /// <summary>
423 /// Flattens the ordering dependencies in the groups/items.
424 /// </summary>
425 private void FlattenOrdering()
426 {
427 // Because items don't know about their parent groups (and can, in fact, be
428 // in more than one group at a time), we need to pre-propagate the 'afters'
429 // from each parent item to its children before we attempt to flatten the
430 // ordering.
431 foreach (Item item in this.items)
432 {
433 item.PropagateAfterToChildItems(this);
434 }
435
436 foreach (Item item in this.items)
437 {
438 item.FlattenAfters(this);
439 }
440 }
441
442 /// <summary>
443 /// A variant of KeyedCollection that doesn't throw when an item is re-added.
444 /// </summary>
445 /// <typeparam name="TKey">Key type for the collection.</typeparam>
446 /// <typeparam name="TItem">Item type for the colelction.</typeparam>
447 internal abstract class EnhancedKeyCollection<TKey, TItem> : KeyedCollection<TKey, TItem>
448 {
449 new public void Add(TItem item)
450 {
451 if (!this.Contains(item))
452 {
453 base.Add(item);
454 }
455 }
456
457 public void Add(Collection<TItem> list)
458 {
459 foreach (TItem item in list)
460 {
461 this.Add(item);
462 }
463 }
464
465 public void Remove(Collection<TItem> list)
466 {
467 foreach (TItem item in list)
468 {
469 this.Remove(item);
470 }
471 }
472
473 public bool TryGetValue(TKey key, out TItem item)
474 {
475 // KeyedCollection doesn't implement the TryGetValue() method, but it's
476 // a useful concept. We can't just always pass this to the enclosed
477 // Dictionary, however, because it doesn't always exist! If it does, we
478 // can delegate to it as one would expect. If it doesn't, we have to
479 // implement everything ourselves in terms of Contains().
480
481 if (null != this.Dictionary)
482 {
483 return this.Dictionary.TryGetValue(key, out item);
484 }
485
486 if (this.Contains(key))
487 {
488 item = this[key];
489 return true;
490 }
491
492 item = default(TItem);
493 return false;
494 }
495
496#if DEBUG
497 // This just makes debugging easier...
498 public override string ToString()
499 {
500 StringBuilder sb = new StringBuilder();
501 foreach (TItem item in this)
502 {
503 sb.AppendFormat("{0}, ", item);
504 }
505 sb.Length -= 2;
506 return sb.ToString();
507 }
508#endif // DEBUG
509 }
510
511 /// <summary>
512 /// A specialized EnhancedKeyCollection, typed to Items.
513 /// </summary>
514 internal class ItemCollection : EnhancedKeyCollection<string, Item>
515 {
516 protected override string GetKeyForItem(Item item)
517 {
518 return item.Key;
519 }
520
521 public bool TryGetValue(string type, string id, out Item item)
522 {
523 return this.TryGetValue(CreateKeyFromTypeId(type, id), out item);
524 }
525
526 public static string CreateKeyFromTypeId(string type, string id)
527 {
528 return String.Format(CultureInfo.InvariantCulture, "{0}_{1}", type, id);
529 }
530 }
531
532 /// <summary>
533 /// An item (or group) in the grouping/ordering engine.
534 /// </summary>
535 /// <remarks>Encapsulates nested group membership and also before/after
536 /// ordering dependencies.</remarks>
537 internal class Item
538 {
539 private ItemCollection afterItems;
540 private ItemCollection beforeItems; // for checking for circular references
541 private bool flattenedAfterItems;
542
543 public Item(Row row, string type, string id)
544 {
545 this.Row = row;
546 this.Type = type;
547 this.Id = id;
548
549 this.Key = ItemCollection.CreateKeyFromTypeId(type, id);
550
551 afterItems = new ItemCollection();
552 beforeItems = new ItemCollection();
553 flattenedAfterItems = false;
554 }
555
556 public Row Row { get; private set; }
557 public string Type { get; private set; }
558 public string Id { get; private set; }
559 public string Key { get; private set; }
560
561#if DEBUG
562 // Makes debugging easier...
563 public override string ToString()
564 {
565 return this.Key;
566 }
567#endif // DEBUG
568
569 private ItemCollection childItems = new ItemCollection();
570 public ItemCollection ChildItems { get { return childItems; } }
571
572 /// <summary>
573 /// Removes any nested groups under this item and replaces
574 /// them with their child items.
575 /// </summary>
576 public void FlattenChildItems()
577 {
578 ItemCollection flattenedChildItems = new ItemCollection();
579
580 foreach (Item childItem in this.ChildItems)
581 {
582 if (0 == childItem.ChildItems.Count)
583 {
584 flattenedChildItems.Add(childItem);
585 }
586 else
587 {
588 childItem.FlattenChildItems();
589 flattenedChildItems.Add(childItem.ChildItems);
590 }
591 }
592
593 this.ChildItems.Clear();
594 this.ChildItems.Add(flattenedChildItems);
595 }
596
597 /// <summary>
598 /// Adds a list of items to the 'after' ordering collection.
599 /// </summary>
600 /// <param name="items">List of items to add.</param>
601 /// <param name="messageHandler">Message handler in case a circular ordering reference is found.</param>
602 public void AddAfter(ItemCollection items, IMessageHandler messageHandler)
603 {
604 foreach (Item item in items)
605 {
606 this.AddAfter(item, messageHandler);
607 }
608 }
609
610 /// <summary>
611 /// Adds an item to the 'after' ordering collection.
612 /// </summary>
613 /// <param name="item">Items to add.</param>
614 /// <param name="messageHandler">Message handler in case a circular ordering reference is found.</param>
615 public void AddAfter(Item after, IMessageHandler messageHandler)
616 {
617 if (this.beforeItems.Contains(after))
618 {
619 // We could try to chain this up (the way that group circular dependencies
620 // are reported), but since we're in the process of flattening, we may already
621 // have lost some distinction between authored and propagated ordering.
622 string circularReference = String.Format(CultureInfo.InvariantCulture, "{0}:{1} -> {2}:{3} -> {0}:{1}",
623 this.Type, this.Id, after.Type, after.Id);
624 messageHandler.OnMessage(WixErrors.OrderingReferenceLoopDetected(after.Row.SourceLineNumbers, circularReference));
625 return;
626 }
627
628 this.afterItems.Add(after);
629 after.beforeItems.Add(this);
630 }
631
632 /// <summary>
633 /// Propagates 'after' dependencies from an item to its child items.
634 /// </summary>
635 /// <param name="messageHandler">Message handler in case a circular ordering reference is found.</param>
636 /// <remarks>Because items don't know about their parent groups (and can, in fact, be in more
637 /// than one group at a time), we need to propagate the 'afters' from each parent item to its children
638 /// before we attempt to flatten the ordering.</remarks>
639 public void PropagateAfterToChildItems(IMessageHandler messageHandler)
640 {
641 if (this.ShouldItemPropagateChildOrdering())
642 {
643 foreach (Item childItem in this.childItems)
644 {
645 childItem.AddAfter(this.afterItems, messageHandler);
646 }
647 }
648 }
649
650 /// <summary>
651 /// Flattens the ordering dependency for this item.
652 /// </summary>
653 /// <param name="messageHandler">Message handler in case a circular ordering reference is found.</param>
654 public void FlattenAfters(IMessageHandler messageHandler)
655 {
656 if (this.flattenedAfterItems)
657 {
658 return;
659 }
660
661 this.flattenedAfterItems = true;
662
663 // Ensure that if we're after something (A), and *it's* after something (B),
664 // that we list ourselved as after both (A) *and* (B).
665 ItemCollection nestedAfterItems = new ItemCollection();
666
667 foreach (Item afterItem in this.afterItems)
668 {
669 afterItem.FlattenAfters(messageHandler);
670 nestedAfterItems.Add(afterItem.afterItems);
671
672 if (afterItem.ShouldItemPropagateChildOrdering())
673 {
674 // If we are after a group, it really means
675 // we are after all of the group's children.
676 foreach (Item childItem in afterItem.ChildItems)
677 {
678 childItem.FlattenAfters(messageHandler);
679 nestedAfterItems.Add(childItem.afterItems);
680 nestedAfterItems.Add(childItem);
681 }
682 }
683 }
684
685 this.AddAfter(nestedAfterItems, messageHandler);
686 }
687
688 // We *don't* propagate ordering information from Packages or
689 // Containers to their children, because ordering doesn't matter
690 // for them, and a Payload in two Packages (or Containers) can
691 // cause a circular reference to occur. We do, however, need to
692 // track the ordering in the UX Container, because we need the
693 // first payload to be the entrypoint.
694 private bool ShouldItemPropagateChildOrdering()
695 {
696 if (String.Equals("Package", this.Type, StringComparison.Ordinal) ||
697 (String.Equals("Container", this.Type, StringComparison.Ordinal) &&
698 !String.Equals(Compiler.BurnUXContainerId, this.Id, StringComparison.Ordinal)))
699 {
700 return false;
701 }
702 return true;
703 }
704
705 /// <summary>
706 /// Helper IComparer class to make ordering easier.
707 /// </summary>
708 internal sealed class AfterItemComparer : IComparer<Item>
709 {
710 public int Compare(Item x, Item y)
711 {
712 if (x.afterItems.Contains(y))
713 {
714 return 1;
715 }
716 else if (y.afterItems.Contains(x))
717 {
718 return -1;
719 }
720
721 return string.CompareOrdinal(x.Id, y.Id);
722 }
723 }
724 }
725 }
726}