83d157cc0efa9c720ad675cfea7f0874d3abd096
[mono.git] / mcs / class / Microsoft.Build / Microsoft.Build.Construction / ProjectRootElement.cs
1 //
2 // ProjectRootElement.cs
3 //
4 // Author:
5 //   Leszek Ciesielski (skolima@gmail.com)
6 //
7 // (C) 2011 Leszek Ciesielski
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
29 using System.Collections.Generic;
30
31 using Microsoft.Build.Evaluation;
32 using Microsoft.Build.Internal;
33 using System.Text;
34 using System;
35 using System.Xml;
36 using System.IO;
37 using System.Linq;
38 using System.Globalization;
39 using Microsoft.Build.Exceptions;
40
41 namespace Microsoft.Build.Construction
42 {
43         [System.Diagnostics.DebuggerDisplayAttribute("{FullPath} #Children={Count} DefaultTargets={DefaultTargets} "
44                                                      + "ToolsVersion={ToolsVersion} InitialTargets={InitialTargets}")]
45         public class ProjectRootElement : ProjectElementContainer
46         {
47                 public override string Condition { get { return null; } set { throw new InvalidOperationException (
48                         "Can not set Condition."); } }
49                 public string DefaultTargets { get; set; }
50
51                 string fullPath;
52                 public string FullPath {
53                         get { return fullPath; }
54                         set {
55                                 fullPath = Path.GetFullPath (value);
56                                 DirectoryPath = Path.GetDirectoryName (fullPath);
57                         }
58                 }
59
60                 string directoryPath;
61                 public string DirectoryPath {
62                         get { return directoryPath ?? String.Empty; }
63                         internal set { directoryPath = value; }
64                 }
65
66                 public ICollection<ProjectPropertyElement> Properties {
67                         get { return new CollectionFromEnumerable<ProjectPropertyElement> (
68                                 new FilteredEnumerable<ProjectPropertyElement> (AllChildren)); }
69                 }
70
71                 public ICollection<ProjectChooseElement> ChooseElements {
72                         get { return new CollectionFromEnumerable<ProjectChooseElement> (
73                                 new FilteredEnumerable<ProjectChooseElement> (Children)); }
74                 }
75
76                 public Encoding Encoding {
77                         get { return Encoding.UTF8; }
78                 }
79
80                 public bool HasUnsavedChanges {
81                         get { return true; }
82                 }
83
84                 public ICollection<ProjectImportGroupElement> ImportGroups {
85                         get { return new CollectionFromEnumerable<ProjectImportGroupElement> (
86                                 new FilteredEnumerable<ProjectImportGroupElement> (Children)); }
87                 }
88
89                 public ICollection<ProjectImportGroupElement> ImportGroupsReversed {
90                         get { return new CollectionFromEnumerable<ProjectImportGroupElement> (
91                                 new FilteredEnumerable<ProjectImportGroupElement> (ChildrenReversed)); }
92                 }
93
94                 public ICollection<ProjectImportElement> Imports {
95                         get { return new CollectionFromEnumerable<ProjectImportElement> (
96                                 new FilteredEnumerable<ProjectImportElement> (AllChildren)); }
97                 }
98
99                 public string InitialTargets { get; set; }
100
101                 public ICollection<ProjectItemDefinitionGroupElement> ItemDefinitionGroups {
102                         get { return new CollectionFromEnumerable<ProjectItemDefinitionGroupElement> (
103                                 new FilteredEnumerable<ProjectItemDefinitionGroupElement> (Children)); }
104                 }
105
106                 public ICollection<ProjectItemDefinitionGroupElement> ItemDefinitionGroupsReversed {
107                         get { return new CollectionFromEnumerable<ProjectItemDefinitionGroupElement> (
108                                 new FilteredEnumerable<ProjectItemDefinitionGroupElement> (ChildrenReversed)); }
109                 }
110
111                 public ICollection<ProjectItemDefinitionElement> ItemDefinitions {
112                         get { return new CollectionFromEnumerable<ProjectItemDefinitionElement> (
113                                 new FilteredEnumerable<ProjectItemDefinitionElement> (AllChildren)); }
114                 }
115
116                 public ICollection<ProjectItemGroupElement> ItemGroups {
117                         get { return new CollectionFromEnumerable<ProjectItemGroupElement> (
118                                 new FilteredEnumerable<ProjectItemGroupElement> (Children)); }
119                 }
120
121                 public ICollection<ProjectItemGroupElement> ItemGroupsReversed {
122                         get { return new CollectionFromEnumerable<ProjectItemGroupElement> (
123                                 new FilteredEnumerable<ProjectItemGroupElement> (ChildrenReversed)); }
124                 }
125
126                 public ICollection<ProjectItemElement> Items {
127                         get { return new CollectionFromEnumerable<ProjectItemElement> (
128                                 new FilteredEnumerable<ProjectItemElement> (AllChildren)); }
129                 }
130
131                 public DateTime LastWriteTimeWhenRead {
132                         get { return DateTime.MinValue; }
133                 }
134
135                 public ICollection<ProjectPropertyGroupElement> PropertyGroups {
136                         get { return new CollectionFromEnumerable<ProjectPropertyGroupElement> (
137                                 new FilteredEnumerable<ProjectPropertyGroupElement> (Children)); }
138                 }
139
140                 public ICollection<ProjectPropertyGroupElement> PropertyGroupsReversed {
141                         get { return new CollectionFromEnumerable<ProjectPropertyGroupElement> (
142                                 new FilteredEnumerable<ProjectPropertyGroupElement> (ChildrenReversed)); }
143                 }
144
145                 public string RawXml {
146                         get {
147                                 using (var writer = new StringWriter (CultureInfo.InvariantCulture)) {
148                                         Save (writer);
149                                         return writer.ToString ();
150                                 }
151                         }
152                 }
153
154                 public ICollection<ProjectTargetElement> Targets {
155                         get { return new CollectionFromEnumerable<ProjectTargetElement> (
156                                 new FilteredEnumerable<ProjectTargetElement> (Children)); }
157                 }
158
159                 public DateTime TimeLastChanged {
160                         get { return DateTime.Now; }
161                 }
162
163                 string toolsVersion = "4.0";
164                 public string ToolsVersion {
165                         get { return toolsVersion; }
166                         set { toolsVersion = value; }
167                 }
168
169                 public ICollection<ProjectUsingTaskElement> UsingTasks {
170                         get { return new CollectionFromEnumerable<ProjectUsingTaskElement> (
171                                 new FilteredEnumerable<ProjectUsingTaskElement> (Children)); }
172                 }
173
174                 public int Version {
175                         get { return 0; }
176                 }
177
178                 ProjectRootElement (ProjectCollection projectCollection)
179                 {
180                 }
181
182                 public static ProjectRootElement Create ()
183                 {
184                         return Create (ProjectCollection.GlobalProjectCollection);
185                 }
186
187                 public static ProjectRootElement Create (ProjectCollection projectCollection)
188                 {
189                         return new ProjectRootElement (projectCollection);
190                 }
191
192                 public static ProjectRootElement Create (string path)
193                 {
194                         return Create (path, ProjectCollection.GlobalProjectCollection);
195                 }
196
197                 public static ProjectRootElement Create (XmlReader xmlReader)
198                 {
199                         return Create (xmlReader, ProjectCollection.GlobalProjectCollection);
200                 }
201
202                 public static ProjectRootElement Create (string path, ProjectCollection projectCollection)
203                 {
204                         var result = Create (projectCollection);
205                         result.FullPath = path;
206                         return result;
207                 }
208
209                 public static ProjectRootElement Create (XmlReader xmlReader, ProjectCollection projectCollection)
210                 {
211                         // yes, this should create en empty project
212                         var result = Create (projectCollection);
213                         return result;
214                 }
215
216                 public ProjectImportElement AddImport (string project)
217                 {
218                         var import = CreateImportElement (project);
219                         AppendChild (import);
220                         return import;
221                 }
222
223                 public ProjectImportGroupElement AddImportGroup ()
224                 {
225                         var importGroup = CreateImportGroupElement ();
226                         AppendChild (importGroup);
227                         return importGroup;
228                 }
229
230                 public ProjectItemElement AddItem (string itemType, string include)
231                 {
232                         return AddItem (itemType, include, null);
233                 }
234
235                 public ProjectItemElement AddItem (string itemType, string include,
236                                                    IEnumerable<KeyValuePair<string, string>> metadata)
237                 {
238                         var @group = ItemGroups.
239                                 Where (p => string.IsNullOrEmpty (p.Condition)
240                                        && p.Items.Where (s => s.ItemType.Equals (itemType,
241                                                 StringComparison.OrdinalIgnoreCase)).FirstOrDefault () != null).
242                                         FirstOrDefault ();
243                         if (@group == null)
244                                 @group = AddItemGroup ();
245                         return @group.AddItem (itemType, include, metadata);
246                 }
247
248                 public ProjectItemDefinitionElement AddItemDefinition (string itemType)
249                 {
250                         var @group = ItemDefinitionGroups.
251                                 Where (p => string.IsNullOrEmpty (p.Condition)
252                                        && p.ItemDefinitions.Where (s => s.ItemType.Equals (itemType,
253                                                 StringComparison.OrdinalIgnoreCase)).FirstOrDefault () != null).
254                                         FirstOrDefault ();
255                         if (@group == null)
256                                 @group = AddItemDefinitionGroup ();
257                         return @group.AddItemDefinition (itemType);
258                 }
259
260                 public ProjectItemDefinitionGroupElement AddItemDefinitionGroup ()
261                 {
262                         var @group = CreateItemDefinitionGroupElement ();
263                         ProjectElementContainer last = ItemDefinitionGroupsReversed.FirstOrDefault ();
264                         if (last == null)
265                                 last = PropertyGroupsReversed.FirstOrDefault ();
266                         InsertAfterChild (@group, last);
267                         return @group;
268                 }
269
270                 public ProjectItemGroupElement AddItemGroup ()
271                 {
272                         var @group = CreateItemGroupElement ();
273                         ProjectElementContainer last = ItemGroupsReversed.FirstOrDefault ();
274                         if (last == null)
275                                 last = PropertyGroupsReversed.FirstOrDefault ();
276                         InsertAfterChild (@group, last);
277                         return @group;
278                 }
279
280                 public ProjectPropertyElement AddProperty (string name, string value)
281                 {
282                         ProjectPropertyGroupElement parentGroup = null;
283                         foreach (var @group in PropertyGroups) {
284                                 if (string.IsNullOrEmpty (@group.Condition)) {
285                                         if (parentGroup == null)
286                                                 parentGroup = @group;
287                                         var property = @group.Properties.
288                                                 Where (p => string.IsNullOrEmpty (p.Condition)
289                                                        && p.Name.Equals (name, StringComparison.OrdinalIgnoreCase)).
290                                                         FirstOrDefault ();
291                                         if (property != null) {
292                                                 property.Value = value;
293                                                 return property;
294                                         }
295                                 }
296                         }
297                         if (parentGroup == null)
298                                 parentGroup = AddPropertyGroup ();
299                         return parentGroup.AddProperty (name, value);
300                 }
301
302                 public ProjectPropertyGroupElement AddPropertyGroup ()
303                 {
304                         var @group = CreatePropertyGroupElement ();
305                         var last = PropertyGroupsReversed.FirstOrDefault ();
306                         InsertAfterChild (@group, last);
307                         return @group;
308                 }
309
310                 public ProjectTargetElement AddTarget (string name)
311                 {
312                         var target = CreateTargetElement (name);
313                         AppendChild (target);
314                         return target;
315                 }
316
317                 public ProjectUsingTaskElement AddUsingTask (string name, string assemblyFile, string assemblyName)
318                 {
319                         var usingTask = CreateUsingTaskElement (name, assemblyFile, assemblyName);
320                         AppendChild (usingTask);
321                         return usingTask;
322                 }
323
324                 public ProjectChooseElement CreateChooseElement ()
325                 {
326                         return new ProjectChooseElement (this);
327                 }
328
329                 public ProjectImportElement CreateImportElement (string project)
330                 {
331                         return new ProjectImportElement (project, this);
332                 }
333
334                 public ProjectImportGroupElement CreateImportGroupElement ()
335                 {
336                         return new ProjectImportGroupElement (this);
337                 }
338
339                 public ProjectItemDefinitionElement CreateItemDefinitionElement (string itemType)
340                 {
341                         return new ProjectItemDefinitionElement (itemType, this);
342                 }
343
344                 public ProjectItemDefinitionGroupElement CreateItemDefinitionGroupElement ()
345                 {
346                         return new ProjectItemDefinitionGroupElement (this);
347                 }
348
349                 public ProjectItemElement CreateItemElement (string itemType)
350                 {
351                         return new ProjectItemElement (itemType, this);
352                 }
353
354                 public ProjectItemElement CreateItemElement (string itemType, string include)
355                 {
356                         var item = CreateItemElement (itemType);
357                         item.Include = include;
358                         return item;
359                 }
360
361                 public ProjectItemGroupElement CreateItemGroupElement ()
362                 {
363                         return new ProjectItemGroupElement (this);
364                 }
365
366                 public ProjectMetadataElement CreateMetadataElement (string name)
367                 {
368                         return new ProjectMetadataElement (name, this);
369                 }
370
371                 public ProjectMetadataElement CreateMetadataElement (string name, string unevaluatedValue)
372                 {
373                         var metadata = CreateMetadataElement (name);
374                         metadata.Value = unevaluatedValue;
375                         return metadata;
376                 }
377
378                 public ProjectOnErrorElement CreateOnErrorElement (string executeTargets)
379                 {
380                         return new ProjectOnErrorElement (executeTargets, this);
381                 }
382
383                 public ProjectOtherwiseElement CreateOtherwiseElement ()
384                 {
385                         return new ProjectOtherwiseElement (this);
386                 }
387
388                 public ProjectOutputElement CreateOutputElement (string taskParameter, string itemType,
389                                                                  string propertyName)
390                 {
391                         return new ProjectOutputElement (taskParameter, itemType, propertyName, this);
392                 }
393
394                 public ProjectExtensionsElement CreateProjectExtensionsElement ()
395                 {
396                         return new ProjectExtensionsElement (this);
397                 }
398
399                 public ProjectPropertyElement CreatePropertyElement (string name)
400                 {
401                         return new ProjectPropertyElement (name, this);
402                 }
403
404                 public ProjectPropertyGroupElement CreatePropertyGroupElement ()
405                 {
406                         return new ProjectPropertyGroupElement (this);
407                 }
408
409                 public ProjectTargetElement CreateTargetElement (string name)
410                 {
411                         return new ProjectTargetElement (name, this);
412                 }
413
414                 public ProjectTaskElement CreateTaskElement (string name)
415                 {
416                         return new ProjectTaskElement (name, this);
417                 }
418
419                 public ProjectUsingTaskBodyElement CreateUsingTaskBodyElement (string evaluate, string body)
420                 {
421                         return new ProjectUsingTaskBodyElement (evaluate, body, this);
422                 }
423
424                 public ProjectUsingTaskElement CreateUsingTaskElement (string taskName, string assemblyFile,
425                                                                        string assemblyName)
426                 {
427                         return new ProjectUsingTaskElement (taskName, assemblyFile, assemblyName, this);
428                 }
429
430                 public ProjectUsingTaskParameterElement CreateUsingTaskParameterElement (string name, string output,
431                                                                                          string required,
432                                                                                          string parameterType)
433                 {
434                         return new ProjectUsingTaskParameterElement (name, output, required, parameterType, this);
435                 }
436
437                 public UsingTaskParameterGroupElement CreateUsingTaskParameterGroupElement ()
438                 {
439                         return new UsingTaskParameterGroupElement (this);
440                 }
441
442                 public ProjectWhenElement CreateWhenElement (string condition)
443                 {
444                         return new ProjectWhenElement (condition, this);
445                 }
446
447                 public static ProjectRootElement Open (string path)
448                 {
449                         return Open (path, ProjectCollection.GlobalProjectCollection);
450                 }
451
452                 public static ProjectRootElement Open (string path, ProjectCollection projectCollection)
453                 {
454                         var result = Create (path, projectCollection);
455                         using (var reader = XmlReader.Create (path))
456                                 result.Load (reader);
457                         return result;
458                 }
459
460                 public void Save ()
461                 {
462                         Save (Encoding);
463                 }
464
465                 public void Save (Encoding saveEncoding)
466                 {
467                         using (var writer = new StreamWriter (File.Create (FullPath), saveEncoding)) {
468                                 Save (writer);
469                         }
470                 }
471
472                 public void Save (string path)
473                 {
474                         Save (path, Encoding);
475                 }
476
477                 public void Save (TextWriter writer)
478                 {
479                         using (var xmlWriter = XmlWriter.Create (writer, new XmlWriterSettings { Indent = true,
480                                 NewLineChars = "\r\n" })) {
481                                 Save (xmlWriter);
482                         }
483                 }
484
485                 public void Save (string path, Encoding encoding)
486                 {
487                         FullPath = path;
488                         Save (encoding);
489                 }
490
491                 public static ProjectRootElement TryOpen (string path)
492                 {
493                         return TryOpen (path, ProjectCollection.GlobalProjectCollection);
494                 }
495
496                 public static ProjectRootElement TryOpen (string path, ProjectCollection projectCollection)
497                 {
498                         // this should be non-null only if the project is already cached
499                         // and caching is not yet implemented
500                         return null;
501                 }
502
503                 internal override void Load (XmlReader reader)
504                 {
505                         try {
506                                 base.Load (reader);
507                         } catch (XmlException ex) {
508                                 throw new InvalidProjectFileException (FullPath, ex.LineNumber, ex.LinePosition, 0, 0,
509                                         ex.Message, null, null, null);
510                         }
511                 }
512
513                 internal override ProjectElement LoadChildElement (string name)
514                 {
515                         switch (name) {
516                         case "PropertyGroup":
517                                 var prop = CreatePropertyGroupElement ();
518                                 AppendChild (prop);
519                                 return prop;
520                         case "ItemGroup":
521                                 var item = CreateItemGroupElement ();
522                                 AppendChild (item);
523                                 return item;
524                         case "Import":
525                                 return AddImport (null);
526                         case "Target":
527                                 return AddTarget (null);
528                         case "ItemDefinitionGroup":
529                                 var def = CreateItemDefinitionGroupElement ();
530                                 AppendChild (def);
531                                 return def;
532                         case "UsingTask":
533                                 return AddUsingTask (null, null, null);
534                         case "Choose":
535                                 var choose = CreateChooseElement ();
536                                 AppendChild (choose);
537                                 return choose;
538                         case "ProjectExtensions":
539                                 var ext = CreateProjectExtensionsElement ();
540                                 AppendChild (ext);
541                                 return ext;
542                         default:
543                                 throw new InvalidProjectFileException (string.Format (
544                                         "Child \"{0}\" is not a known node type.", name));
545                         }
546                 }
547
548                 internal override void LoadAttribute (string name, string value)
549                 {
550                         switch (name) {
551                         case "ToolsVersion":
552                                 ToolsVersion = value;
553                                 break;
554                         case "DefaultTargets":
555                                 DefaultTargets = value;
556                                 break;
557                         case "InitialTargets":
558                                 InitialTargets = value;
559                                 break;
560                         default:
561                                 base.LoadAttribute (name, value);
562                                 break;
563                         }
564                 }
565
566                 internal override void Save (XmlWriter writer)
567                 {
568                         writer.WriteStartElement (XmlName, "http://schemas.microsoft.com/developer/msbuild/2003");
569                         SaveValue (writer);
570                         writer.WriteEndElement ();
571                 }
572
573                 internal override void SaveValue (XmlWriter writer)
574                 {
575                         SaveAttribute (writer, "ToolsVersion", ToolsVersion);
576                         SaveAttribute (writer, "DefaultTargets", DefaultTargets);
577                         SaveAttribute (writer, "InitialTargets", InitialTargets);
578                         base.SaveValue (writer);
579                 }
580
581                 internal override string XmlName {
582                         get { return "Project"; }
583                 }
584         }
585 }