bf8e11612f878ee2da3a634c3907d97c7adc2921
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / internal / Xaml / XamlLoader.cs
1 //
2 // XamlLoader.cs
3 //
4 // Author:
5 //       Stephane Delcroix <stephane@mi8.be>
6 //
7 // Copyright (c) 2013 Mobile Inception
8 // Copyright (c) 2013-2014 Xamarin, Inc
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining a copy
11 // of this software and associated documentation files (the "Software"), to deal
12 // in the Software without restriction, including without limitation the rights
13 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 // copies of the Software, and to permit persons to whom the Software is
15 // furnished to do so, subject to the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be included in
18 // all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 // THE SOFTWARE.
27
28 using System;
29 using System.Collections.Generic;
30 using System.Diagnostics;
31 using System.IO;
32 using System.Reflection;
33 using System.Text.RegularExpressions;
34 using System.Xml;
35 using Tizen.NUI.Binding;
36 using Tizen.NUI.Internals;
37
38 namespace Tizen.NUI.Xaml.Internals
39 {
40         [Obsolete ("Replaced by ResourceLoader")]
41         internal static class XamlLoader
42         {
43                 static Func<Type, string> xamlFileProvider;
44
45                 public static Func<Type, string> XamlFileProvider {
46                         get { return xamlFileProvider; }
47                         internal set {
48                                 xamlFileProvider = value;
49                                 Tizen.NUI.Xaml.DesignMode.IsDesignModeEnabled = true;
50                                 //¯\_(ツ)_/¯ the previewer forgot to set that bool
51                                 DoNotThrowOnExceptions = value != null;
52                         }
53                 }
54
55                 internal static bool DoNotThrowOnExceptions { get; set; }
56         }
57 }
58
59 namespace Tizen.NUI.Xaml
60 {
61         static class XamlLoader
62         {
63                 public static void Load(object view, Type callingType)
64                 {
65                         var xaml = GetXamlForType(callingType);
66                         if (string.IsNullOrEmpty(xaml))
67                                 throw new XamlParseException(string.Format("No embeddedresource found for {0}", callingType), new XmlLineInfo());
68                         Console.WriteLine("============= Got xaml text is {0} ===========", xaml);
69                         Load(view, xaml);
70                 }
71
72                 public static void Load(object view, string xaml)
73                 {
74                         using (var textReader = new StringReader(xaml))
75                         using (var reader = XmlReader.Create(textReader))
76                         {
77                                 while (reader.Read())
78                                 {
79                                         //Skip until element
80                                         if (reader.NodeType == XmlNodeType.Whitespace)
81                                                 continue;
82                                         if (reader.NodeType == XmlNodeType.XmlDeclaration)
83                                                 continue;
84                                         if (reader.NodeType != XmlNodeType.Element) {
85                                                 Debug.WriteLine("Unhandled node {0} {1} {2}", reader.NodeType, reader.Name, reader.Value);
86                                                 continue;
87                                         }
88
89                                         var rootnode = new RuntimeRootNode (new XmlType (reader.NamespaceURI, reader.Name, null), view, (IXmlNamespaceResolver)reader);
90                                         XamlParser.ParseXaml (rootnode, reader);
91                                         Visit (rootnode, new HydrationContext {
92                                                 RootElement = view,
93 #pragma warning disable 0618
94                                                 ExceptionHandler = ResourceLoader.ExceptionHandler ?? (Internals.XamlLoader.DoNotThrowOnExceptions ? e => { }: (Action<Exception>)null)
95 #pragma warning restore 0618
96                                         });
97                                         break;
98                                 }
99                         }
100                 }
101
102                 [Obsolete ("Use the XamlFileProvider to provide xaml files. We will remove this when Cycle 8 hits Stable.")]
103                 public static object Create (string xaml, bool doNotThrow = false)
104                 {
105                         object inflatedView = null;
106                         using (var textreader = new StringReader(xaml))
107                         using (var reader = XmlReader.Create (textreader)) {
108                                 while (reader.Read ()) {
109                                         //Skip until element
110                                         if (reader.NodeType == XmlNodeType.Whitespace)
111                                                 continue;
112                                         if (reader.NodeType == XmlNodeType.XmlDeclaration)
113                                                 continue;
114                                         if (reader.NodeType != XmlNodeType.Element) {
115                                                 Debug.WriteLine("Unhandled node {0} {1} {2}", reader.NodeType, reader.Name, reader.Value);
116                                                 continue;
117                                         }
118
119                                         var rootnode = new RuntimeRootNode (new XmlType (reader.NamespaceURI, reader.Name, null), null, (IXmlNamespaceResolver)reader);
120                                         XamlParser.ParseXaml (rootnode, reader);
121                                         var visitorContext = new HydrationContext {
122                                                 ExceptionHandler = doNotThrow ? e => { } : (Action<Exception>)null,
123                                         };
124                                         var cvv = new CreateValuesVisitor (visitorContext);
125                                         cvv.Visit ((ElementNode)rootnode, null);
126                                         inflatedView = rootnode.Root = visitorContext.Values [rootnode];
127                                         visitorContext.RootElement = inflatedView as BindableObject;
128
129                                         Visit (rootnode, visitorContext);
130                                         break;
131                                 }
132                         }
133                         return inflatedView;
134                 }
135
136                 static void Visit (RootNode rootnode, HydrationContext visitorContext)
137                 {
138                         rootnode.Accept (new XamlNodeVisitor ((node, parent) => node.Parent = parent), null); //set parents for {StaticResource}
139                         rootnode.Accept (new ExpandMarkupsVisitor (visitorContext), null);
140                         rootnode.Accept (new PruneIgnoredNodesVisitor(), null);
141                         rootnode.Accept (new NamescopingVisitor (visitorContext), null); //set namescopes for {x:Reference}
142                         rootnode.Accept (new CreateValuesVisitor (visitorContext), null);
143                         rootnode.Accept (new RegisterXNamesVisitor (visitorContext), null);
144                         rootnode.Accept (new FillResourceDictionariesVisitor (visitorContext), null);
145                         rootnode.Accept (new ApplyPropertiesVisitor (visitorContext, true), null);
146                 }
147
148                 static string GetXamlForType(Type type)
149                 {
150                         //the Previewer might want to provide it's own xaml for this... let them do that
151                         //the check at the end is preferred (using ResourceLoader). keep this until all the previewers are updated
152
153                         string xaml;
154 #pragma warning disable 0618
155                         if (ResourceLoader.ResourceProvider == null && (xaml = Internals.XamlLoader.XamlFileProvider?.Invoke(type)) != null)
156                                 return xaml;
157 #pragma warning restore 0618
158
159                         var assembly = type.GetTypeInfo().Assembly;
160                         var resourceId = XamlResourceIdAttribute.GetResourceIdForType(type);
161
162                         if (resourceId == null)
163                                 return LegacyGetXamlForType(type);
164
165                         using (var stream = assembly.GetManifestResourceStream(resourceId)) {
166                                 if (stream != null)
167                                         using (var reader = new StreamReader(stream))
168                                                 xaml = reader.ReadToEnd();
169                                 else
170                                         xaml = null;
171                         }
172
173                         var alternateXaml = ResourceLoader.ResourceProvider?.Invoke(assembly.GetName(), XamlResourceIdAttribute.GetPathForType(type));
174                         return alternateXaml ?? xaml;
175                 }
176
177                 //if the assembly was generated using a version of XamlG that doesn't outputs XamlResourceIdAttributes, we still need to find the resource, and load it
178                 static readonly Dictionary<Type, string> XamlResources = new Dictionary<Type, string>();                
179                 static string LegacyGetXamlForType(Type type)
180                 {
181                         var assembly = type.GetTypeInfo().Assembly;
182
183                         string resourceId;
184                         if (XamlResources.TryGetValue(type, out resourceId)) {
185                                 var result = ReadResourceAsXaml(type, assembly, resourceId);
186                                 if (result != null)
187                                         return result;
188                         }
189
190                         var likelyResourceName = type.Name + ".xaml";
191                         var resourceNames = assembly.GetManifestResourceNames();
192                         string resourceName = null;
193
194                         // first pass, pray to find it because the user named it correctly
195
196                         foreach (var resource in resourceNames) {
197                                 if (ResourceMatchesFilename(assembly, resource, likelyResourceName)) {
198                                         resourceName = resource;
199                                         var xaml = ReadResourceAsXaml(type, assembly, resource);
200                                         if (xaml != null)
201                                                 return xaml;
202                                 }
203                         }
204
205                         // okay maybe they at least named it .xaml
206
207                         foreach (var resource in resourceNames) {
208                                 if (!resource.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase))
209                                         continue;
210
211                                 resourceName = resource;
212                                 var xaml = ReadResourceAsXaml(type, assembly, resource);
213                                 if (xaml != null)
214                                         return xaml;
215                         }
216
217                         foreach (var resource in resourceNames) {
218                                 if (resource.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase))
219                                         continue;
220
221                                 resourceName = resource;
222                                 var xaml = ReadResourceAsXaml(type, assembly, resource, true);
223                                 if (xaml != null)
224                                         return xaml;
225                         }
226
227                         return null;
228                 }
229
230                 //legacy...
231                 static bool ResourceMatchesFilename(Assembly assembly, string resource, string filename)
232                 {
233                         try {
234                                 var info = assembly.GetManifestResourceInfo(resource);
235
236                                 if (!string.IsNullOrEmpty(info.FileName) &&
237                                         string.Compare(info.FileName, filename, StringComparison.OrdinalIgnoreCase) == 0)
238                                         return true;
239                         }
240                         catch (PlatformNotSupportedException) {
241                                 // Because Win10 + .NET Native
242                         }
243
244                         if (resource.EndsWith("." + filename, StringComparison.OrdinalIgnoreCase) ||
245                                 string.Compare(resource, filename, StringComparison.OrdinalIgnoreCase) == 0)
246                                 return true;
247
248                         return false;
249                 }
250
251                 //part of the legacy as well...
252                 static string ReadResourceAsXaml(Type type, Assembly assembly, string likelyTargetName, bool validate = false)
253                 {
254                         using (var stream = assembly.GetManifestResourceStream(likelyTargetName))
255                         using (var reader = new StreamReader(stream)) {
256                                 if (validate) {
257                                         // terrible validation of XML. Unfortunately it will probably work most of the time since comments
258                                         // also start with a <. We can't bring in any real deps.
259
260                                         var firstNonWhitespace = (char)reader.Read();
261                                         while (char.IsWhiteSpace(firstNonWhitespace))
262                                                 firstNonWhitespace = (char)reader.Read();
263
264                                         if (firstNonWhitespace != '<')
265                                                 return null;
266
267                                         stream.Seek(0, SeekOrigin.Begin);
268                                 }
269
270                                 var xaml = reader.ReadToEnd();
271
272                                 var pattern = String.Format("x:Class *= *\"{0}\"", type.FullName);
273                                 var regex = new Regex(pattern, RegexOptions.ECMAScript);
274                                 if (regex.IsMatch(xaml) || xaml.Contains(String.Format("x:Class=\"{0}\"", type.FullName)))
275                                         return xaml;
276                         }
277                         return null;
278                 }
279
280                 public class RuntimeRootNode : RootNode
281                 {
282                         public RuntimeRootNode(XmlType xmlType, object root, IXmlNamespaceResolver resolver) : base (xmlType, resolver)
283                         {
284                                 Root = root;
285                         }
286
287                         public object Root { get; internal set; }
288                 }
289         }
290 }