PageRenderTime 52ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/ABB.SrcML/ShortXmlFileNameMapping.cs

https://github.com/nkcsgexi/SrcML.NET
C# | 190 lines | 137 code | 12 blank | 41 comment | 22 complexity | 4f668b2e8ca1de5c486af8d298fe571e MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Text.RegularExpressions;
  8. using System.Xml;
  9. namespace ABB.SrcML {
  10. /// <summary>
  11. /// Maintains a mapping between source file paths and the paths where XML versions are stored.
  12. /// The names of the XML files are relatively short to avoid exceeding the Windows file path character limit.
  13. /// </summary>
  14. public class ShortXmlFileNameMapping : XmlFileNameMapping {
  15. private const string mappingFile = "mapping.txt";
  16. private readonly object mappingLock = new object();
  17. //maps source path to xml path
  18. //TODO should support case insensitive paths, add option in constructor
  19. private Dictionary<string, string> mapping;
  20. //maps source files names (without path) to a count of how many times that name has been seen
  21. private Dictionary<string, int> nameCount;
  22. /// <summary>
  23. /// Creates a new ShortXmlFileNameMapping.
  24. /// </summary>
  25. /// <param name="xmlDirectory">The directory for the XML files.</param>
  26. public ShortXmlFileNameMapping(string xmlDirectory)
  27. : base(xmlDirectory) {
  28. if(CheckIfDirectoryIsCaseInsensitive(xmlDirectory)) {
  29. mapping = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);
  30. nameCount = new Dictionary<string, int>(StringComparer.CurrentCultureIgnoreCase);
  31. } else {
  32. mapping = new Dictionary<string, string>();
  33. nameCount = new Dictionary<string, int>();
  34. }
  35. ReadMapping();
  36. }
  37. /// <summary>
  38. /// Returns the path for the XML file mapped to <paramref name="sourcePath"/>.
  39. /// </summary>
  40. /// <param name="sourcePath">The path for the source file.</param>
  41. /// <returns>The full path for an XML file based on <paramref name="sourcePath"/>.</returns>
  42. public override string GetXmlPath(string sourcePath) {
  43. if(string.IsNullOrWhiteSpace(sourcePath)) {
  44. throw new ArgumentException("Argument cannot be null, string.Empty, or whitespace.", "sourcePath");
  45. }
  46. sourcePath = Path.GetFullPath(sourcePath);
  47. string xmlPath;
  48. lock(mappingLock) {
  49. if(mapping.ContainsKey(sourcePath)) {
  50. xmlPath = mapping[sourcePath];
  51. } else {
  52. var sourceName = Path.GetFileName(sourcePath);
  53. int newNameNum = nameCount.ContainsKey(sourceName) ? nameCount[sourceName] + 1 : 1;
  54. nameCount[sourceName] = newNameNum;
  55. xmlPath = Path.Combine(XmlDirectory, string.Format("{0}.{1}.xml", sourceName, newNameNum));
  56. mapping[sourcePath] = xmlPath;
  57. }
  58. }
  59. return xmlPath;
  60. }
  61. /// <summary>
  62. /// Returns the path where the source file for <paramref name="xmlPath"/> is located.
  63. /// </summary>
  64. /// <param name="xmlPath">The path for the XML file.</param>
  65. /// <returns>The full path for the source file that <paramref name="xmlPath"/> is based on.</returns>
  66. public override string GetSourcePath(string xmlPath) {
  67. if(!Path.IsPathRooted(xmlPath)) {
  68. xmlPath = Path.Combine(XmlDirectory, xmlPath);
  69. }
  70. string result;
  71. lock(mappingLock) {
  72. if(mapping.ContainsValue(xmlPath)) {
  73. var sourcePath = (from kvp in mapping
  74. where kvp.Value == xmlPath
  75. select kvp.Key);
  76. result = sourcePath.FirstOrDefault();
  77. } else {
  78. result = null;
  79. }
  80. }
  81. return result;
  82. }
  83. /// <summary>
  84. /// Saves the file name mapping to the XmlDirectory.
  85. /// </summary>
  86. public override void SaveMapping() {
  87. lock(mappingLock) {
  88. using(var outFile = new StreamWriter(Path.Combine(XmlDirectory, mappingFile))) {
  89. foreach(var kvp in mapping) {
  90. outFile.WriteLine(string.Format("{0}|{1}", kvp.Key, kvp.Value));
  91. }
  92. }
  93. }
  94. }
  95. /// <summary>
  96. /// Disposes of the object. This will write the mapping file to disk.
  97. /// </summary>
  98. public override void Dispose() {
  99. SaveMapping();
  100. }
  101. /// <summary>
  102. /// Reads the mapping file in XmlDirectory.
  103. /// If this doesn't exist, it constructs a mapping from any existing SrcML files in the directory.
  104. /// </summary>
  105. protected void ReadMapping() {
  106. lock(mappingLock) {
  107. mapping.Clear();
  108. var mappingPath = Path.Combine(XmlDirectory, mappingFile);
  109. if(File.Exists(mappingPath)) {
  110. //read mapping file
  111. foreach(var line in File.ReadLines(mappingPath)) {
  112. var paths = line.Split('|');
  113. if(paths.Length != 2) {
  114. Debug.WriteLine(string.Format("Bad line found in mapping file. Expected 2 fields, has {0}: {1}", paths.Length, line));
  115. continue;
  116. }
  117. ProcessMapFileEntry(paths[0].Trim(), paths[1].Trim());
  118. }
  119. //TODO: remove file from disk
  120. } else {
  121. //mapping file doesn't exist, so construct mapping from the xml files in the directory
  122. Debug.WriteLine(string.Format("Mapping file not found: {0}", mappingPath));
  123. if(Directory.Exists(XmlDirectory)) {
  124. foreach(var xmlFile in Directory.GetFiles(XmlDirectory, "*.xml")) {
  125. var unit = XmlHelper.StreamElements(xmlFile, SRC.Unit, 0).FirstOrDefault();
  126. if(unit != null) {
  127. //should be a SrcML file
  128. var sourcePath = SrcMLElement.GetFileNameForUnit(unit);
  129. if(!string.IsNullOrWhiteSpace(sourcePath)) {
  130. ProcessMapFileEntry(sourcePath, Path.GetFullPath(xmlFile));
  131. }
  132. }
  133. }
  134. }
  135. }
  136. }
  137. }
  138. /// <summary>
  139. /// Updates the mapping data structures with the info from a single map file entry.
  140. /// </summary>
  141. /// <param name="sourcePath"></param>
  142. /// <param name="xmlPath"></param>
  143. protected void ProcessMapFileEntry(string sourcePath, string xmlPath) {
  144. lock(mappingLock) {
  145. mapping[sourcePath] = xmlPath;
  146. //determine duplicate number
  147. var m = Regex.Match(xmlPath, @"\.(\d+)\.xml$");
  148. if(m.Success) {
  149. var sourceName = Path.GetFileName(sourcePath);
  150. var nameNum = int.Parse(m.Groups[1].Value);
  151. var currMax = -1;
  152. if(nameCount.ContainsKey(sourceName)) {
  153. currMax = nameCount[sourceName];
  154. }
  155. nameCount[sourceName] = nameNum > currMax ? nameNum : currMax;
  156. }
  157. }
  158. }
  159. private bool CheckIfDirectoryIsCaseInsensitive(string directory) {
  160. bool isCaseInsensitive = false;
  161. string tempFile = string.Empty;
  162. try {
  163. if(Directory.Exists(directory)) {
  164. tempFile = Path.Combine(directory, Guid.NewGuid().ToString()).ToLower();
  165. } else {
  166. tempFile = Path.GetTempFileName().ToLower();
  167. }
  168. File.Create(tempFile).Close();
  169. isCaseInsensitive = File.Exists(tempFile.ToUpper());
  170. } finally {
  171. if(File.Exists(tempFile)) {
  172. File.Delete(tempFile);
  173. }
  174. }
  175. return isCaseInsensitive;
  176. }
  177. }
  178. }