5 // Michael Hutchinson <mhutchinson@novell.com>
6 // Ankit Jain <jankit@novell.com>
8 // Copyright (c) 2009 Novell, Inc. (http://www.novell.com)
9 // Copyright (c) 2010 Novell, Inc. (http://www.novell.com)
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System.Collections;
32 using System.Collections.Generic;
35 using System.CodeDom.Compiler;
38 using Microsoft.Build.Framework;
39 using Microsoft.Build.Utilities;
41 namespace Moonlight.Build.Tasks {
42 public class XamlG : Task {
44 public override bool Execute ()
46 if (Sources.Length == 0)
49 if (OutputFiles == null || Sources.Length != OutputFiles.Length) {
50 Log.LogError ("Number of OutputFiles must match the number of Source files");
54 var codedom_provider = GetCodeDomProviderForLanguage (Language);
55 if (codedom_provider == null) {
56 Log.LogError ("Language {0} not supported for code generation.", Language);
60 for (int i = 0; i < Sources.Length; i ++) {
61 ITaskItem source_item = Sources [i];
62 ITaskItem dest_item = OutputFiles [i];
63 if (!File.Exists (dest_item.ItemSpec) ||
64 File.GetLastWriteTime (dest_item.ItemSpec) < File.GetLastWriteTime (source_item.ItemSpec)) {
65 Log.LogMessage (MessageImportance.Low, "Generating codebehind accessors for {0}...", source_item.ItemSpec);
67 string full_source_path = source_item.GetMetadata ("FullPath");
69 if (!XamlGCompiler.GenerateFile (codedom_provider, AssemblyName, full_source_path,
70 Path.Combine (source_item.GetMetadata ("RelativeDir"),
71 Path.GetFileName (source_item.ItemSpec)),
72 dest_item.ItemSpec, Log)) {
73 Log.LogError ("Error generating {0} from {1}", full_source_path, dest_item.ItemSpec);
76 } catch (Exception e) {
77 Log.LogError ("Error generating {0} from {1}: {2}", full_source_path, dest_item.ItemSpec, e.Message);
78 Log.LogMessage (MessageImportance.Low, "Error generating {0} from {1}: {2}",
79 full_source_path, dest_item.ItemSpec, e.ToString ());
88 CodeDomProvider GetCodeDomProviderForLanguage (string lang)
90 switch (lang.ToLower ()) {
91 case "c#": return new Microsoft.CSharp.CSharpCodeProvider ();
92 case "vb": return new Microsoft.VisualBasic.VBCodeProvider ();
99 public ITaskItem [] Sources {
104 public string Language {
109 public string AssemblyName {
114 public ITaskItem [] OutputFiles {
118 bool HasFileChanged (string source, string dest)
120 if (!File.Exists (dest))
123 FileInfo sourceInfo = new FileInfo (source);
124 FileInfo destinationInfo = new FileInfo (dest);
126 return !(sourceInfo.Length == destinationInfo.Length &&
127 File.GetLastWriteTime(source) <= File.GetLastWriteTime (dest));
132 static class XamlGCompiler
134 private static bool sl2 = true;
136 public static bool GenerateFile (CodeDomProvider provider, string app_name,
137 string xaml_file, string xaml_path_in_project, string out_file, TaskLoggingHelper log)
139 XmlDocument xmldoc = new XmlDocument ();
140 xmldoc.Load (xaml_file);
142 XmlNamespaceManager nsmgr = new XmlNamespaceManager (xmldoc.NameTable);
143 nsmgr.AddNamespace("x", "http://schemas.microsoft.com/winfx/2006/xaml");
145 XmlNode root = xmldoc.SelectSingleNode ("/*", nsmgr);
147 log.LogError ("{0}: No root node found.", xaml_file);
151 XmlAttribute root_class = root.Attributes ["x:Class"];
152 if (root_class == null) {
153 File.WriteAllText (out_file, "");
157 bool is_application = root.LocalName == "Application";
162 ParseXmlns (root_class.Value, out root_type, out root_ns, out root_asm);
164 Hashtable names_and_types = GetNamesAndTypes (root, nsmgr);
165 // Hashtable keys_and_types = GetKeysAndTypes (root, nsmgr);
167 CodeCompileUnit ccu = new CodeCompileUnit ();
168 CodeNamespace decl_ns = new CodeNamespace (root_ns);
169 ccu.Namespaces.Add (decl_ns);
171 decl_ns.Imports.Add (new CodeNamespaceImport ("System"));
172 decl_ns.Imports.Add (new CodeNamespaceImport ("System.Windows"));
173 decl_ns.Imports.Add (new CodeNamespaceImport ("System.Windows.Controls"));
174 decl_ns.Imports.Add (new CodeNamespaceImport ("System.Windows.Documents"));
175 decl_ns.Imports.Add (new CodeNamespaceImport ("System.Windows.Input"));
176 decl_ns.Imports.Add (new CodeNamespaceImport ("System.Windows.Media"));
177 decl_ns.Imports.Add (new CodeNamespaceImport ("System.Windows.Media.Animation"));
178 decl_ns.Imports.Add (new CodeNamespaceImport ("System.Windows.Shapes"));
179 decl_ns.Imports.Add (new CodeNamespaceImport ("System.Windows.Controls.Primitives"));
181 CodeTypeDeclaration decl_type = new CodeTypeDeclaration (root_type);
182 decl_type.IsPartial = true;
184 decl_ns.Types.Add (decl_type);
186 CodeMemberMethod initcomp = new CodeMemberMethod ();
187 initcomp.Name = "InitializeComponent";
188 decl_type.Members.Add (initcomp);
191 CodeMemberField field = new CodeMemberField ();
192 field.Name = "_contentLoaded";
193 field.Type = new CodeTypeReference (typeof (bool));
195 decl_type.Members.Add (field);
197 CodeConditionStatement is_content_loaded = new CodeConditionStatement (new CodeVariableReferenceExpression ("_contentLoaded"),
198 new CodeStatement [] { new CodeMethodReturnStatement () });
199 initcomp.Statements.Add (is_content_loaded);
201 CodeAssignStatement set_content_loaded = new CodeAssignStatement (new CodeVariableReferenceExpression ("_contentLoaded"),
202 new CodePrimitiveExpression (true));
204 initcomp.Statements.Add (set_content_loaded);
206 string component_path = String.Format ("/{0};component/{1}", app_name, xaml_path_in_project);
207 CodeMethodInvokeExpression load_component = new CodeMethodInvokeExpression (
208 new CodeTypeReferenceExpression ("System.Windows.Application"), "LoadComponent",
209 new CodeExpression [] { new CodeThisReferenceExpression (),
210 new CodeObjectCreateExpression (new CodeTypeReference ("System.Uri"), new CodeExpression [] {
211 new CodePrimitiveExpression (component_path),
212 new CodeFieldReferenceExpression (new CodeTypeReferenceExpression ("System.UriKind"), "Relative") })
214 initcomp.Statements.Add (load_component);
217 if (!is_application) {
218 foreach (DictionaryEntry entry in names_and_types) {
219 string name = (string) entry.Key;
220 CodeTypeReference type = (CodeTypeReference) entry.Value;
222 CodeMemberField field = new CodeMemberField ();
225 field.Attributes = MemberAttributes.Assembly;
230 decl_type.Members.Add (field);
232 CodeMethodInvokeExpression find_invoke = new CodeMethodInvokeExpression (
233 new CodeThisReferenceExpression(), "FindName",
234 new CodeExpression[] { new CodePrimitiveExpression (name) } );
236 CodeCastExpression cast = new CodeCastExpression (type, find_invoke);
238 CodeAssignStatement assign = new CodeAssignStatement (
239 new CodeVariableReferenceExpression (name), cast);
241 initcomp.Statements.Add (assign);
246 using (StreamWriter writer = new StreamWriter (out_file)) {
247 provider.GenerateCodeFromCompileUnit (ccu, writer, new CodeGeneratorOptions ());
253 private static Hashtable GetNamesAndTypes (XmlNode root, XmlNamespaceManager nsmgr)
255 Hashtable res = new Hashtable ();
257 XmlNodeList names = root.SelectNodes ("//*[@x:Name]", nsmgr);
258 foreach (XmlNode node in names) {
260 // Don't take the root canvas
264 XmlAttribute attr = node.Attributes ["x:Name"];
265 string name = attr.Value;
266 string ns = GetNamespace (node);
267 string member_type = node.LocalName;
270 member_type = String.Concat (ns, ".", member_type);
272 CodeTypeReference type = new CodeTypeReference (member_type);
274 type.Options |= CodeTypeReferenceOptions.GlobalReference;
283 private static Hashtable GetKeysAndTypes (XmlNode root, XmlNamespaceManager nsmgr)
285 Hashtable res = new Hashtable ();
287 XmlNodeList keys = root.SelectNodes ("//*[@x:Key]", nsmgr);
288 foreach (XmlNode node in keys) {
290 // Don't take the root canvas
294 XmlAttribute attr = node.Attributes ["x:Key"];
295 string key = attr.Value;
296 string ns = GetNamespace (node);
297 string member_type = node.LocalName;
300 member_type = String.Concat (ns, ".", member_type);
302 res [key] = member_type;
309 internal static string GetNamespace (XmlNode node)
311 if (!IsCustom (node.NamespaceURI))
314 return ParseNamespaceFromXmlns (node.NamespaceURI);
317 private static bool IsCustom (string ns)
320 case "http://schemas.microsoft.com/winfx/2006/xaml":
321 case "http://schemas.microsoft.com/winfx/2006/xaml/presentation":
322 case "http://schemas.microsoft.com/client/2007":
329 private static string ParseNamespaceFromXmlns (string xmlns)
331 string type_name = null;
335 ParseXmlns (xmlns, out type_name, out ns, out asm);
340 // private static string ParseTypeFromXmlns (string xmlns)
342 // string type_name = null;
344 // string asm = null;
346 // ParseXmlns (xmlns, out type_name, out ns, out asm);
351 internal static void ParseXmlns (string xmlns, out string type_name, out string ns, out string asm)
357 string [] decls = xmlns.Split (';');
358 foreach (string decl in decls) {
359 if (decl.StartsWith ("clr-namespace:")) {
360 ns = decl.Substring (14, decl.Length - 14);
363 if (decl.StartsWith ("assembly=")) {
364 asm = decl.Substring (9, decl.Length - 9);
367 int nsind = decl.LastIndexOf (".");
369 ns = decl.Substring (0, nsind);
370 type_name = decl.Substring (nsind + 1, decl.Length - nsind - 1);