Merge pull request #249 from pcc/xgetinputfocus
[mono.git] / mcs / class / System.Web.Mvc3 / Mvc / TypeCacheSerializer.cs
1 namespace System.Web.Mvc {
2     using System;
3     using System.Collections.Generic;
4     using System.Diagnostics.CodeAnalysis;
5     using System.IO;
6     using System.Linq;
7     using System.Reflection;
8     using System.Web.Mvc.Resources;
9     using System.Xml;
10
11     // Processes files with this format:
12     //
13     // <typeCache lastModified=... mvcVersionId=...>
14     //   <assembly name=...>
15     //     <module versionId=...>
16     //       <type>...</type>
17     //     </module>
18     //   </assembly>
19     // </typeCache>
20     //
21     // This is used to store caches of files between AppDomain resets, leading to improved cold boot time
22     // and more efficient use of memory.
23
24     internal sealed class TypeCacheSerializer {
25
26         private static readonly Guid _mvcVersionId = typeof(TypeCacheSerializer).Module.ModuleVersionId;
27
28         // used for unit testing
29
30         private DateTime CurrentDate {
31             get {
32                 return CurrentDateOverride ?? DateTime.Now;
33             }
34         }
35
36         internal DateTime? CurrentDateOverride {
37             get;
38             set;
39         }
40
41         [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This is an instance method for consistency with the SerializeTypes() method.")]
42         public List<Type> DeserializeTypes(TextReader input) {
43             XmlDocument doc = new XmlDocument();
44             doc.Load(input);
45             XmlElement rootElement = doc.DocumentElement;
46
47             Guid readMvcVersionId = new Guid(rootElement.Attributes["mvcVersionId"].Value);
48             if (readMvcVersionId != _mvcVersionId) {
49                 // The cache is outdated because the cache file was produced by a different version
50                 // of MVC.
51                 return null;
52             }
53
54             List<Type> deserializedTypes = new List<Type>();
55             foreach (XmlNode assemblyNode in rootElement.ChildNodes) {
56                 string assemblyName = assemblyNode.Attributes["name"].Value;
57                 Assembly assembly = Assembly.Load(assemblyName);
58
59                 foreach (XmlNode moduleNode in assemblyNode.ChildNodes) {
60                     Guid moduleVersionId = new Guid(moduleNode.Attributes["versionId"].Value);
61
62                     foreach (XmlNode typeNode in moduleNode.ChildNodes) {
63                         string typeName = typeNode.InnerText;
64                         Type type = assembly.GetType(typeName);
65                         if (type == null || type.Module.ModuleVersionId != moduleVersionId) {
66                             // The cache is outdated because we couldn't find a previously recorded
67                             // type or the type's containing module was modified.
68                             return null;
69                         }
70                         else {
71                             deserializedTypes.Add(type);
72                         }
73                     }
74                 }
75             }
76
77             return deserializedTypes;
78         }
79
80         public void SerializeTypes(IEnumerable<Type> types, TextWriter output) {
81             var groupedByAssembly = from type in types
82                                     group type by type.Module into groupedByModule
83                                     group groupedByModule by groupedByModule.Key.Assembly;
84
85             XmlDocument doc = new XmlDocument();
86             doc.AppendChild(doc.CreateComment(MvcResources.TypeCache_DoNotModify));
87
88             XmlElement typeCacheElement = doc.CreateElement("typeCache");
89             doc.AppendChild(typeCacheElement);
90             typeCacheElement.SetAttribute("lastModified", CurrentDate.ToString());
91             typeCacheElement.SetAttribute("mvcVersionId", _mvcVersionId.ToString());
92
93             foreach (var assemblyGroup in groupedByAssembly) {
94                 XmlElement assemblyElement = doc.CreateElement("assembly");
95                 typeCacheElement.AppendChild(assemblyElement);
96                 assemblyElement.SetAttribute("name", assemblyGroup.Key.FullName);
97
98                 foreach (var moduleGroup in assemblyGroup) {
99                     XmlElement moduleElement = doc.CreateElement("module");
100                     assemblyElement.AppendChild(moduleElement);
101                     moduleElement.SetAttribute("versionId", moduleGroup.Key.ModuleVersionId.ToString());
102
103                     foreach (Type type in moduleGroup) {
104                         XmlElement typeElement = doc.CreateElement("type");
105                         moduleElement.AppendChild(typeElement);
106                         typeElement.AppendChild(doc.CreateTextNode(type.FullName));
107                     }
108                 }
109             }
110
111             doc.Save(output);
112         }
113
114     }
115 }