[RTL]
[reactos.git] / reactos / tools / sysgen / SysGen.BuildEngine / Elements / Base / Element.cs
1 using System;
2 using System.IO;
3 using System.Reflection;
4 using System.Xml;
5
6 using SysGen.RBuild.Framework;
7 using SysGen.BuildEngine.Log;
8 using SysGen.BuildEngine.Attributes;
9 using SysGen.BuildEngine.Tasks;
10
11 namespace SysGen.BuildEngine
12 {
13 /// <summary>Models a NAnt XML element in the build file.</summary>
14 /// <remarks>
15 /// <para>Automatically validates attributes in the element based on Attribute settings in the derived class.</para>
16 /// </remarks>
17 public class Element : IElement
18 {
19 protected Location _location = Location.UnknownLocation;
20 protected SysGenEngine _sysgen = null;
21 protected RBuildProject _project = null;
22 protected XmlNode _xmlNode = null;
23 protected IElement _parent = null;
24 protected bool m_FailOnMissingRequired = true;
25
26 /// <summary>
27 /// The default contstructor.
28 /// </summary>
29 public Element()
30 {
31 }
32
33 /// <summary>A copy contstructor.</summary>
34 protected Element(Element element) : this()
35 {
36 _location = element._location;
37 _sysgen = element._sysgen;
38 _xmlNode = element._xmlNode;
39 }
40
41 /// <summary><see cref="Location"/> in the build file where the element is defined.</summary>
42 protected virtual Location Location {
43 get { return _location; }
44 set { _location = value; }
45 }
46
47 /// <summary>
48 /// The Parent object. This will be your parent Task, Target, or Project depeding on where the element is defined.
49 /// </summary>
50 public IElement Parent { get { return _parent; } set { _parent = value; } }
51
52 /// <summary>Name of the XML element used to initialize this element.</summary>
53 public virtual string Name
54 {
55 get {
56 ElementNameAttribute elementNameAttribute = (ElementNameAttribute)
57 Attribute.GetCustomAttribute(GetType(), typeof(ElementNameAttribute));
58
59 string name = null;
60 if (elementNameAttribute != null) {
61 name = elementNameAttribute.Name;
62 }
63 return name;
64 }
65 }
66
67 /// <summary>
68 /// The <see cref="Project"/> this element belongs to.
69 /// </summary>
70 public virtual SysGenEngine SysGen
71 {
72 get { return _sysgen; }
73 set { _sysgen = value; }
74 }
75
76 public RBuildProject Project
77 {
78 get { return _project; }
79 set { _project = value; }
80 }
81
82 public RBuildModule Module
83 {
84 get
85 {
86 RBuildModule module = RBuildElement as RBuildModule;
87
88 if (module == null)
89 throw new BuildException(String.Format("Task <{0} ... \\> is not child of any ModuleTask." , Name), Location);
90
91 return module;
92 }
93 }
94
95 /// <summary>
96 /// <see cref="Project"/> this element belongs to.
97 /// </summary>
98 public virtual RBuildElement RBuildElement
99 {
100 get
101 {
102 IElement element = this;
103 while (element != null)
104 {
105 if (element is ISysGenObject)
106 return ((ISysGenObject)element).RBuildElement;
107
108 //Set to his parent
109 element = element.Parent;
110 }
111
112 return SysGen.Project;
113 }
114 }
115
116 public string BaseBuildLocation
117 {
118 get { return Path.GetDirectoryName(new Uri(_xmlNode.BaseURI).LocalPath); }
119 }
120
121 public XmlNode XmlNode
122 {
123 get { return _xmlNode; }
124 }
125
126 /// <summary>
127 /// Initializes all build attributes.
128 /// </summary>
129 private void InitializeProperties(XmlNode elementNode)
130 {
131 // Get the current element Type
132 Type currentType = GetType();
133
134 PropertyInfo[] propertyInfoArray = currentType.GetProperties(BindingFlags.Public|BindingFlags.Instance);
135 foreach (PropertyInfo propertyInfo in propertyInfoArray )
136 {
137 // process all TaskPropertyAttribute attributes
138 TaskPropertyAttribute[] propertyAttributes = (TaskPropertyAttribute[])
139 Attribute.GetCustomAttributes(propertyInfo, typeof(TaskPropertyAttribute) , false);
140
141 foreach(TaskPropertyAttribute propertyAttribute in propertyAttributes)
142 {
143 string propertyValue = null;
144
145 if (propertyAttribute.Location == TaskPropertyLocation.Attribute)
146 {
147 if (elementNode.Attributes[propertyAttribute.Name] != null)
148 {
149 propertyValue = elementNode.Attributes[propertyAttribute.Name].Value;
150 }
151 }
152 else if (propertyAttribute.Location == TaskPropertyLocation.Node)
153 {
154 propertyValue = elementNode.InnerText;
155 }
156
157 // check if its required
158 if (propertyValue == null && propertyAttribute.Required && m_FailOnMissingRequired)
159 {
160 throw new BuildException(String.Format("'{0}' is a required '{1}' of <{2} ... \\>.", propertyAttribute.Name, propertyAttribute.Location , Name), Location);
161 }
162
163 if (propertyValue != null)
164 {
165 //string attrValue = attributeNode.Value;
166 if (propertyAttribute.ExpandProperties)
167 {
168 // expand attribute properites
169 propertyValue = SysGen.ExpandProperties(propertyValue);
170 }
171
172 if (propertyInfo.CanWrite)
173 {
174 // set the property value instead
175 MethodInfo info = propertyInfo.GetSetMethod();
176 object[] paramaters = new object[1];
177
178 Type propertyType = propertyInfo.PropertyType;
179
180 // If the object is an emum
181 if (propertyType.IsSubclassOf(typeof(System.Enum)))
182 {
183 try
184 {
185 paramaters[0] = Enum.Parse(propertyType, propertyValue, true);
186 }
187 catch (Exception)
188 {
189 // catch type conversion exceptions here
190 string message = string.Format("Invalid value '{0}'. Valid values for this attribute are:\n", propertyValue);
191 foreach (object value in Enum.GetValues(propertyType))
192 {
193 message += string.Format("\t{0}\n", value.ToString());
194 }
195 throw new BuildException(message, Location);
196 }
197 }
198 else
199 {
200 //validate attribute value with custom ValidatorAttribute(ors)
201 ValidatorAttribute[] validateAttributes = (ValidatorAttribute[])
202 Attribute.GetCustomAttributes(propertyInfo, typeof(ValidatorAttribute));
203 try
204 {
205 foreach (ValidatorAttribute validator in validateAttributes)
206 validator.Validate(propertyValue);
207 }
208 catch (ValidationException ve)
209 {
210 throw new ValidationException(ve.Message, Location);
211 }
212
213 if (propertyType == typeof(System.Boolean))
214 {
215 paramaters[0] = Convert.ChangeType(SysGenConversion.ToBolean(propertyValue), propertyInfo.PropertyType);
216 }
217 else
218 {
219 paramaters[0] = Convert.ChangeType(propertyValue, propertyInfo.PropertyType);
220 }
221 }
222
223 info.Invoke(this, paramaters);
224 }
225 else
226 {
227 new BuildException(string.Format("Property '{0}' was found but '{1}' does no implement Set", propertyAttribute.Name, Name));
228 }
229 }
230 }
231
232 // now do nested BuildElements
233 BuildElementAttribute buildElementAttribute = (BuildElementAttribute)
234 Attribute.GetCustomAttribute(propertyInfo, typeof(BuildElementAttribute));
235
236 if (buildElementAttribute != null)
237 {
238 // get value from xml node
239 XmlNode nestedElementNode = elementNode[buildElementAttribute.Name, elementNode.OwnerDocument.DocumentElement.NamespaceURI];
240 // check if its required
241 if (nestedElementNode == null && buildElementAttribute.Required) {
242 throw new BuildException(String.Format("'{0}' is a required element of <{1} ...//>.", buildElementAttribute.Name, this.Name), Location);
243 }
244 if (nestedElementNode != null) {
245 Element childElement = (Element)propertyInfo.GetValue(this, null);
246 // Sanity check: Ensure property wasn't null.
247 if ( childElement == null )
248 throw new BuildException(String.Format("Property '{0}' value cannot be null for <{1} ...//>", propertyInfo.Name, this.Name), Location);
249 childElement.SysGen = SysGen;
250 childElement.Initialize(nestedElementNode);
251 }
252 }
253 }
254 }
255
256 /// <summary>Performs default initialization.</summary>
257 /// <remarks>
258 /// <para>Derived classes that wish to add custom initialization should override <see cref="InitializeElement"/>.</para>
259 /// </remarks>
260 public void Initialize(XmlNode elementNode)
261 {
262 if (SysGen == null)
263 throw new InvalidOperationException("Element has invalid BuildFileLoader property.");
264
265 // Save the element node
266 _xmlNode = elementNode;
267
268 // Save position in buildfile for reporting useful error messages.
269 try
270 {
271 _location = SysGen.LocationMap.GetLocation(elementNode);
272 }
273 catch(ArgumentException ae)
274 {
275 BuildLog.WriteLineIf(SysGen.Verbose, ae.ToString());
276 }
277
278 InitializeProperties(elementNode);
279
280 OnInit();
281
282 // Allow inherited classes a chance to do some custom initialization.
283 InitializeElement(elementNode);
284
285 // The Element has been completly initialized
286 OnLoad();
287 }
288
289 /// <summary>
290 /// Allows derived classes to provide extra initialization and validation not covered by the base class.
291 /// </summary>
292 /// <param name="elementNode">The xml node of the element to use for initialization.</param>
293 protected virtual void InitializeElement(XmlNode elementNode)
294 {
295 }
296
297 protected virtual void OnLoad()
298 {
299 }
300
301 protected virtual void OnInit()
302 {
303 }
304
305 }
306 }