Merge pull request #819 from brendanzagaeski/patch-1
[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                         reader.MoveToElement ();
77                         LoadValue (reader);
78                 }
79                 internal virtual void LoadAttribute (string name, string value)
80                 {
81                         switch (name) {
82                         case "xmlns":
83                                 break;
84                         case "Label":
85                                 Label = value;
86                                 break;
87                         case "Condition":
88                                 Condition = value;
89                                 break;
90                         default:
91                                 throw new InvalidProjectFileException (Location, null, string.Format (
92                                         "Attribute \"{0}\" is not known on node \"{1}\" [type {2}].", name, XmlName,
93                                         GetType ()));
94                         }
95                 }
96                 internal virtual void LoadValue (XmlReader reader)
97                 {
98                 }
99                 internal abstract string XmlName { get; }
100                 internal virtual void Save (XmlWriter writer)
101                 {
102                         writer.WriteStartElement (XmlName);
103                         SaveValue (writer);
104                         writer.WriteEndElement ();
105                 }
106                 internal virtual void SaveValue (XmlWriter writer)
107                 {
108                         SaveAttribute (writer, "Label", Label);
109                         SaveAttribute (writer, "Condition", Condition);
110                 }
111                 internal void SaveAttribute (XmlWriter writer, string attributeName, string attributeValue)
112                 {
113                         if (!string.IsNullOrWhiteSpace (attributeValue))
114                                 writer.WriteAttributeString (attributeName, attributeValue);
115                 }
116                 
117 #if NET_4_5
118                 public ElementLocation Location { get; private set; }
119                 public ElementLocation LabelLocation { get; private set; }
120                 public ElementLocation ConditionLocation { get; private set; }
121 #else
122                 internal ElementLocation Location { get; private set; }
123                 internal ElementLocation LabelLocation { get; private set; }
124                 internal ElementLocation ConditionLocation { get; private set; }
125 #endif
126                 
127                 internal void FillLocation (XmlReader reader)
128                 {
129                         var l = reader as IXmlLineInfo;
130                         if (l != null && l.HasLineInfo ())
131                                 Location = new ProjectElementLocation (reader.BaseURI, l);
132                         if (reader.MoveToAttribute ("Condition") && l.HasLineInfo ())
133                                 ConditionLocation = new ProjectElementLocation (reader.BaseURI, l);
134                         if (reader.MoveToAttribute ("Label") && l.HasLineInfo ())
135                                 LabelLocation = new ProjectElementLocation (reader.BaseURI, l);
136                         reader.MoveToElement ();
137                 }
138                 
139                 class ProjectElementLocation : ElementLocation
140                 {
141                         public ProjectElementLocation (string file, IXmlLineInfo li)
142                         {
143                                 this.file = file;
144                                 this.line = li.LineNumber;
145                                 this.column = li.LinePosition;
146                         }
147
148                         string file;
149                         int line;
150                         int column;
151
152                         public override string File { get { return file; } }
153                         public override int Line { get { return line; } }
154                         public override int Column { get { return column; } }
155                 }
156                 
157                 internal InvalidProjectFileException CreateError (XmlReader reader, string message, int columnOffset = 0)
158                 {
159                         var li = reader as IXmlLineInfo;
160                         bool valid = li != null && li.HasLineInfo ();
161                         throw new InvalidProjectFileException (reader.BaseURI, valid ? li.LineNumber : 0, valid ? li.LinePosition + columnOffset : 0, 0, 0, message, null, null, null);
162                 }
163         }
164 }