3 using System.Reflection;
6 using SysGen.RBuild.Framework;
7 using SysGen.BuildEngine.Log;
8 using SysGen.BuildEngine.Attributes;
9 using SysGen.BuildEngine.Tasks;
11 namespace SysGen.BuildEngine
13 /// <summary>Models a NAnt XML element in the build file.</summary>
15 /// <para>Automatically validates attributes in the element based on Attribute settings in the derived class.</para>
17 public class Element : IElement
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;
27 /// The default contstructor.
33 /// <summary>A copy contstructor.</summary>
34 protected Element(Element element) : this()
36 _location = element._location;
37 _sysgen = element._sysgen;
38 _xmlNode = element._xmlNode;
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; }
48 /// The Parent object. This will be your parent Task, Target, or Project depeding on where the element is defined.
50 public IElement Parent { get { return _parent; } set { _parent = value; } }
52 /// <summary>Name of the XML element used to initialize this element.</summary>
53 public virtual string Name
56 ElementNameAttribute elementNameAttribute = (ElementNameAttribute)
57 Attribute.GetCustomAttribute(GetType(), typeof(ElementNameAttribute));
60 if (elementNameAttribute != null) {
61 name = elementNameAttribute.Name;
68 /// The <see cref="Project"/> this element belongs to.
70 public virtual SysGenEngine SysGen
72 get { return _sysgen; }
73 set { _sysgen = value; }
76 public RBuildProject Project
78 get { return _project; }
79 set { _project = value; }
82 public RBuildModule Module
86 RBuildModule module = RBuildElement as RBuildModule;
89 throw new BuildException(String.Format("Task <{0} ... \\> is not child of any ModuleTask." , Name), Location);
96 /// <see cref="Project"/> this element belongs to.
98 public virtual RBuildElement RBuildElement
102 IElement element = this;
103 while (element != null)
105 if (element is ISysGenObject)
106 return ((ISysGenObject)element).RBuildElement;
109 element = element.Parent;
112 return SysGen.Project;
116 public string BaseBuildLocation
118 get { return Path.GetDirectoryName(new Uri(_xmlNode.BaseURI).LocalPath); }
121 public XmlNode XmlNode
123 get { return _xmlNode; }
127 /// Initializes all build attributes.
129 private void InitializeProperties(XmlNode elementNode)
131 // Get the current element Type
132 Type currentType = GetType();
134 PropertyInfo[] propertyInfoArray = currentType.GetProperties(BindingFlags.Public|BindingFlags.Instance);
135 foreach (PropertyInfo propertyInfo in propertyInfoArray )
137 // process all TaskPropertyAttribute attributes
138 TaskPropertyAttribute[] propertyAttributes = (TaskPropertyAttribute[])
139 Attribute.GetCustomAttributes(propertyInfo, typeof(TaskPropertyAttribute) , false);
141 foreach(TaskPropertyAttribute propertyAttribute in propertyAttributes)
143 string propertyValue = null;
145 if (propertyAttribute.Location == TaskPropertyLocation.Attribute)
147 if (elementNode.Attributes[propertyAttribute.Name] != null)
149 propertyValue = elementNode.Attributes[propertyAttribute.Name].Value;
152 else if (propertyAttribute.Location == TaskPropertyLocation.Node)
154 propertyValue = elementNode.InnerText;
157 // check if its required
158 if (propertyValue == null && propertyAttribute.Required && m_FailOnMissingRequired)
160 throw new BuildException(String.Format("'{0}' is a required '{1}' of <{2} ... \\>.", propertyAttribute.Name, propertyAttribute.Location , Name), Location);
163 if (propertyValue != null)
165 //string attrValue = attributeNode.Value;
166 if (propertyAttribute.ExpandProperties)
168 // expand attribute properites
169 propertyValue = SysGen.ExpandProperties(propertyValue);
172 if (propertyInfo.CanWrite)
174 // set the property value instead
175 MethodInfo info = propertyInfo.GetSetMethod();
176 object[] paramaters = new object[1];
178 Type propertyType = propertyInfo.PropertyType;
180 // If the object is an emum
181 if (propertyType.IsSubclassOf(typeof(System.Enum)))
185 paramaters[0] = Enum.Parse(propertyType, propertyValue, true);
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))
193 message += string.Format("\t{0}\n", value.ToString());
195 throw new BuildException(message, Location);
200 //validate attribute value with custom ValidatorAttribute(ors)
201 ValidatorAttribute[] validateAttributes = (ValidatorAttribute[])
202 Attribute.GetCustomAttributes(propertyInfo, typeof(ValidatorAttribute));
205 foreach (ValidatorAttribute validator in validateAttributes)
206 validator.Validate(propertyValue);
208 catch (ValidationException ve)
210 throw new ValidationException(ve.Message, Location);
213 if (propertyType == typeof(System.Boolean))
215 paramaters[0] = Convert.ChangeType(SysGenConversion.ToBolean(propertyValue), propertyInfo.PropertyType);
219 paramaters[0] = Convert.ChangeType(propertyValue, propertyInfo.PropertyType);
223 info.Invoke(this, paramaters);
227 new BuildException(string.Format("Property '{0}' was found but '{1}' does no implement Set", propertyAttribute.Name, Name));
232 // now do nested BuildElements
233 BuildElementAttribute buildElementAttribute = (BuildElementAttribute)
234 Attribute.GetCustomAttribute(propertyInfo, typeof(BuildElementAttribute));
236 if (buildElementAttribute != null)
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);
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);
256 /// <summary>Performs default initialization.</summary>
258 /// <para>Derived classes that wish to add custom initialization should override <see cref="InitializeElement"/>.</para>
260 public void Initialize(XmlNode elementNode)
263 throw new InvalidOperationException("Element has invalid BuildFileLoader property.");
265 // Save the element node
266 _xmlNode = elementNode;
268 // Save position in buildfile for reporting useful error messages.
271 _location = SysGen.LocationMap.GetLocation(elementNode);
273 catch(ArgumentException ae)
275 BuildLog.WriteLineIf(SysGen.Verbose, ae.ToString());
278 InitializeProperties(elementNode);
282 // Allow inherited classes a chance to do some custom initialization.
283 InitializeElement(elementNode);
285 // The Element has been completly initialized
290 /// Allows derived classes to provide extra initialization and validation not covered by the base class.
292 /// <param name="elementNode">The xml node of the element to use for initialization.</param>
293 protected virtual void InitializeElement(XmlNode elementNode)
297 protected virtual void OnLoad()
301 protected virtual void OnInit()