In class/Microsoft.Build.Engine:
[mono.git] / mcs / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / Project.cs
1 //
2 // Project.cs: Project class
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.Collections.Specialized;
34 using System.IO;
35 using System.Text;
36 using System.Xml;
37 using System.Xml.Schema;
38 using Microsoft.Build.Framework;
39 using Mono.XBuild.Framework;
40
41 namespace Microsoft.Build.BuildEngine {
42         public class Project {
43         
44                 bool                            buildEnabled;
45                 Dictionary <string, List <string>>      conditionedProperties;
46                 string[]                        defaultTargets;
47                 Encoding                        encoding;
48                 BuildItemGroup                  evaluatedItems;
49                 BuildItemGroup                  evaluatedItemsIgnoringCondition;
50                 Dictionary <string, BuildItemGroup>     evaluatedItemsByName;
51                 Dictionary <string, BuildItemGroup>     evaluatedItemsByNameIgnoringCondition;
52                 BuildPropertyGroup              evaluatedProperties;
53                 string                          firstTargetName;
54                 string                          fullFileName;
55                 BuildPropertyGroup              globalProperties;
56                 GroupingCollection              groupingCollection;
57                 bool                            isDirty;
58                 bool                            isValidated;
59                 BuildItemGroupCollection        itemGroups;
60                 ImportCollection                imports;
61                 string[]                        initialTargets;
62                 Dictionary <string, BuildItemGroup> last_item_group_containing;
63                 bool                            needToReevaluate;
64                 Engine                          parentEngine;
65                 BuildPropertyGroupCollection    propertyGroups;
66                 string                          schemaFile;
67                 TaskDatabase                    taskDatabase;
68                 TargetCollection                targets;
69                 DateTime                        timeOfLastDirty;
70                 UsingTaskCollection             usingTasks;
71                 XmlDocument                     xmlDocument;
72                 bool                            unloaded;
73                 bool                            initialTargetsBuilt;
74
75                 static XmlNamespaceManager      manager;
76                 static string ns = "http://schemas.microsoft.com/developer/msbuild/2003";
77
78                 public Project ()
79                         : this (Engine.GlobalEngine)
80                 {
81                 }
82
83                 public Project (Engine engine)
84                 {
85                         parentEngine  = engine;
86
87                         buildEnabled = ParentEngine.BuildEnabled;
88                         xmlDocument = new XmlDocument ();
89                         xmlDocument.PreserveWhitespace = false;
90                         xmlDocument.AppendChild (xmlDocument.CreateElement ("Project", XmlNamespace));
91                         xmlDocument.DocumentElement.SetAttribute ("xmlns", ns);
92                         
93                         fullFileName = String.Empty;
94
95                         globalProperties = new BuildPropertyGroup (null, this, null, false);
96                         foreach (BuildProperty bp in parentEngine.GlobalProperties)
97                                 GlobalProperties.AddProperty (bp.Clone (true));
98                         
99                         ProcessXml ();
100                 }
101
102                 [MonoTODO ("Not tested")]
103                 public void AddNewImport (string importLocation,
104                                           string importCondition)
105                 {
106                         if (importLocation == null)
107                                 throw new ArgumentNullException ("importLocation");
108
109                         XmlElement importElement = xmlDocument.CreateElement ("Import", XmlNamespace);
110                         xmlDocument.DocumentElement.AppendChild (importElement);
111                         importElement.SetAttribute ("Project", importLocation);
112                         if (!String.IsNullOrEmpty (importCondition))
113                                 importElement.SetAttribute ("Condition", importCondition);
114
115                         Import import = new Import (importElement, this, null);
116                         imports.Add (import);
117                         MarkProjectAsDirty ();
118                         NeedToReevaluate ();
119                 }
120
121                 public BuildItem AddNewItem (string itemName,
122                                              string itemInclude)
123                 {
124                         return AddNewItem (itemName, itemInclude, false);
125                 }
126                 
127                 [MonoTODO ("Adds item not in the same place as MS")]
128                 public BuildItem AddNewItem (string itemName,
129                                              string itemInclude,
130                                              bool treatItemIncludeAsLiteral)
131                 {
132                         BuildItemGroup big;
133
134                         if (itemGroups.Count == 0)
135                                 big = AddNewItemGroup ();
136                         else {
137                                 if (last_item_group_containing.ContainsKey (itemName)) {
138                                         big = last_item_group_containing [itemName];
139                                 } else {
140                                         // FIXME: not tested
141                                         BuildItemGroup [] groups = new BuildItemGroup [itemGroups.Count];
142                                         itemGroups.CopyTo (groups, 0);
143                                         big = groups [0];
144                                 }
145                         }
146
147                         BuildItem item = big.AddNewItem (itemName, itemInclude, treatItemIncludeAsLiteral);
148                                 
149                         MarkProjectAsDirty ();
150                         NeedToReevaluate ();
151
152                         return item;
153                 }
154
155                 [MonoTODO ("Not tested")]
156                 public BuildItemGroup AddNewItemGroup ()
157                 {
158                         XmlElement element = xmlDocument.CreateElement ("ItemGroup", XmlNamespace);
159                         xmlDocument.DocumentElement.AppendChild (element);
160
161                         BuildItemGroup big = new BuildItemGroup (element, this, null, false);
162                         itemGroups.Add (big);
163                         MarkProjectAsDirty ();
164                         NeedToReevaluate ();
165
166                         return big;
167                 }
168
169                 [MonoTODO ("Ignores insertAtEndOfProject")]
170                 public BuildPropertyGroup AddNewPropertyGroup (bool insertAtEndOfProject)
171                 {
172                         XmlElement element = xmlDocument.CreateElement ("PropertyGroup", XmlNamespace);
173                         xmlDocument.DocumentElement.AppendChild (element);
174
175                         BuildPropertyGroup bpg = new BuildPropertyGroup (element, this, null, false);
176                         propertyGroups.Add (bpg);
177                         MarkProjectAsDirty ();
178                         NeedToReevaluate ();
179
180                         return bpg;
181                 }
182                 
183                 [MonoTODO ("Not tested, isn't added to TaskDatabase (no reevaluation)")]
184                 public void AddNewUsingTaskFromAssemblyFile (string taskName,
185                                                              string assemblyFile)
186                 {
187                         if (taskName == null)
188                                 throw new ArgumentNullException ("taskName");
189                         if (assemblyFile == null)
190                                 throw new ArgumentNullException ("assemblyFile");
191
192                         XmlElement element = xmlDocument.CreateElement ("UsingTask", XmlNamespace);
193                         xmlDocument.DocumentElement.AppendChild (element);
194                         element.SetAttribute ("TaskName", taskName);
195                         element.SetAttribute ("AssemblyFile", assemblyFile);
196
197                         UsingTask ut = new UsingTask (element, this, null);
198                         usingTasks.Add (ut);
199                         MarkProjectAsDirty ();
200                 }
201                 
202                 [MonoTODO ("Not tested, isn't added to TaskDatabase (no reevaluation)")]
203                 public void AddNewUsingTaskFromAssemblyName (string taskName,
204                                                              string assemblyName)
205                 {
206                         if (taskName == null)
207                                 throw new ArgumentNullException ("taskName");
208                         if (assemblyName == null)
209                                 throw new ArgumentNullException ("assemblyName");
210
211                         XmlElement element = xmlDocument.CreateElement ("UsingTask", XmlNamespace);
212                         xmlDocument.DocumentElement.AppendChild (element);
213                         element.SetAttribute ("TaskName", taskName);
214                         element.SetAttribute ("AssemblyName", assemblyName);
215
216                         UsingTask ut = new UsingTask (element, this, null);
217                         usingTasks.Add (ut);
218                         MarkProjectAsDirty ();
219                 }
220                 
221                 [MonoTODO ("Not tested")]
222                 public bool Build ()
223                 {
224                         return Build (new string [0]);
225                 }
226                 
227                 [MonoTODO ("Not tested")]
228                 public bool Build (string targetName)
229                 {
230                         if (targetName == null)
231                                 return Build ((string[]) null);
232                         else
233                                 return Build (new string [1] { targetName });
234                 }
235                 
236                 [MonoTODO ("Not tested")]
237                 public bool Build (string [] targetNames)
238                 {
239                         return Build (targetNames, null);
240                 }
241                 
242                 [MonoTODO ("Not tested")]
243                 public bool Build (string [] targetNames,
244                                    IDictionary targetOutputs)
245                 {
246                         return Build (targetNames, targetOutputs, BuildSettings.None);
247                 }
248                 
249                 [MonoTODO ("Not tested")]
250                 public bool Build (string [] targetNames,
251                                    IDictionary targetOutputs,
252                                    BuildSettings buildFlags)
253                 
254                 {
255                         CheckUnloaded ();
256                         ParentEngine.StartBuild ();
257                         NeedToReevaluate ();
258                         
259                         if (targetNames == null || targetNames.Length == 0) {
260                                 if (defaultTargets != null && defaultTargets.Length != 0)
261                                         targetNames = defaultTargets;
262                                 else if (firstTargetName != null)
263                                         targetNames = new string [1] { firstTargetName};
264                                 else
265                                         return false;
266                         }
267
268                         if (!initialTargetsBuilt && initialTargets != null && initialTargets.Length > 0) {
269                                 foreach (string target in initialTargets) {
270                                         if (!BuildTarget (target.Trim (), targetOutputs))
271                                                 return false;
272                                 }
273                                 initialTargetsBuilt = true;
274                         }
275
276                         foreach (string target in targetNames)
277                                 if (!BuildTarget (target.Trim (), targetOutputs))
278                                         return false;
279                                 
280                         return true;
281                 }
282
283                 bool BuildTarget (string target, IDictionary targetOutputs)
284                 {
285                         if (target == null)
286                                 throw new ArgumentException ("targetNames cannot contain null strings");
287
288                         if (!targets.Exists (target)) {
289                                 //FIXME: Log this!
290                                 Console.WriteLine ("Target named '{0}' not found in the project.", target);
291                                 return false;
292                         }
293
294                         if (!targets [target].Build ())
295                                 return false;
296
297                         if (targetOutputs != null)
298                                 targetOutputs.Add (target, targets [target].Outputs);
299
300                         return true;
301                 }
302
303                 [MonoTODO]
304                 public string [] GetConditionedPropertyValues (string propertyName)
305                 {
306                         if (conditionedProperties.ContainsKey (propertyName))
307                                 return conditionedProperties [propertyName].ToArray ();
308                         else
309                                 return new string [0];
310                 }
311
312                 public BuildItemGroup GetEvaluatedItemsByName (string itemName)
313                 {                       
314                         if (needToReevaluate) {
315                                 needToReevaluate = false;
316                                 Reevaluate ();
317                         }
318
319                         if (evaluatedItemsByName.ContainsKey (itemName))
320                                 return evaluatedItemsByName [itemName];
321                         else
322                                 return new BuildItemGroup ();
323                 }
324
325                 public BuildItemGroup GetEvaluatedItemsByNameIgnoringCondition (string itemName)
326                 {
327                         if (needToReevaluate) {
328                                 needToReevaluate = false;
329                                 Reevaluate ();
330                         }
331
332                         if (evaluatedItemsByNameIgnoringCondition.ContainsKey (itemName))
333                                 return evaluatedItemsByNameIgnoringCondition [itemName];
334                         else
335                                 return new BuildItemGroup ();
336                 }
337
338                 public string GetEvaluatedProperty (string propertyName)
339                 {
340                         if (needToReevaluate) {
341                                 needToReevaluate = false;
342                                 Reevaluate ();
343                         }
344
345                         if (propertyName == null)
346                                 throw new ArgumentNullException ("propertyName");
347
348                         BuildProperty bp = evaluatedProperties [propertyName];
349
350                         return bp == null ? null : (string) bp;
351                 }
352
353                 [MonoTODO ("We should remember that node and not use XPath to get it")]
354                 public string GetProjectExtensions (string id)
355                 {
356                         if (id == null || id == String.Empty)
357                                 return String.Empty;
358
359                         XmlNode node = xmlDocument.SelectSingleNode (String.Format ("/tns:Project/tns:ProjectExtensions/tns:{0}", id), XmlNamespaceManager);
360
361                         if (node == null)
362                                 return String.Empty;
363                         else
364                                 return node.InnerXml;
365                 }
366
367
368                 public void Load (string projectFileName)
369                 {
370                         this.fullFileName = Path.GetFullPath (projectFileName);
371                         DoLoad (new StreamReader (projectFileName));
372                 }
373                 
374                 [MonoTODO ("Not tested")]
375                 public void Load (TextReader textReader)
376                 {
377                         fullFileName = String.Empty;
378                         DoLoad (textReader);
379                 }
380
381                 public void LoadXml (string projectXml)
382                 {
383                         fullFileName = String.Empty;
384                         DoLoad (new StringReader (projectXml));
385                         MarkProjectAsDirty ();
386                 }
387
388
389                 public void MarkProjectAsDirty ()
390                 {
391                         isDirty = true;
392                         timeOfLastDirty = DateTime.Now;
393                 }
394
395                 [MonoTODO ("Not tested")]
396                 public void RemoveAllItemGroups ()
397                 {
398                         int length = ItemGroups.Count;
399                         BuildItemGroup [] groups = new BuildItemGroup [length];
400                         ItemGroups.CopyTo (groups, 0);
401
402                         for (int i = 0; i < length; i++)
403                                 RemoveItemGroup (groups [i]);
404
405                         MarkProjectAsDirty ();
406                         NeedToReevaluate ();
407                 }
408
409                 [MonoTODO ("Not tested")]
410                 public void RemoveAllPropertyGroups ()
411                 {
412                         int length = PropertyGroups.Count;
413                         BuildPropertyGroup [] groups = new BuildPropertyGroup [length];
414                         PropertyGroups.CopyTo (groups, 0);
415
416                         for (int i = 0; i < length; i++)
417                                 RemovePropertyGroup (groups [i]);
418
419                         MarkProjectAsDirty ();
420                         NeedToReevaluate ();
421                 }
422
423                 [MonoTODO]
424                 public void RemoveItem (BuildItem itemToRemove)
425                 {
426                         if (itemToRemove == null)
427                                 throw new ArgumentNullException ("itemToRemove");
428
429                         if (!itemToRemove.FromXml && !itemToRemove.HasParent)
430                                 throw new InvalidOperationException ("The object passed in is not part of the project.");
431
432                         BuildItemGroup big = itemToRemove.ParentItemGroup;
433
434                         if (big.Count == 1) {
435                                 // ParentItemGroup for items from xml and that have parent is the same
436                                 groupingCollection.Remove (big);
437                         } else {
438                                 if (big.ParentProject != this)
439                                         throw new InvalidOperationException ("The object passed in is not part of the project.");
440
441                                 if (itemToRemove.FromXml)
442                                         big.RemoveItem (itemToRemove);
443                                 else
444                                         big.RemoveItem (itemToRemove.ParentItem);
445                         }
446
447                         MarkProjectAsDirty ();
448                         NeedToReevaluate ();
449                 }
450
451                 [MonoTODO ("Not tested")]
452                 public void RemoveItemGroup (BuildItemGroup itemGroupToRemove)
453                 {
454                         if (itemGroupToRemove == null)
455                                 throw new ArgumentNullException ("itemGroupToRemove");
456
457                         groupingCollection.Remove (itemGroupToRemove);
458                         MarkProjectAsDirty ();
459                 }
460                 
461                 [MonoTODO]
462                 // NOTE: does not modify imported projects
463                 public void RemoveItemGroupsWithMatchingCondition (string matchingCondition)
464                 {
465                         throw new NotImplementedException ();
466                 }
467
468                 [MonoTODO]
469                 public void RemoveItemsByName (string itemName)
470                 {
471                         if (itemName == null)
472                                 throw new ArgumentNullException ("itemName");
473
474                         throw new NotImplementedException ();
475                 }
476
477                 [MonoTODO ("Not tested")]
478                 public void RemovePropertyGroup (BuildPropertyGroup propertyGroupToRemove)
479                 {
480                         if (propertyGroupToRemove == null)
481                                 throw new ArgumentNullException ("propertyGroupToRemove");
482
483                         groupingCollection.Remove (propertyGroupToRemove);
484                         MarkProjectAsDirty ();
485                 }
486                 
487                 [MonoTODO]
488                 // NOTE: does not modify imported projects
489                 public void RemovePropertyGroupsWithMatchingCondition (string matchCondition)
490                 {
491                         throw new NotImplementedException ();
492                 }
493
494                 [MonoTODO]
495                 public void ResetBuildStatus ()
496                 {
497                         throw new NotImplementedException ();
498                 }
499
500                 public void Save (string projectFileName)
501                 {
502                         Save (projectFileName, Encoding.Default);
503                         isDirty = false;
504                 }
505
506                 [MonoTODO ("Ignores encoding")]
507                 public void Save (string projectFileName, Encoding encoding)
508                 {
509                         xmlDocument.Save (projectFileName);
510                         isDirty = false;
511                 }
512
513                 public void Save (TextWriter outTextWriter)
514                 {
515                         xmlDocument.Save (outTextWriter);
516                         isDirty = false;
517                 }
518
519                 public void SetImportedProperty (string propertyName,
520                                                  string propertyValue,
521                                                  string condition,
522                                                  Project importProject)
523                 {
524                         SetImportedProperty (propertyName, propertyValue, condition, importProject,
525                                 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup);
526                 }
527
528                 public void SetImportedProperty (string propertyName,
529                                                  string propertyValue,
530                                                  string condition,
531                                                  Project importedProject,
532                                                  PropertyPosition position)
533                 {
534                         SetImportedProperty (propertyName, propertyValue, condition, importedProject,
535                                 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
536                 }
537
538                 [MonoTODO]
539                 public void SetImportedProperty (string propertyName,
540                                                  string propertyValue,
541                                                  string condition,
542                                                  Project importedProject,
543                                                  PropertyPosition position,
544                                                  bool treatPropertyValueAsLiteral)
545                 {
546                         throw new NotImplementedException ();
547                 }
548
549                 public void SetProjectExtensions (string id, string xmlText)
550                 {
551                         if (id == null)
552                                 throw new ArgumentNullException ("id");
553                         if (xmlText == null)
554                                 throw new ArgumentNullException ("xmlText");
555
556                         XmlNode projectExtensions, node;
557
558                         projectExtensions = xmlDocument.SelectSingleNode ("/tns:Project/tns:ProjectExtensions", XmlNamespaceManager);
559                         
560                         if (projectExtensions == null) {
561                                 projectExtensions = xmlDocument.CreateElement ("ProjectExtensions", XmlNamespace);
562                                 xmlDocument.DocumentElement.AppendChild (projectExtensions);
563
564                                 node = xmlDocument.CreateElement (id, XmlNamespace);
565                                 node.InnerXml = xmlText;
566                                 projectExtensions.AppendChild (node);
567                         } else {
568                                 node = xmlDocument.SelectSingleNode (String.Format ("/tns:Project/tns:ProjectExtensions/tns:{0}", id), XmlNamespaceManager);
569
570                                 if (node == null) {
571                                         node = xmlDocument.CreateElement (id, XmlNamespace);
572                                         projectExtensions.AppendChild (node);
573                                 }
574                                 
575                                 node.InnerXml = xmlText;
576                                 
577                         }
578
579                         MarkProjectAsDirty ();
580                 }
581                 
582                 public void SetProperty (string propertyName,
583                                          string propertyValue)
584                 {
585                         SetProperty (propertyName, propertyValue, "true",
586                                 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
587                 }
588
589                 public void SetProperty (string propertyName,
590                                          string propertyValue,
591                                          string condition)
592                 {
593                         SetProperty (propertyName, propertyValue, condition,
594                                 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup);
595                 }
596
597                 public void SetProperty (string propertyName,
598                                          string propertyValue,
599                                          string condition,
600                                          PropertyPosition position)
601                 {
602                         SetProperty (propertyName, propertyValue, condition,
603                                 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
604                 }
605
606                 [MonoTODO]
607                 public void SetProperty (string propertyName,
608                                          string propertyValue,
609                                          string condition,
610                                          PropertyPosition position,
611                                          bool treatPropertyValueAsLiteral)
612                 {
613                         throw new NotImplementedException ();
614                 }
615
616                 internal void Unload ()
617                 {
618                         unloaded = true;
619                 }
620
621                 internal void CheckUnloaded ()
622                 {
623                         if (unloaded)
624                                 throw new InvalidOperationException ("This project object has been unloaded from the MSBuild engine and is no longer valid.");
625                 }
626
627                 internal void NeedToReevaluate ()
628                 {
629                         needToReevaluate = true;
630                 }
631                                 
632                 // Does the actual loading.
633                 void DoLoad (TextReader textReader)
634                 {
635                         try {
636                                 ParentEngine.RemoveLoadedProject (this);
637         
638                                 XmlReaderSettings settings = new XmlReaderSettings ();
639         
640                                 if (SchemaFile != null) {
641                                         settings.Schemas.Add (null, SchemaFile);
642                                         settings.ValidationType = ValidationType.Schema;
643                                         settings.ValidationEventHandler += new ValidationEventHandler (ValidationCallBack);
644                                 }
645         
646                                 XmlReader xmlReader = XmlReader.Create (textReader, settings);
647                                 xmlDocument.Load (xmlReader);
648
649                                 if (xmlDocument.DocumentElement.Name != "Project") {
650                                         throw new InvalidProjectFileException (String.Format (
651                                                 "The element <{0}> is unrecognized, or not supported in this context.", xmlDocument.DocumentElement.Name));
652                                 }
653         
654                                 if (xmlDocument.DocumentElement.GetAttribute ("xmlns") != ns) {
655                                         throw new InvalidProjectFileException (
656                                                 @"The default XML namespace of the project must be the MSBuild XML namespace." + 
657                                                 " If the project is authored in the MSBuild 2003 format, please add " +
658                                                 "xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\" to the <Project> element. " +
659                                                 "If the project has been authored in the old 1.0 or 1.2 format, please convert it to MSBuild 2003 format.  ");
660                                 }
661                                 ProcessXml ();
662                                 ParentEngine.AddLoadedProject (this);
663                         } catch (Exception e) {
664                                 throw new InvalidProjectFileException (String.Format ("{0}: {1}",
665                                                         fullFileName, e.Message), e);
666                         } finally {
667                                 if (textReader != null)
668                                         textReader.Close ();
669                         }
670                 }
671
672                 void Reevaluate ()
673                 {
674                         ProcessXml ();
675                 }
676
677                 void ProcessXml ()
678                 {
679                         groupingCollection = new GroupingCollection (this);
680                         imports = new ImportCollection (groupingCollection);
681                         usingTasks = new UsingTaskCollection (this);
682                         itemGroups = new BuildItemGroupCollection (groupingCollection);
683                         propertyGroups = new BuildPropertyGroupCollection (groupingCollection);
684                         targets = new TargetCollection (this);
685                         last_item_group_containing = new Dictionary <string, BuildItemGroup> ();
686                         
687                         taskDatabase = new TaskDatabase ();
688                         if (ParentEngine.DefaultTasksRegistered)
689                                 taskDatabase.CopyTasks (ParentEngine.DefaultTasks);     
690
691                         if (xmlDocument.DocumentElement.GetAttributeNode ("DefaultTargets") != null)
692                                 defaultTargets = xmlDocument.DocumentElement.GetAttribute ("DefaultTargets").Split (';');
693                         else
694                                 defaultTargets = new string [0];
695                         
696                         ProcessProjectAttributes (xmlDocument.DocumentElement.Attributes);
697                         ProcessElements (xmlDocument.DocumentElement, null);
698                         
699                         isDirty = false;
700                         Evaluate ();
701                 }
702
703                 void ProcessProjectAttributes (XmlAttributeCollection attributes)
704                 {
705                         foreach (XmlAttribute attr in attributes) {
706                                 switch (attr.Name) {
707                                 case "InitialTargets":
708                                         initialTargets = attr.Value.Split (new char [] {';'},
709                                                         StringSplitOptions.RemoveEmptyEntries);
710                                         break;
711                                 case "DefaultTargets":
712                                         defaultTargets = attr.Value.Split (new char [] {';'},
713                                                         StringSplitOptions.RemoveEmptyEntries);
714                                         break;
715                                 }
716                         }
717                 }
718
719                 internal void ProcessElements (XmlElement rootElement, ImportedProject ip)
720                 {
721                         foreach (XmlNode xn in rootElement.ChildNodes) {
722                                 if (xn is XmlElement) {
723                                         XmlElement xe = (XmlElement) xn;
724                                         switch (xe.Name) {
725                                         case "ProjectExtensions":
726                                                 AddProjectExtensions (xe);
727                                                 break;
728                                         case "Warning":
729                                         case "Message":
730                                         case "Error":
731                                                 AddMessage (xe);
732                                                 break;
733                                         case "Target":
734                                                 AddTarget (xe, ip);
735                                                 break;
736                                         case "UsingTask":
737                                                 AddUsingTask (xe, ip);
738                                                 break;
739                                         case "Import":
740                                                 AddImport (xe, ip);
741                                                 break;
742                                         case "ItemGroup":
743                                                 AddItemGroup (xe, ip);
744                                                 break;
745                                         case "PropertyGroup":
746                                                 AddPropertyGroup (xe, ip);
747                                                 break;
748                                         case  "Choose":
749                                                 AddChoose (xe);
750                                                 break;
751                                         default:
752                                                 throw new InvalidProjectFileException ("Invalid element in project file.");
753                                         }
754                                 }
755                         }
756                 }
757                 
758                 void Evaluate ()
759                 {
760                         evaluatedItems = new BuildItemGroup (null, this, null, true);
761                         evaluatedItemsIgnoringCondition = new BuildItemGroup (null, this, null, true);
762                         evaluatedItemsByName = new Dictionary <string, BuildItemGroup> (StringComparer.InvariantCultureIgnoreCase);
763                         evaluatedItemsByNameIgnoringCondition = new Dictionary <string, BuildItemGroup> (StringComparer.InvariantCultureIgnoreCase);
764                         evaluatedProperties = new BuildPropertyGroup (null, null, null, true);
765
766                         InitializeProperties ();
767
768                         groupingCollection.Evaluate ();
769
770                         //FIXME: UsingTasks aren't really evaluated. (shouldn't use expressions or anything)
771                         foreach (UsingTask usingTask in UsingTasks)
772                                 usingTask.Evaluate ();
773                 }
774
775                 void InitializeProperties ()
776                 {
777                         BuildProperty bp;
778
779                         foreach (BuildProperty gp in GlobalProperties) {
780                                 bp = new BuildProperty (gp.Name, gp.Value, PropertyType.Global);
781                                 EvaluatedProperties.AddProperty (bp);
782                         }
783                         
784                         foreach (DictionaryEntry de in Environment.GetEnvironmentVariables ()) {
785                                 bp = new BuildProperty ((string) de.Key, (string) de.Value, PropertyType.Environment);
786                                 EvaluatedProperties.AddProperty (bp);
787                         }
788
789                         EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildBinPath", parentEngine.BinPath, PropertyType.Reserved));
790
791                         // FIXME: make some internal method that will work like GetDirectoryName but output String.Empty on null/String.Empty
792                         string projectDir;
793                         if (FullFileName == String.Empty)
794                                 projectDir = Environment.CurrentDirectory;
795                         else
796                                 projectDir = Path.GetDirectoryName (FullFileName);
797
798                         EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDirectory", projectDir, PropertyType.Reserved));
799                 }
800                 
801                 void AddProjectExtensions (XmlElement xmlElement)
802                 {
803                 }
804                 
805                 void AddMessage (XmlElement xmlElement)
806                 {
807                 }
808                 
809                 void AddTarget (XmlElement xmlElement, ImportedProject importedProject)
810                 {
811                         Target target = new Target (xmlElement, this, importedProject);
812                         targets.AddTarget (target);
813                         
814                         if (firstTargetName == null)
815                                 firstTargetName = target.Name;
816                 }
817                 
818                 void AddUsingTask (XmlElement xmlElement, ImportedProject importedProject)
819                 {
820                         UsingTask usingTask;
821
822                         usingTask = new UsingTask (xmlElement, this, importedProject);
823                         UsingTasks.Add (usingTask);
824                 }
825                 
826                 void AddImport (XmlElement xmlElement, ImportedProject importingProject)
827                 {
828                         Import import;
829                         
830                         import = new Import (xmlElement, this, importingProject);
831                         Imports.Add (import);
832                 }
833                 
834                 void AddItemGroup (XmlElement xmlElement, ImportedProject importedProject)
835                 {
836                         BuildItemGroup big = new BuildItemGroup (xmlElement, this, importedProject, false);
837                         ItemGroups.Add (big);
838                 }
839                 
840                 void AddPropertyGroup (XmlElement xmlElement, ImportedProject importedProject)
841                 {
842                         BuildPropertyGroup bpg = new BuildPropertyGroup (xmlElement, this, importedProject, false);
843                         PropertyGroups.Add (bpg);
844                 }
845                 
846                 void AddChoose (XmlElement xmlElement)
847                 {
848                         BuildChoose bc = new BuildChoose (xmlElement, this);
849                         groupingCollection.Add (bc);
850                 }
851                 
852                 static void ValidationCallBack (object sender, ValidationEventArgs e)
853                 {
854                         Console.WriteLine ("Validation Error: {0}", e.Message);
855                 }
856                 
857                 public bool BuildEnabled {
858                         get {
859                                 return buildEnabled;
860                         }
861                         set {
862                                 buildEnabled = value;
863                         }
864                 }
865
866                 [MonoTODO]
867                 public Encoding Encoding {
868                         get { return encoding; }
869                 }
870
871                 public string DefaultTargets {
872                         get {
873                                 return xmlDocument.DocumentElement.GetAttribute ("DefaultTargets");
874                         }
875                         set {
876                                 xmlDocument.DocumentElement.SetAttribute ("DefaultTargets", value);
877                                 defaultTargets = value.Split (new char [] {';'}, StringSplitOptions.RemoveEmptyEntries);
878                         }
879                 }
880
881                 public BuildItemGroup EvaluatedItems {
882                         get {
883                                 if (needToReevaluate) {
884                                         needToReevaluate = false;
885                                         Reevaluate ();
886                                 }
887                                 return evaluatedItems;
888                         }
889                 }
890
891                 public BuildItemGroup EvaluatedItemsIgnoringCondition {
892                         get {
893                                 if (needToReevaluate) {
894                                         needToReevaluate = false;
895                                         Reevaluate ();
896                                 }
897                                 return evaluatedItemsIgnoringCondition;
898                         }
899                 }
900                 
901                 internal IDictionary <string, BuildItemGroup> EvaluatedItemsByName {
902                         get {
903                                 // FIXME: do we need to do this here?
904                                 if (needToReevaluate) {
905                                         needToReevaluate = false;
906                                         Reevaluate ();
907                                 }
908                                 return evaluatedItemsByName;
909                         }
910                 }
911                 
912                 internal IDictionary <string, BuildItemGroup> EvaluatedItemsByNameIgnoringCondition {
913                         get {
914                                 // FIXME: do we need to do this here?
915                                 if (needToReevaluate) {
916                                         needToReevaluate = false;
917                                         Reevaluate ();
918                                 }
919                                 return evaluatedItemsByNameIgnoringCondition;
920                         }
921                 }
922
923                 // For batching implementation
924                 Dictionary<string, BuildItemGroup> perBatchItemsByName;
925                 Dictionary<string, BuildItemGroup> commonItemsByName;
926
927                 internal void SetBatchedItems (Dictionary<string, BuildItemGroup> perBatchItemsByName, Dictionary<string, BuildItemGroup> commonItemsByName)
928                 {
929                         this.perBatchItemsByName = perBatchItemsByName;
930                         this.commonItemsByName = commonItemsByName;
931                 }
932
933                 // Honors batching
934                 internal bool TryGetEvaluatedItemByNameBatched (string itemName, out BuildItemGroup group)
935                 {
936                         if (perBatchItemsByName == null && commonItemsByName == null)
937                                 return EvaluatedItemsByName.TryGetValue (itemName, out group);
938
939                         if (perBatchItemsByName != null)
940                                 return perBatchItemsByName.TryGetValue (itemName, out group);
941
942                         if (commonItemsByName != null)
943                                 return commonItemsByName.TryGetValue (itemName, out group);
944
945                         group = null;
946                         return false;
947                 }
948
949                 internal string GetMetadataBatched (string itemName, string metadataName)
950                 {
951                         BuildItemGroup group = null;
952                         if (itemName == null) {
953                                 //unqualified, all items in a batch(bucket) have the
954                                 //same metadata values
955                                 group = GetFirst<BuildItemGroup> (perBatchItemsByName.Values);
956                                 if (group == null)
957                                         group = GetFirst<BuildItemGroup> (commonItemsByName.Values);
958                         } else {
959                                 //qualified
960                                 TryGetEvaluatedItemByNameBatched (itemName, out group);
961                         }
962
963                         if (group != null) {
964                                 foreach (BuildItem item in group) {
965                                         if (item.HasMetadata (metadataName))
966                                                 return item.GetMetadata (metadataName);
967                                 }
968                         }
969                         return String.Empty;
970                 }
971
972                 internal IEnumerable<BuildItemGroup> GetAllItemGroups ()
973                 {
974                         if (perBatchItemsByName == null && commonItemsByName == null)
975                                 foreach (BuildItemGroup group in EvaluatedItemsByName.Values)
976                                         yield return group;
977
978                         if (perBatchItemsByName != null)
979                                 foreach (BuildItemGroup group in perBatchItemsByName.Values)
980                                         yield return group;
981
982                         if (commonItemsByName != null)
983                                 foreach (BuildItemGroup group in commonItemsByName.Values)
984                                         yield return group;
985                 }
986
987                 T GetFirst<T> (ICollection<T> list)
988                 {
989                         if (list == null)
990                                 return default (T);
991
992                         foreach (T t in list)
993                                 return t;
994
995                         return default (T);
996                 }
997
998                 public BuildPropertyGroup EvaluatedProperties {
999                         get {
1000                                 if (needToReevaluate) {
1001                                         needToReevaluate = false;
1002                                         Reevaluate ();
1003                                 }
1004                                 return evaluatedProperties;
1005                         }
1006                 }
1007
1008                 public string FullFileName {
1009                         get { return fullFileName; }
1010                         set { fullFileName = value; }
1011                 }
1012
1013                 public BuildPropertyGroup GlobalProperties {
1014                         get { return globalProperties; }
1015                         set {
1016                                 if (value == null)
1017                                         throw new ArgumentNullException ("value");
1018                                 
1019                                 if (value.FromXml)
1020                                         throw new InvalidOperationException ("GlobalProperties can not be set to persisted property group.");
1021                                 
1022                                 globalProperties = value;
1023                                 NeedToReevaluate ();
1024                         }
1025                 }
1026
1027                 public bool IsDirty {
1028                         get { return isDirty; }
1029                 }
1030
1031                 public bool IsValidated {
1032                         get { return isValidated; }
1033                         set { isValidated = value; }
1034                 }
1035
1036                 public BuildItemGroupCollection ItemGroups {
1037                         get { return itemGroups; }
1038                 }
1039                 
1040                 public ImportCollection Imports {
1041                         get { return imports; }
1042                 }
1043                 
1044                 public string InitialTargets {
1045                         get {
1046                                 return xmlDocument.DocumentElement.GetAttribute ("InitialTargets");
1047                         }
1048                         set {
1049                                 xmlDocument.DocumentElement.SetAttribute ("InitialTargets", value);
1050                                 initialTargets = value.Split (new char [] {';'}, StringSplitOptions.RemoveEmptyEntries);
1051                         }
1052                 }
1053
1054                 public Engine ParentEngine {
1055                         get { return parentEngine; }
1056                 }
1057
1058                 public BuildPropertyGroupCollection PropertyGroups {
1059                         get { return propertyGroups; }
1060                 }
1061
1062                 public string SchemaFile {
1063                         get { return schemaFile; }
1064                         set { schemaFile = value; }
1065                 }
1066
1067                 public TargetCollection Targets {
1068                         get { return targets; }
1069                 }
1070
1071                 public DateTime TimeOfLastDirty {
1072                         get { return timeOfLastDirty; }
1073                 }
1074                 
1075                 public UsingTaskCollection UsingTasks {
1076                         get { return usingTasks; }
1077                 }
1078
1079                 [MonoTODO]
1080                 public string Xml {
1081                         get { return xmlDocument.InnerXml; }
1082                 }
1083
1084                 internal Dictionary <string, BuildItemGroup> LastItemGroupContaining {
1085                         get { return last_item_group_containing; }
1086                 }
1087                 
1088                 internal static XmlNamespaceManager XmlNamespaceManager {
1089                         get {
1090                                 if (manager == null) {
1091                                         manager = new XmlNamespaceManager (new NameTable ());
1092                                         manager.AddNamespace ("tns", ns);
1093                                 }
1094                                 
1095                                 return manager;
1096                         }
1097                 }
1098                 
1099                 internal TaskDatabase TaskDatabase {
1100                         get { return taskDatabase; }
1101                 }
1102                 
1103                 internal XmlDocument XmlDocument {
1104                         get { return xmlDocument; }
1105                 }
1106                 
1107                 internal static string XmlNamespace {
1108                         get { return ns; }
1109                 }
1110         }
1111 }
1112
1113 #endif