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