1 // Copyright (c) Microsoft. All rights reserved.
2 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 
4 using System;
5 using System.Collections.Generic;
6 using System.Text;
7 using System.Xml;
8 
9 using Microsoft.Build.BuildEngine.Shared;
10 
11 namespace Microsoft.Build.BuildEngine
12 {
13     /// <summary>
14     /// This class represents a single Import element in a project file
15     /// </summary>
16     /// <owner>LukaszG</owner>
17     public class Import : IItemPropertyGrouping
18     {
19         #region Properties
20 
21         private Project parentProject = null;
22 
23         /// <summary>
24         /// Returns the parent MSBuild Project object.
25         /// </summary>
26         internal Project ParentProject
27         {
28             get { return this.parentProject; }
29             set { this.parentProject = value; }
30         }
31 
32         private XmlElement importElement = null;
33 
34         /// <summary>
35         /// Returns the source XmlElement this Import is based on.
36         /// </summary>
37         internal XmlElement ImportElement
38         {
39             get { return this.importElement; }
40         }
41 
42         private bool importedFromAnotherProject;
43 
44         /// <summary>
45         /// Returns true if this Import came from an imported project
46         /// </summary>
47         /// <owner>LukaszG</owner>
48         public bool IsImported
49         {
50             get { return this.importedFromAnotherProject; }
51         }
52 
53         private XmlAttribute projectPathAttribute = null;
54 
55         /// <summary>
56         /// Returns the original import path from the Import element
57         /// </summary>
58         /// <owner>LukaszG</owner>
59         public string ProjectPath
60         {
61             get
62             {
63                 return (this.projectPathAttribute != null) ? this.projectPathAttribute.Value : null;
64             }
65             set
66             {
67                 ImportElement.SetAttribute(XMakeAttributes.project, value);
68                 ParentProject.MarkProjectAsDirtyForReprocessXml();
69             }
70         }
71 
72         /// <summary>
73         /// Internal accessor for the project path XML attribute
74         /// </summary>
75         /// <owner>LukaszG</owner>
76         internal XmlAttribute ProjectPathAttribute
77         {
78             get { return this.projectPathAttribute; }
79         }
80 
81         private string evaluatedProjectPath = null;
82 
83         /// <summary>
84         /// Returns the full evaluated import path
85         /// </summary>
86         /// <owner>LukaszG</owner>
87         public string EvaluatedProjectPath
88         {
89             get { return this.evaluatedProjectPath; }
90         }
91 
92         private XmlAttribute conditionAttribute = null;
93 
94         /// <summary>
95         /// The condition string for this UsingTask
96         /// </summary>
97         /// <owner>LukaszG</owner>
98         public string Condition
99         {
100             get
101             {
102                 return (this.conditionAttribute != null) ? this.conditionAttribute.Value : null;
103             }
104             set
105             {
106                 ImportElement.SetAttribute(XMakeAttributes.condition, value);
107                 if (conditionAttribute == null)
108                 {
109                     conditionAttribute = ImportElement.Attributes[XMakeAttributes.condition];
110                 }
111                 ParentProject.MarkProjectAsDirtyForReprocessXml();
112             }
113         }
114 
115         /// <summary>
116         /// Internal accessor for the condition XML attribute
117         /// </summary>
118         internal XmlAttribute ConditionAttribute
119         {
120             get { return this.conditionAttribute; }
121         }
122 
123         #endregion
124 
125         #region Constructors
126 
127         /// <summary>
128         /// Internal constructor
129         /// </summary>
130         /// <param name="importElement"></param>
131         /// <param name="isImported"></param>
132         /// <owner>LukaszG</owner>
Import(XmlElement importElement, Project parentProject, bool isImported)133         internal Import(XmlElement importElement, Project parentProject, bool isImported)
134         {
135             this.importedFromAnotherProject = isImported;
136 
137             // Make sure the <Import> node has been given to us.
138             ErrorUtilities.VerifyThrow(importElement != null,
139                 "Need an XML node representing the <Import> element.");
140 
141             this.importElement = importElement;
142 
143             // Make sure we have a valid parent Project
144             ErrorUtilities.VerifyThrow(parentProject != null,
145                 "Need a parent Project object to instantiate an Import.");
146 
147             this.parentProject = parentProject;
148 
149             // Make sure this really is the <Import> node.
150             ProjectXmlUtilities.VerifyThrowElementName(importElement, XMakeElements.import);
151 
152             // Loop through the list of attributes on the <Import> element.
153             foreach (XmlAttribute importAttribute in importElement.Attributes)
154             {
155                 switch (importAttribute.Name)
156                 {
157                     // The "project" attribute points us at the project file to import.
158                     case XMakeAttributes.project:
159                         // Just store the attribute value at this point. We want to make sure that we evaluate any
160                         // Condition attribute before looking at the Project attribute - if the Condition is going to be false,
161                         // it's legitimate for the value of the Project attribute to be completely invalid.
162                         // For example, <Import Project="$(A)" Condition="$(A)!=''"/> should not cause an error
163                         // that the Project attribute is empty.
164                         this.projectPathAttribute = importAttribute;
165                         break;
166 
167                     // If the "condition" attribute is present, then it must evaluate to "true".
168                     case XMakeAttributes.condition:
169                         this.conditionAttribute = importAttribute;
170                         break;
171 
172                     // We've come across an attribute in the <Import> element that we
173                     // don't recognize.  Fail due to invalid project file.
174                     default:
175                         ProjectXmlUtilities.ThrowProjectInvalidAttribute(importAttribute);
176                         break;
177                 }
178             }
179 
180             ProjectErrorUtilities.VerifyThrowInvalidProject((this.projectPathAttribute != null) && (this.projectPathAttribute.Value.Length != 0),
181                 importElement, "MissingRequiredAttribute",
182                 XMakeAttributes.project, XMakeElements.import);
183 
184             // Make sure this node has no children.  Our schema doesn't support having
185             // children beneath the <Import> element.
186             if (importElement.HasChildNodes)
187             {
188                 // Don't put the "if" condition inside the first parameter to
189                 // VerifyThrow..., because we'll get null reference exceptions,
190                 // since the parameter importElement.FirstChild.Name is being
191                 // passed in regardless of whether the condition holds true or not.
192                 ProjectXmlUtilities.ThrowProjectInvalidChildElement(importElement.FirstChild);
193             }
194         }
195 
196         #endregion
197 
198         /// <summary>
199         /// Sets the full evaluated project path for this import.
200         /// </summary>
201         /// <param name="newEvaluatedProjectPath"></param>
202         /// <owner>LukaszG</owner>
SetEvaluatedProjectPath(string newEvaluatedProjectPath)203         internal void SetEvaluatedProjectPath(string newEvaluatedProjectPath)
204         {
205             this.evaluatedProjectPath = newEvaluatedProjectPath;
206         }
207     }
208 }
209