PageRenderTime 48ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/CompositeC1/Composite/Data/DataMetaDataFacade.cs

#
C# | 394 lines | 262 code | 89 blank | 43 comment | 34 complexity | 787ada9edb1684b80e81e4bf17957d58 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /*
  2. * The contents of this web application are subject to the Mozilla Public License Version
  3. * 1.1 (the "License"); you may not use this web application except in compliance with
  4. * the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/.
  5. *
  6. * Software distributed under the License is distributed on an "AS IS" basis,
  7. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  8. * for the specific language governing rights and limitations under the License.
  9. *
  10. * The Original Code is owned by and the Initial Developer of the Original Code is
  11. * Composite A/S (Danish business reg.no. 21744409). All Rights Reserved
  12. *
  13. * Section 11 of the License is EXPRESSLY amended to include a provision stating
  14. * that any dispute, including but not limited to disputes related to the enforcement
  15. * of the License, to which Composite A/S as owner of the Original Code, as Initial
  16. * Developer or in any other role, becomes a part to shall be governed by Danish law
  17. * and be initiated before the Copenhagen City Court ("K�benhavns Byret")
  18. */
  19. using System;
  20. using System.Collections.Generic;
  21. using System.Configuration;
  22. using System.IO;
  23. using System.Linq;
  24. using System.Reflection;
  25. using System.Xml;
  26. using System.Xml.Linq;
  27. using Composite.C1Console.Events;
  28. using Composite.Core;
  29. using Composite.Core.Configuration;
  30. using Composite.Core.Extensions;
  31. using Composite.Core.IO;
  32. using Composite.Core.Linq;
  33. using Composite.Core.Types;
  34. using Composite.Core.Xml;
  35. using Composite.Data.DynamicTypes;
  36. using Composite.Data.DynamicTypes.Foundation;
  37. namespace Composite.Data
  38. {
  39. /// <summary>
  40. /// </summary>
  41. /// <exclude />
  42. [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
  43. public static class DataMetaDataFacade
  44. {
  45. private static readonly string LogTitle = "DataMetaDataFacade";
  46. private static Dictionary<Guid, DataTypeDescriptor> _dataTypeDescriptorCache;
  47. private static Dictionary<Guid, string> _dataTypeDescriptorFilesnamesCache;
  48. private static readonly object _lock = new object();
  49. private static readonly string _metaDataPath;
  50. static DataMetaDataFacade()
  51. {
  52. GlobalEventSystemFacade.SubscribeToFlushEvent(args => Flush());
  53. _metaDataPath = PathUtil.Resolve(GlobalSettingsFacade.DataMetaDataDirectory);
  54. if (!C1Directory.Exists(_metaDataPath))
  55. {
  56. C1Directory.CreateDirectory(_metaDataPath);
  57. }
  58. UpdateFilenames();
  59. }
  60. private static void Initialize()
  61. {
  62. if (_dataTypeDescriptorCache != null) return;
  63. lock (_lock)
  64. {
  65. _dataTypeDescriptorCache = new Dictionary<Guid, DataTypeDescriptor>();
  66. _dataTypeDescriptorFilesnamesCache = new Dictionary<Guid, string>();
  67. string[] filepaths = C1Directory.GetFiles(_metaDataPath, "*.xml");
  68. foreach (string filepath in filepaths)
  69. {
  70. var dataTypeDescriptor = LoadFromFile(filepath);
  71. Verify.That(!_dataTypeDescriptorCache.ContainsKey(dataTypeDescriptor.DataTypeId),
  72. "Data type with id '{0}' is already added. File: '{1}'", dataTypeDescriptor.DataTypeId, filepath);
  73. _dataTypeDescriptorCache.Add(dataTypeDescriptor.DataTypeId, dataTypeDescriptor);
  74. _dataTypeDescriptorFilesnamesCache.Add(dataTypeDescriptor.DataTypeId, filepath);
  75. }
  76. }
  77. }
  78. private static DataTypeDescriptor LoadFromFile(string filePath)
  79. {
  80. XDocument doc;
  81. try
  82. {
  83. doc = XDocumentUtils.Load(filePath, LoadOptions.SetBaseUri | LoadOptions.SetLineInfo);
  84. }
  85. catch (XmlException e)
  86. {
  87. throw new ConfigurationErrorsException("Error loading meta data file '{0}': {1}".FormatWith(filePath, e.Message), e, filePath, e.LineNumber);
  88. }
  89. return DataTypeDescriptor.FromXml(doc.Root);
  90. }
  91. private static void UpdateFilenames()
  92. {
  93. List<string> filepaths = C1Directory.GetFiles(_metaDataPath, "*.xml").ToList();
  94. var ids = new Dictionary<Guid, string>();
  95. foreach (string filepath in filepaths)
  96. {
  97. Guid id = GetGuidFromFilename(filepath);
  98. if (!ids.ContainsKey(id))
  99. {
  100. ids.Add(id, filepath);
  101. }
  102. else // This should never happen, but is here to be robust
  103. {
  104. if (!IsMetaDataFileName(Path.GetFileNameWithoutExtension(filepath))) // Old version of the file, delete it
  105. {
  106. FileUtils.Delete(filepath);
  107. }
  108. else // Old version is stored in ids, delete it and change the value to new version
  109. {
  110. FileUtils.Delete(ids[id]);
  111. ids[id] = filepath;
  112. }
  113. }
  114. }
  115. foreach (var kvp in ids)
  116. {
  117. string filepath = kvp.Value;
  118. if (!IsMetaDataFileName(Path.GetFileNameWithoutExtension(filepath)))
  119. {
  120. continue;
  121. }
  122. var dataTypeDescriptor = LoadFromFile(filepath);
  123. string newFilepath = CreateFilename(dataTypeDescriptor);
  124. FileUtils.RemoveReadOnly(filepath);
  125. Func<string, string> normalizeFileName = f => f.Replace('_', ' ').ToLowerInvariant();
  126. if (normalizeFileName(filepath) != normalizeFileName(newFilepath))
  127. {
  128. C1File.Move(filepath, newFilepath);
  129. }
  130. }
  131. }
  132. private static bool IsMetaDataFileName(string fileNameWithoutExtension)
  133. {
  134. return fileNameWithoutExtension.Contains("_") || fileNameWithoutExtension.Contains(" ");
  135. }
  136. /// <exclude />
  137. public static IEnumerable<DataTypeDescriptor> AllDataTypeDescriptors
  138. {
  139. get
  140. {
  141. Initialize();
  142. return _dataTypeDescriptorCache.Values.Evaluate();
  143. }
  144. }
  145. /// <exclude />
  146. public static IEnumerable<DataTypeDescriptor> GeneratedTypeDataTypeDescriptors
  147. {
  148. get
  149. {
  150. return AllDataTypeDescriptors.Where(d => d.IsCodeGenerated);
  151. }
  152. }
  153. /// <summary>
  154. /// This method will return the data type descriptor for the given data type id.
  155. /// If the data type descriptor has not yet been created (file not existing) and
  156. /// the <paramref name="allowDataTypeCreation"/> is set to true,
  157. /// this method will try getting it through the <see cref="Composite.Data.DynamicTypes.Foundation.ReflectionBasedDescriptorBuilder"/>
  158. /// that will try locating the type from the data type id using refelction
  159. /// going through know assemblies.
  160. /// </summary>
  161. /// <param name="dataTypeId">The id of the data type.</param>
  162. /// <param name="allowDataTypeCreation">
  163. /// If this is true and the data type descriptor does not exists, it will try to
  164. /// be created.
  165. /// </param>
  166. /// <returns></returns>
  167. public static DataTypeDescriptor GetDataTypeDescriptor(Guid dataTypeId, bool allowDataTypeCreation = false)
  168. {
  169. Initialize();
  170. DataTypeDescriptor dataTypeDescriptor;
  171. _dataTypeDescriptorCache.TryGetValue(dataTypeId, out dataTypeDescriptor);
  172. if (dataTypeDescriptor != null) return dataTypeDescriptor;
  173. if (!allowDataTypeCreation) return null;
  174. foreach (Assembly assembly in AssemblyFacade.GetLoadedAssembliesFromBin())
  175. {
  176. Type[] types;
  177. try
  178. {
  179. types = assembly.GetTypes();
  180. }
  181. catch(ReflectionTypeLoadException ex)
  182. {
  183. throw new InvalidOperationException("Failed to get types from assembly '{0}'".FormatWith(assembly.FullName), ex);
  184. }
  185. foreach (Type type in types )
  186. {
  187. if (type.GetInterfaces().Contains(typeof(IData)))
  188. {
  189. ImmutableTypeIdAttribute attribute = type.GetCustomAttributes(false).OfType<ImmutableTypeIdAttribute>().SingleOrDefault();
  190. if ((attribute == null) || (attribute.ImmutableTypeId != dataTypeId)) continue;
  191. DataTypeDescriptor newDataTypeDescriptor = ReflectionBasedDescriptorBuilder.Build(type);
  192. PersistMetaData(newDataTypeDescriptor);
  193. return newDataTypeDescriptor;
  194. }
  195. }
  196. }
  197. Log.LogError(LogTitle, string.Format("No data type found with the given data type id '{0}'", dataTypeId));
  198. return null;
  199. }
  200. /// <exclude />
  201. public static void PersistMetaData(DataTypeDescriptor dataTypeDescriptor)
  202. {
  203. lock (_lock)
  204. {
  205. Initialize();
  206. string filepath = CreateFilename(dataTypeDescriptor);
  207. XElement rootElement = dataTypeDescriptor.ToXml();
  208. XDocument doc = new XDocument(rootElement);
  209. XDocumentUtils.Save(doc, filepath);
  210. _dataTypeDescriptorCache[dataTypeDescriptor.DataTypeId] = dataTypeDescriptor;
  211. if ((_dataTypeDescriptorFilesnamesCache.ContainsKey(dataTypeDescriptor.DataTypeId)) &&
  212. (_dataTypeDescriptorFilesnamesCache[dataTypeDescriptor.DataTypeId] != filepath))
  213. {
  214. FileUtils.Delete(_dataTypeDescriptorFilesnamesCache[dataTypeDescriptor.DataTypeId]);
  215. _dataTypeDescriptorFilesnamesCache[dataTypeDescriptor.DataTypeId] = filepath;
  216. }
  217. }
  218. }
  219. /// <exclude />
  220. public static void DeleteMetaData(Guid dataTypeId)
  221. {
  222. lock (_lock)
  223. {
  224. Initialize();
  225. if (_dataTypeDescriptorFilesnamesCache.ContainsKey(dataTypeId))
  226. {
  227. FileUtils.Delete(_dataTypeDescriptorFilesnamesCache[dataTypeId]);
  228. _dataTypeDescriptorCache.Remove(dataTypeId);
  229. _dataTypeDescriptorFilesnamesCache.Remove(dataTypeId);
  230. }
  231. }
  232. }
  233. private static string CreateFilename(DataTypeDescriptor dataTypeDescriptor)
  234. {
  235. return Path.Combine(_metaDataPath, string.Format("{0} {1}.xml", dataTypeDescriptor.Name, dataTypeDescriptor.DataTypeId));
  236. }
  237. private static Guid GetGuidFromFilename(string filepath)
  238. {
  239. string tmp = Path.GetFileNameWithoutExtension(filepath);
  240. int index = Math.Max(tmp.LastIndexOf('_'), tmp.LastIndexOf(' '));
  241. if (index == -1)
  242. {
  243. return new Guid(tmp);
  244. }
  245. Guid result;
  246. string guidStr = tmp.Substring(index + 1);
  247. if (!Guid.TryParse(guidStr, out result))
  248. {
  249. throw new InvalidOperationException("Failed to extract ID from file '{0}'".FormatWith(filepath));
  250. }
  251. return result;
  252. }
  253. /// <summary>
  254. /// Used for processing xml/sql data providers configuration build by C1 vesrion older than 3.0
  255. /// </summary>
  256. internal static Dictionary<string, Guid> GetTypeManagerTypeNameToTypeIdMap()
  257. {
  258. string metaDataFolderPath = PathUtil.Resolve(GlobalSettingsFacade.DataMetaDataDirectory);
  259. List<string> filepaths = C1Directory.GetFiles(metaDataFolderPath, "*.xml").ToList();
  260. var result = new Dictionary<string, Guid>();
  261. foreach (string filepath in filepaths)
  262. {
  263. try
  264. {
  265. XDocument doc = XDocumentUtils.Load(filepath);
  266. XAttribute dataTypeIdAttr = doc.Root.Attribute("dataTypeId");
  267. XAttribute typeManagerTypeNameAttr = doc.Root.Attribute("typeManagerTypeName");
  268. if (dataTypeIdAttr == null || typeManagerTypeNameAttr == null) continue;
  269. string typeManagerTypeName = typeManagerTypeNameAttr.Value;
  270. Guid dataTypeId = new Guid(dataTypeIdAttr.Value);
  271. const string redundantSuffix = ",Composite.Generated";
  272. if (typeManagerTypeName.EndsWith(redundantSuffix, StringComparison.OrdinalIgnoreCase))
  273. {
  274. typeManagerTypeName = typeManagerTypeName.Substring(0, typeManagerTypeName.Length - redundantSuffix.Length);
  275. }
  276. if (!result.ContainsKey(typeManagerTypeName))
  277. {
  278. result.Add(typeManagerTypeName, dataTypeId);
  279. }
  280. if(!typeManagerTypeName.Contains(",") && !typeManagerTypeName.StartsWith("DynamicType:"))
  281. {
  282. string fixedTypeManagerTypeName = "DynamicType:" + typeManagerTypeName;
  283. if (!result.ContainsKey(fixedTypeManagerTypeName))
  284. {
  285. result.Add(fixedTypeManagerTypeName, dataTypeId);
  286. }
  287. }
  288. }
  289. catch (Exception ex)
  290. {
  291. Log.LogWarning(LogTitle, "Error while parsing meta data file '{0}'".FormatWith(filepath));
  292. Log.LogWarning(LogTitle, ex);
  293. }
  294. }
  295. // Backward compatibility for configuraiton files. (Breaking change C1 3.2 -> C1 4.0)
  296. result["Composite.Data.Types.IPageTemplate,Composite"] = new Guid("7b54d7d2-6be6-48a6-9ae1-2e0373073d1d");
  297. return result;
  298. }
  299. private static void Flush()
  300. {
  301. _dataTypeDescriptorCache = null;
  302. _dataTypeDescriptorFilesnamesCache = null;
  303. }
  304. }
  305. }