[NUI] Rollback split-nui (#887)
[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) 2018 Mobile Inception
8 // Copyright (c) 2018-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.ComponentModel;
31 using System.Diagnostics;
32 using System.IO;
33 using System.Reflection;
34 using System.Text.RegularExpressions;
35 using System.Xml;
36 using Tizen.NUI.BaseComponents;
37 using Tizen.NUI.Binding;
38 using Tizen.NUI.Binding.Internals;
39
40 namespace Tizen.NUI.Xaml.Internals
41 {
42     [Obsolete ("Replaced by ResourceLoader")]
43     internal static class XamlLoader
44     {
45         static Func<Type, string> xamlFileProvider;
46
47         public static Func<Type, string> XamlFileProvider {
48             get { return xamlFileProvider; }
49             internal set {
50                 xamlFileProvider = value;
51                 Tizen.NUI.Xaml.DesignMode.IsDesignModeEnabled = true;
52                 //¯\_(ツ)_/¯ the previewer forgot to set that bool
53                 DoNotThrowOnExceptions = value != null;
54             }
55         }
56
57         internal static bool DoNotThrowOnExceptions { get; set; }
58     }
59 }
60
61 namespace Tizen.NUI.Xaml
62 {
63     static internal class XamlLoader
64     {
65         public static void Load(object view, Type callingType)
66         {
67             try
68             {
69                 var xaml = GetXamlForType(callingType);
70                 if (string.IsNullOrEmpty(xaml))
71                     throw new XamlParseException(string.Format("Can't get xaml from type {0}", callingType), new XmlLineInfo());
72                 Load(view, xaml);
73             }
74             catch (XamlParseException e)
75             {
76                 Tizen.Log.Fatal("NUI", "XamlParseException e.Message: " + e.Message);
77                 Console.WriteLine("\n[FATAL] XamlParseException e.Message: {0}\n", e.Message);
78             }
79         }
80
81         public static T LoadObject<T>(string path)
82         {
83             var xaml = GetAnimationXaml(path);
84             if (string.IsNullOrEmpty(xaml))
85                 throw new XamlParseException(string.Format("No embeddedresource found for {0}", path), new XmlLineInfo());
86             Type type = typeof(T);
87             T ret = (T)type.Assembly.CreateInstance(type.FullName);
88
89             NameScopeExtensions.PushElement(ret);
90
91             using (var textReader = new StringReader(xaml))
92             using (var reader = XmlReader.Create(textReader))
93             {
94                 while (reader.Read())
95                 {
96                     //Skip until element
97                     if (reader.NodeType == XmlNodeType.Whitespace)
98                         continue;
99                     if (reader.NodeType == XmlNodeType.XmlDeclaration)
100                         continue;
101                     if (reader.NodeType != XmlNodeType.Element)
102                     {
103                         Debug.WriteLine("Unhandled node {0} {1} {2}", reader.NodeType, reader.Name, reader.Value);
104                         continue;
105                     }
106
107                     var rootnode = new RuntimeRootNode(new XmlType(reader.NamespaceURI, reader.Name, null), ret, (IXmlNamespaceResolver)reader);
108                     XamlParser.ParseXaml(rootnode, reader);
109                     Visit(rootnode, new HydrationContext
110                     {
111                         RootElement = ret,
112 #pragma warning disable 0618
113                         ExceptionHandler = ResourceLoader.ExceptionHandler ?? (Internals.XamlLoader.DoNotThrowOnExceptions ? e => { } : (Action<Exception>)null)
114 #pragma warning restore 0618
115                     });
116                     break;
117                 }
118             }
119
120             NameScopeExtensions.PopElement();
121             return ret;
122         }
123
124         public static void Load(object view, string xaml)
125         {
126             using (var textReader = new StringReader(xaml))
127             using (var reader = XmlReader.Create(textReader))
128             {
129                 while (reader.Read())
130                 {
131                     //Skip until element
132                     if (reader.NodeType == XmlNodeType.Whitespace)
133                         continue;
134                     if (reader.NodeType == XmlNodeType.XmlDeclaration)
135                         continue;
136                     if (reader.NodeType != XmlNodeType.Element) {
137                         Debug.WriteLine("Unhandled node {0} {1} {2}", reader.NodeType, reader.Name, reader.Value);
138                         continue;
139                     }
140
141                     if (view is Element)
142                     {
143                         (view as Element).IsCreateByXaml = true;
144                     }
145
146                     var rootnode = new RuntimeRootNode (new XmlType (reader.NamespaceURI, reader.Name, null), view, (IXmlNamespaceResolver)reader);
147                     XamlParser.ParseXaml (rootnode, reader);
148                     Visit (rootnode, new HydrationContext {
149                         RootElement = view,
150 #pragma warning disable 0618
151                         ExceptionHandler = ResourceLoader.ExceptionHandler ?? (Internals.XamlLoader.DoNotThrowOnExceptions ? e => { }: (Action<Exception>)null)
152 #pragma warning restore 0618
153                     });
154                     break;
155                 }
156             }
157         }
158
159         [Obsolete ("Use the XamlFileProvider to provide xaml files. We will remove this when Cycle 8 hits Stable.")]
160         public static object Create (string xaml, bool doNotThrow = false)
161         {
162             object inflatedView = null;
163             using (var textreader = new StringReader(xaml))
164             using (var reader = XmlReader.Create (textreader)) {
165                 while (reader.Read ()) {
166                     //Skip until element
167                     if (reader.NodeType == XmlNodeType.Whitespace)
168                         continue;
169                     if (reader.NodeType == XmlNodeType.XmlDeclaration)
170                         continue;
171                     if (reader.NodeType != XmlNodeType.Element) {
172                         Debug.WriteLine("Unhandled node {0} {1} {2}", reader.NodeType, reader.Name, reader.Value);
173                         continue;
174                     }
175
176                     var rootnode = new RuntimeRootNode (new XmlType (reader.NamespaceURI, reader.Name, null), null, (IXmlNamespaceResolver)reader);
177                     XamlParser.ParseXaml (rootnode, reader);
178                     var visitorContext = new HydrationContext {
179                         ExceptionHandler = doNotThrow ? e => { } : (Action<Exception>)null,
180                     };
181                     var cvv = new CreateValuesVisitor (visitorContext);
182                     cvv.Visit ((ElementNode)rootnode, null);
183                     inflatedView = rootnode.Root = visitorContext.Values [rootnode];
184                     visitorContext.RootElement = inflatedView as BindableObject;
185
186                     Visit (rootnode, visitorContext);
187                     break;
188                 }
189             }
190             return inflatedView;
191         }
192
193         static void Visit (RootNode rootnode, HydrationContext visitorContext)
194         {
195             rootnode.Accept (new XamlNodeVisitor ((node, parent) => node.Parent = parent), null); //set parents for {StaticResource}
196             rootnode.Accept (new ExpandMarkupsVisitor (visitorContext), null);
197             rootnode.Accept (new PruneIgnoredNodesVisitor(), null);
198             rootnode.Accept (new NamescopingVisitor (visitorContext), null); //set namescopes for {x:Reference}
199             rootnode.Accept (new CreateValuesVisitor (visitorContext), null);
200             rootnode.Accept (new RegisterXNamesVisitor (visitorContext), null);
201             rootnode.Accept (new FillResourceDictionariesVisitor (visitorContext), null);
202             rootnode.Accept (new ApplyPropertiesVisitor (visitorContext, true), null);
203         }
204
205         static string GetAnimationXaml(string animationXamlPath)
206         {
207             string xaml;
208             if (File.Exists(animationXamlPath))
209             {
210                 StreamReader reader = new StreamReader(animationXamlPath);
211                 xaml = reader.ReadToEnd();
212                 reader.Close();
213                 reader.Dispose();
214                 Tizen.Log.Fatal("NUI", "File is exist!, try with xaml: " + xaml);
215                 return xaml;
216             }
217
218             return null;
219         }
220         static string GetXamlForType(Type type)
221         {
222             //the Previewer might want to provide it's own xaml for this... let them do that
223             //the check at the end is preferred (using ResourceLoader). keep this until all the previewers are updated
224
225             string xaml;
226             string resourceName = type.Name + ".xaml";
227             string resource = Tizen.Applications.Application.Current.DirectoryInfo.Resource;
228
229             Tizen.Log.Fatal("NUI", "the resource path: " + resource);
230             int windowWidth = Window.Instance.Size.Width;
231             int windowHeight = Window.Instance.Size.Height;
232
233             string likelyResourcePath = resource + "layout/" + windowWidth.ToString() + "x" + windowHeight.ToString() + "/" + resourceName;
234             Tizen.Log.Fatal("NUI", "the resource path: " + likelyResourcePath);
235
236             if (!File.Exists(likelyResourcePath))
237             {
238                 likelyResourcePath = resource + "layout/" + resourceName;
239             }
240
241             //Find the xaml file in the layout folder
242             if (File.Exists(likelyResourcePath))
243             {
244                 StreamReader reader = new StreamReader(likelyResourcePath);
245                 xaml = reader.ReadToEnd();
246                 reader.Close();
247                 reader.Dispose();
248                 Tizen.Log.Fatal("NUI", "File is exist!, try with xaml: " + xaml);
249                 var pattern = String.Format("x:Class *= *\"{0}\"", type.FullName);
250                 var regex = new Regex(pattern, RegexOptions.ECMAScript);
251                 if (regex.IsMatch(xaml) || xaml.Contains(String.Format("x:Class=\"{0}\"", type.FullName)))
252                 {
253                     return xaml;
254                 }
255                 else
256                 {
257                     throw new XamlParseException(string.Format("Can't find type {0}", type.FullName), new XmlLineInfo());
258                 }
259             }
260
261             return null;
262         }
263
264         //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
265         static readonly Dictionary<Type, string> XamlResources = new Dictionary<Type, string>();
266         static string LegacyGetXamlForType(Type type)
267         {
268             var assembly = type.GetTypeInfo().Assembly;
269
270             string resourceId;
271             if (XamlResources.TryGetValue(type, out resourceId)) {
272                 var result = ReadResourceAsXaml(type, assembly, resourceId);
273                 if (result != null)
274                     return result;
275             }
276
277             var likelyResourceName = type.Name + ".xaml";
278             var resourceNames = assembly.GetManifestResourceNames();
279             string resourceName = null;
280
281             // first pass, pray to find it because the user named it correctly
282
283             foreach (var resource in resourceNames) {
284                 if (ResourceMatchesFilename(assembly, resource, likelyResourceName)) {
285                     resourceName = resource;
286                     var xaml = ReadResourceAsXaml(type, assembly, resource);
287                     if (xaml != null)
288                         return xaml;
289                 }
290             }
291
292             // okay maybe they at least named it .xaml
293
294             foreach (var resource in resourceNames) {
295                 if (!resource.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase))
296                     continue;
297
298                 resourceName = resource;
299                 var xaml = ReadResourceAsXaml(type, assembly, resource);
300                 if (xaml != null)
301                     return xaml;
302             }
303
304             foreach (var resource in resourceNames) {
305                 if (resource.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase))
306                     continue;
307
308                 resourceName = resource;
309                 var xaml = ReadResourceAsXaml(type, assembly, resource, true);
310                 if (xaml != null)
311                     return xaml;
312             }
313
314             return null;
315         }
316
317         //legacy...
318         static bool ResourceMatchesFilename(Assembly assembly, string resource, string filename)
319         {
320             try {
321                 var info = assembly.GetManifestResourceInfo(resource);
322
323                 if (!string.IsNullOrEmpty(info.FileName) &&
324                     string.Compare(info.FileName, filename, StringComparison.OrdinalIgnoreCase) == 0)
325                     return true;
326             }
327             catch (PlatformNotSupportedException) {
328                 // Because Win10 + .NET Native
329             }
330
331             if (resource.EndsWith("." + filename, StringComparison.OrdinalIgnoreCase) ||
332                 string.Compare(resource, filename, StringComparison.OrdinalIgnoreCase) == 0)
333                 return true;
334
335             return false;
336         }
337
338         //part of the legacy as well...
339         static string ReadResourceAsXaml(Type type, Assembly assembly, string likelyTargetName, bool validate = false)
340         {
341             using (var stream = assembly.GetManifestResourceStream(likelyTargetName))
342             using (var reader = new StreamReader(stream)) {
343                 if (validate) {
344                     // terrible validation of XML. Unfortunately it will probably work most of the time since comments
345                     // also start with a <. We can't bring in any real deps.
346
347                     var firstNonWhitespace = (char)reader.Read();
348                     while (char.IsWhiteSpace(firstNonWhitespace))
349                         firstNonWhitespace = (char)reader.Read();
350
351                     if (firstNonWhitespace != '<')
352                         return null;
353
354                     stream.Seek(0, SeekOrigin.Begin);
355                 }
356
357                 var xaml = reader.ReadToEnd();
358
359                 var pattern = String.Format("x:Class *= *\"{0}\"", type.FullName);
360                 var regex = new Regex(pattern, RegexOptions.ECMAScript);
361                 if (regex.IsMatch(xaml) || xaml.Contains(String.Format("x:Class=\"{0}\"", type.FullName)))
362                     return xaml;
363             }
364             return null;
365         }
366
367         public class RuntimeRootNode : RootNode
368         {
369             public RuntimeRootNode(XmlType xmlType, object root, IXmlNamespaceResolver resolver) : base (xmlType, resolver)
370             {
371                 Root = root;
372             }
373
374             public object Root { get; internal set; }
375         }
376     }
377 }