[xbuild] Add new reserved properties $(MSBuildThisFile*).
[mono.git] / mcs / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / BuildPropertyGroup.cs
1 //
2 // BuildPropertyGroup.cs: Represents a group of properties
3 //
4 // Author:
5 //   Marek Sieradzki (marek.sieradzki@gmail.com)
6 // 
7 // (C) 2005 Marek Sieradzki
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
28 #if NET_2_0
29
30 using System;
31 using System.Collections;
32 using System.Collections.Generic;
33 using System.Reflection;
34 using System.Text;
35 using System.Xml;
36
37 namespace Microsoft.Build.BuildEngine {
38         public class BuildPropertyGroup : IEnumerable {
39         
40                 bool                    read_only;
41                 ImportedProject         importedProject;
42                 XmlElement              propertyGroup;
43                 GroupingCollection      parentCollection;
44                 Project                 parentProject;
45                 List <BuildProperty>    properties;
46                 Dictionary <string, BuildProperty>      propertiesByName;
47                 bool evaluated;
48
49                 public BuildPropertyGroup ()
50                         : this (null, null, null, false)
51                 {
52                 }
53
54                 internal BuildPropertyGroup (XmlElement xmlElement, Project project, ImportedProject importedProject, bool readOnly)
55                 {
56                         this.importedProject = importedProject;
57                         this.parentCollection = null;
58                         this.parentProject = project;
59                         this.propertyGroup = xmlElement;
60                         this.read_only = readOnly;
61
62                         if (FromXml) {
63                                 this.properties = new List <BuildProperty> ();
64                                 foreach (XmlNode xn in propertyGroup.ChildNodes) {
65                                         if (!(xn is XmlElement))
66                                                 continue;
67                                         
68                                         XmlElement xe = (XmlElement) xn;
69                                         BuildProperty bp = new BuildProperty (parentProject, xe);
70                                         AddProperty (bp);
71                                 } 
72                         } else
73                                 this.propertiesByName = new Dictionary <string, BuildProperty> (StringComparer.InvariantCultureIgnoreCase);
74
75                         DefinedInFileName = importedProject != null ? importedProject.FullFileName :
76                                                 (project != null ? project.FullFileName : null);
77                 }
78
79                 public BuildProperty AddNewProperty (string propertyName,
80                                                      string propertyValue)
81                 {
82                         return AddNewProperty (propertyName, propertyValue, false);
83                 }
84                 
85                 public BuildProperty AddNewProperty (string propertyName,
86                                                      string propertyValue,
87                                                      bool treatPropertyValueAsLiteral)
88                 {
89                         if (!FromXml)
90                                 throw new InvalidOperationException ("This method is only valid for persisted property groups.");
91
92                         if (treatPropertyValueAsLiteral)
93                                 propertyValue = Utilities.Escape (propertyValue);
94
95                         XmlElement element = propertyGroup.OwnerDocument.CreateElement (propertyName, Project.XmlNamespace);
96                         propertyGroup.AppendChild (element);
97
98                         BuildProperty property = new BuildProperty (parentProject, element);
99                         property.Value = propertyValue;
100                         AddProperty (property);
101
102                         parentProject.MarkProjectAsDirty ();
103                         parentProject.NeedToReevaluate ();
104
105                         return property;
106                 }
107
108                 internal void AddProperty (BuildProperty property)
109                 {
110                         if (FromXml)
111                                 properties.Add (property);
112                         else {
113                                 if (propertiesByName.ContainsKey (property.Name)) {
114                                         BuildProperty existing = propertiesByName [property.Name];
115                                         if (property.PropertyType <= existing.PropertyType) {
116                                                 propertiesByName.Remove (property.Name);
117                                                 propertiesByName.Add (property.Name, property);
118                                         }
119                                 } else
120                                         propertiesByName.Add (property.Name, property);
121                         }
122                 }
123                 
124                 public void Clear ()
125                 {
126                         if (FromXml) {
127                                 propertyGroup.RemoveAll ();
128                                 properties = new List <BuildProperty> ();
129                         } else
130                                 propertiesByName = new Dictionary <string, BuildProperty> ();
131                 }
132
133                 [MonoTODO]
134                 public BuildPropertyGroup Clone (bool deepClone)
135                 {
136                         BuildPropertyGroup bpg = new BuildPropertyGroup (propertyGroup, parentProject, importedProject, read_only);
137                         if (FromXml) {
138                                 foreach (BuildProperty bp in properties) {
139                                         if (deepClone)
140                                                 bpg.AddProperty (bp.Clone (true));
141                                         else
142                                                 bpg.AddNewProperty (bp.Name, bp.FinalValue);
143                                 }
144                         } else {
145                                 foreach (BuildProperty bp in propertiesByName.Values) {
146                                         if (deepClone)
147                                                 bpg.AddProperty (bp.Clone (true));
148                                         else
149                                                 bpg.AddNewProperty (bp.Name, bp.FinalValue);
150                                 }
151                         }
152
153                         return bpg;
154                 }
155
156                 public IEnumerator GetEnumerator ()
157                 {
158                         if (FromXml)
159                                 foreach (BuildProperty bp in properties)
160                                         yield return bp;
161                         else 
162                                 foreach (KeyValuePair <string, BuildProperty> kvp in propertiesByName)
163                                         yield return kvp.Value;
164                 }
165
166                 public void RemoveProperty (BuildProperty propertyToRemove)
167                 {
168                         if (propertyToRemove == null)
169                                 throw new ArgumentNullException ("propertyToRemove");
170
171                         if (FromXml) {
172                                 if (!propertyToRemove.FromXml)
173                                         throw new InvalidOperationException ("The specified property does not belong to the current property group.");
174
175                                 propertyToRemove.XmlElement.ParentNode.RemoveChild (propertyToRemove.XmlElement);
176                                 properties.Remove (propertyToRemove);
177                         } else
178                                 propertiesByName.Remove (propertyToRemove.Name);
179                 }
180
181                 public void RemoveProperty (string propertyName)
182                 {
183                         if (FromXml) {
184                                 foreach (BuildProperty bp in properties)
185                                         if (bp.Name == propertyName) {
186                                                 RemoveProperty (bp);
187                                                 break;
188                                         }
189                         } else
190                                 propertiesByName.Remove (propertyName);
191                 }
192
193                 public void SetProperty (string propertyName,
194                                          string propertyValue)
195                 {
196                         SetProperty (propertyName, propertyValue, false);
197                 }
198                 
199                 public void SetProperty (string propertyName,
200                                          string propertyValue,
201                                          bool treatPropertyValueAsLiteral)
202                 {
203                         if (read_only)
204                                 return;
205                         if (FromXml)
206                                 throw new InvalidOperationException (
207                                         "This method is only valid for virtual property groups, not <PropertyGroup> elements.");
208
209                         if (treatPropertyValueAsLiteral)
210                                 propertyValue = Utilities.Escape (propertyValue);
211
212                         if (propertiesByName.ContainsKey (propertyName))
213                                 propertiesByName.Remove (propertyName);
214
215                         BuildProperty bp = new BuildProperty (propertyName, propertyValue);
216                         if (Char.IsDigit (propertyName [0]))
217                                 throw new ArgumentException (String.Format (
218                                         "The name \"{0}\" contains an invalid character \"{1}\".", propertyName, propertyName [0]));
219
220                         AddProperty (bp);
221
222                         if (IsGlobal)
223                                 parentProject.NeedToReevaluate ();
224                 }
225                 
226                 internal void Evaluate ()
227                 {
228                         if (evaluated)
229                                 return;
230
231                         foreach (BuildProperty bp in properties)
232                                 if (ConditionParser.ParseAndEvaluate (bp.Condition, parentProject))
233                                         bp.Evaluate ();
234
235                         evaluated = true;
236                 }
237                 
238                 public string Condition {
239                         get {
240                                 if (!FromXml)
241                                         return String.Empty;
242                                 return propertyGroup.GetAttribute ("Condition");
243                         }
244                         set {
245                                 if (!FromXml)
246                                         throw new InvalidOperationException (
247                                         "Cannot set a condition on an object not represented by an XML element in the project file.");
248                                 propertyGroup.SetAttribute ("Condition", value);
249                         }
250                 }
251
252                 public int Count {
253                         get {
254                                 if (FromXml)
255                                         return properties.Count;
256                                 else
257                                         return propertiesByName.Count;
258                         }
259                 }
260
261                 public bool IsImported {
262                         get {
263                                 return importedProject != null;
264                         }
265                 }
266
267                 internal bool FromXml {
268                         get {
269                                 return propertyGroup != null;
270                         }
271                 }
272
273                 bool IsGlobal {
274                         get {
275                                 return parentProject != null && propertyGroup == null;
276                         }
277                 }
278
279                 public BuildProperty this [string propertyName] {
280                         get {
281                                 if (FromXml)
282                                         throw new InvalidOperationException ("Properties in persisted property groups cannot be accessed by name.");
283                                 
284                                 if (propertiesByName.ContainsKey (propertyName))
285                                         return propertiesByName [propertyName];
286                                 else
287                                         return null;
288                         }
289                         set {
290                                 propertiesByName [propertyName] = value;
291                         }
292                 }
293
294                 internal string DefinedInFileName { get; private set; }
295
296                 internal GroupingCollection GroupingCollection {
297                         get { return parentCollection; }
298                         set { parentCollection = value; }
299                 }
300
301                 internal XmlElement XmlElement {
302                         get { return propertyGroup; }
303                 }
304         }
305 }
306
307 #endif