Bug 15574. XML deserialization recursion: add array type to known_types?
[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 using System;
29 using System.Collections;
30 using System.Collections.Generic;
31 using System.Reflection;
32 using System.Text;
33 using System.Xml;
34
35 namespace Microsoft.Build.BuildEngine {
36         public class BuildPropertyGroup : IEnumerable {
37         
38                 bool                    read_only;
39                 ImportedProject         importedProject;
40                 XmlElement              propertyGroup;
41                 GroupingCollection      parentCollection;
42                 Project                 parentProject;
43                 List <BuildProperty>    properties;
44                 Dictionary <string, BuildProperty>      propertiesByName;
45                 bool evaluated;
46
47                 public BuildPropertyGroup ()
48                         : this (null, null, null, false)
49                 {
50                 }
51
52                 internal BuildPropertyGroup (XmlElement xmlElement, Project project, ImportedProject importedProject, bool readOnly)
53                 {
54                         this.importedProject = importedProject;
55                         this.parentCollection = null;
56                         this.parentProject = project;
57                         this.propertyGroup = xmlElement;
58                         this.read_only = readOnly;
59
60                         if (FromXml) {
61                                 this.properties = new List <BuildProperty> ();
62                                 foreach (XmlNode xn in propertyGroup.ChildNodes) {
63                                         if (!(xn is XmlElement))
64                                                 continue;
65                                         
66                                         XmlElement xe = (XmlElement) xn;
67                                         BuildProperty bp = new BuildProperty (parentProject, xe);
68                                         AddProperty (bp);
69                                 } 
70                         } else
71                                 this.propertiesByName = new Dictionary <string, BuildProperty> (StringComparer.OrdinalIgnoreCase);
72
73                         DefinedInFileName = importedProject != null ? importedProject.FullFileName :
74                                                 (project != null ? project.FullFileName : null);
75                 }
76
77                 public BuildProperty AddNewProperty (string propertyName,
78                                                      string propertyValue)
79                 {
80                         return AddNewProperty (propertyName, propertyValue, false);
81                 }
82                 
83                 public BuildProperty AddNewProperty (string propertyName,
84                                                      string propertyValue,
85                                                      bool treatPropertyValueAsLiteral)
86                 {
87                         if (!FromXml)
88                                 throw new InvalidOperationException ("This method is only valid for persisted property groups.");
89
90                         if (treatPropertyValueAsLiteral)
91                                 propertyValue = Utilities.Escape (propertyValue);
92
93                         XmlElement element = propertyGroup.OwnerDocument.CreateElement (propertyName, Project.XmlNamespace);
94                         propertyGroup.AppendChild (element);
95
96                         BuildProperty property = new BuildProperty (parentProject, element);
97                         property.Value = propertyValue;
98                         AddProperty (property);
99
100                         parentProject.MarkProjectAsDirty ();
101                         parentProject.NeedToReevaluate ();
102
103                         return property;
104                 }
105
106                 internal void AddProperty (BuildProperty property)
107                 {
108                         if (FromXml)
109                                 properties.Add (property);
110                         else {
111                                 if (propertiesByName.ContainsKey (property.Name)) {
112                                         BuildProperty existing = propertiesByName [property.Name];
113                                         if (property.PropertyType <= existing.PropertyType) {
114                                                 propertiesByName.Remove (property.Name);
115                                                 propertiesByName.Add (property.Name, property);
116                                         }
117                                 } else
118                                         propertiesByName.Add (property.Name, property);
119                         }
120                 }
121                 
122                 public void Clear ()
123                 {
124                         if (FromXml) {
125                                 propertyGroup.RemoveAll ();
126                                 properties = new List <BuildProperty> ();
127                         } else
128                                 propertiesByName = new Dictionary <string, BuildProperty> ();
129                 }
130
131                 [MonoTODO]
132                 public BuildPropertyGroup Clone (bool deepClone)
133                 {
134                         BuildPropertyGroup bpg = new BuildPropertyGroup (propertyGroup, parentProject, importedProject, read_only);
135                         if (FromXml) {
136                                 foreach (BuildProperty bp in properties) {
137                                         if (deepClone)
138                                                 bpg.AddProperty (bp.Clone (true));
139                                         else
140                                                 bpg.AddNewProperty (bp.Name, bp.FinalValue);
141                                 }
142                         } else {
143                                 foreach (BuildProperty bp in propertiesByName.Values) {
144                                         if (deepClone)
145                                                 bpg.AddProperty (bp.Clone (true));
146                                         else
147                                                 bpg.AddNewProperty (bp.Name, bp.FinalValue);
148                                 }
149                         }
150
151                         return bpg;
152                 }
153
154                 public IEnumerator GetEnumerator ()
155                 {
156                         if (FromXml)
157                                 foreach (BuildProperty bp in properties)
158                                         yield return bp;
159                         else 
160                                 foreach (KeyValuePair <string, BuildProperty> kvp in propertiesByName)
161                                         yield return kvp.Value;
162                 }
163
164                 public void RemoveProperty (BuildProperty propertyToRemove)
165                 {
166                         if (propertyToRemove == null)
167                                 throw new ArgumentNullException ("propertyToRemove");
168
169                         if (FromXml) {
170                                 if (!propertyToRemove.FromXml)
171                                         throw new InvalidOperationException ("The specified property does not belong to the current property group.");
172
173                                 propertyToRemove.XmlElement.ParentNode.RemoveChild (propertyToRemove.XmlElement);
174                                 properties.Remove (propertyToRemove);
175                         } else
176                                 propertiesByName.Remove (propertyToRemove.Name);
177                 }
178
179                 public void RemoveProperty (string propertyName)
180                 {
181                         if (FromXml) {
182                                 foreach (BuildProperty bp in properties)
183                                         if (bp.Name == propertyName) {
184                                                 RemoveProperty (bp);
185                                                 break;
186                                         }
187                         } else
188                                 propertiesByName.Remove (propertyName);
189                 }
190
191                 public void SetProperty (string propertyName,
192                                          string propertyValue)
193                 {
194                         SetProperty (propertyName, propertyValue, false);
195                 }
196                 
197                 public void SetProperty (string propertyName,
198                                          string propertyValue,
199                                          bool treatPropertyValueAsLiteral)
200                 {
201                         if (read_only)
202                                 return;
203                         if (FromXml)
204                                 throw new InvalidOperationException (
205                                         "This method is only valid for virtual property groups, not <PropertyGroup> elements.");
206
207                         if (treatPropertyValueAsLiteral)
208                                 propertyValue = Utilities.Escape (propertyValue);
209
210                         if (propertiesByName.ContainsKey (propertyName))
211                                 propertiesByName.Remove (propertyName);
212
213                         BuildProperty bp = new BuildProperty (propertyName, propertyValue);
214                         if (Char.IsDigit (propertyName [0]))
215                                 throw new ArgumentException (String.Format (
216                                         "The name \"{0}\" contains an invalid character \"{1}\".", propertyName, propertyName [0]));
217
218                         AddProperty (bp);
219
220                         if (IsGlobal)
221                                 parentProject.NeedToReevaluate ();
222                 }
223                 
224                 internal void Evaluate ()
225                 {
226                         if (evaluated)
227                                 return;
228
229                         foreach (BuildProperty bp in properties)
230                                 if (ConditionParser.ParseAndEvaluate (bp.Condition, parentProject))
231                                         bp.Evaluate ();
232
233                         evaluated = true;
234                 }
235                 
236                 public string Condition {
237                         get {
238                                 if (!FromXml)
239                                         return String.Empty;
240                                 return propertyGroup.GetAttribute ("Condition");
241                         }
242                         set {
243                                 if (!FromXml)
244                                         throw new InvalidOperationException (
245                                         "Cannot set a condition on an object not represented by an XML element in the project file.");
246                                 propertyGroup.SetAttribute ("Condition", value);
247                         }
248                 }
249
250                 public int Count {
251                         get {
252                                 if (FromXml)
253                                         return properties.Count;
254                                 else
255                                         return propertiesByName.Count;
256                         }
257                 }
258
259                 public bool IsImported {
260                         get {
261                                 return importedProject != null;
262                         }
263                 }
264
265                 internal bool FromXml {
266                         get {
267                                 return propertyGroup != null;
268                         }
269                 }
270
271                 bool IsGlobal {
272                         get {
273                                 return parentProject != null && propertyGroup == null;
274                         }
275                 }
276
277                 public BuildProperty this [string propertyName] {
278                         get {
279                                 if (FromXml)
280                                         throw new InvalidOperationException ("Properties in persisted property groups cannot be accessed by name.");
281                                 
282                                 if (propertiesByName.ContainsKey (propertyName))
283                                         return propertiesByName [propertyName];
284                                 else
285                                         return null;
286                         }
287                         set {
288                                 propertiesByName [propertyName] = value;
289                         }
290                 }
291
292                 internal string DefinedInFileName { get; private set; }
293
294                 internal GroupingCollection GroupingCollection {
295                         get { return parentCollection; }
296                         set { parentCollection = value; }
297                 }
298
299                 internal XmlElement XmlElement {
300                         get { return propertyGroup; }
301                 }
302         }
303 }