fill ElementLocation items at loading. Fix Subtool-related build fix for 4.0.
[mono.git] / mcs / class / Microsoft.Build / Microsoft.Build.Construction / ProjectElement.cs
1 //
2 // ProjectElement.cs
3 //
4 // Author:
5 //   Leszek Ciesielski (skolima@gmail.com)
6 //
7 // (C) 2011 Leszek Ciesielski
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System;
30 using System.Collections.Generic;
31 using System.Xml;
32 using Microsoft.Build.Exceptions;
33
34 namespace Microsoft.Build.Construction
35 {
36         public abstract class ProjectElement
37         {
38                 internal const string MSBuildNamespace = "http://schemas.microsoft.com/developer/msbuild/2003";
39
40                 internal ProjectElement ()
41                 {
42                         linkedListNode = new LinkedListNode<ProjectElement> (this);
43                 }
44                 public ProjectRootElement ContainingProject { get; internal set; }
45                 public ProjectElement PreviousSibling {
46                         get { return LinkedListNode.Previous == null ? null : LinkedListNode.Previous.Value; }
47                         internal set { }
48                 }
49                 public ProjectElementContainer Parent { get; internal set; }
50                 public ProjectElement NextSibling {
51                         get { return LinkedListNode.Next == null ? null : LinkedListNode.Next.Value; }
52                         internal set { }
53                 }
54                 string label;
55                 public string Label { get { return label ?? String.Empty; } set { label = value; } }
56                 string condition;
57                 public virtual string Condition { get { return condition ?? String.Empty; } set { condition = value; } }
58                 public IEnumerable<ProjectElementContainer> AllParents {
59                         get {
60                                 var parent = Parent;
61                                 while (parent != null) {
62                                         yield return parent;
63                                         parent = parent.Parent;
64                                 }
65                         }
66                 }
67                 readonly LinkedListNode<ProjectElement> linkedListNode;
68                 internal LinkedListNode<ProjectElement> LinkedListNode { get { return linkedListNode; } }
69                 internal virtual void Load (XmlReader reader)
70                 {
71                         reader.ReadToFollowing (XmlName);
72                         FillLocation (reader);
73                         while (reader.MoveToNextAttribute ()) {
74                                 LoadAttribute (reader.Name, reader.Value);
75                         }
76                         LoadValue (reader);
77                 }
78                 internal virtual void LoadAttribute (string name, string value)
79                 {
80                         switch (name) {
81                         case "xmlns":
82                                 break;
83                         case "Label":
84                                 Label = value;
85                                 break;
86                         case "Condition":
87                                 Condition = value;
88                                 break;
89                         default:
90                                 throw new InvalidProjectFileException (string.Format (
91                                         "Attribute \"{0}\" is not known on node \"{1}\" [type {2}].", name, XmlName,
92                                         GetType ()));
93                         }
94                 }
95                 internal virtual void LoadValue (XmlReader reader)
96                 {
97                 }
98                 internal abstract string XmlName { get; }
99                 internal virtual void Save (XmlWriter writer)
100                 {
101                         writer.WriteStartElement (XmlName);
102                         SaveValue (writer);
103                         writer.WriteEndElement ();
104                 }
105                 internal virtual void SaveValue (XmlWriter writer)
106                 {
107                         SaveAttribute (writer, "Label", Label);
108                         SaveAttribute (writer, "Condition", Condition);
109                 }
110                 internal void SaveAttribute (XmlWriter writer, string attributeName, string attributeValue)
111                 {
112                         if (!string.IsNullOrWhiteSpace (attributeValue))
113                                 writer.WriteAttributeString (attributeName, attributeValue);
114                 }
115                 
116 #if NET_4_5
117                 public ElementLocation Location { get; private set; }
118                 public ElementLocation LabelLocation { get; private set; }
119                 public ElementLocation ConditionLocation { get; private set; }
120 #endif
121                 
122                 internal void FillLocation (XmlReader reader)
123                 {
124 #if NET_4_5
125                         var l = reader as IXmlLineInfo;
126                         if (l != null && l.HasLineInfo ())
127                                 Location = new ProjectElementLocation (reader.BaseURI, l);
128                         if (reader.MoveToAttribute ("Condition") && l.HasLineInfo ())
129                                 ConditionLocation = new ProjectElementLocation (reader.BaseURI, l);
130                         if (reader.MoveToAttribute ("Label") && l.HasLineInfo ())
131                                 LabelLocation = new ProjectElementLocation (reader.BaseURI, l);
132                         reader.MoveToElement ();
133 #endif
134                 }
135                 
136                 class ProjectElementLocation : ElementLocation
137                 {
138                         public ProjectElementLocation (string file, IXmlLineInfo li)
139                         {
140                                 this.file = file;
141                                 this.line = li.LineNumber;
142                                 this.column = li.LinePosition;
143                         }
144
145                         string file;
146                         int line;
147                         int column;
148
149                         public override string File { get { return file; } }
150                         public override int Line { get { return line; } }
151                         public override int Column { get { return column; } }
152                 }
153                 
154                 internal InvalidProjectFileException CreateError (XmlReader reader, string message, int columnOffset = 0)
155                 {
156                         var li = reader as IXmlLineInfo;
157                         bool valid = li != null && li.HasLineInfo ();
158                         throw new InvalidProjectFileException (reader.BaseURI, valid ? li.LineNumber : 0, valid ? li.LinePosition + columnOffset : 0, 0, 0, message, null, null, null);
159                 }
160         }
161 }