/src/Orchard.Tests/Stubs/StubFileSystem.cs

http://vietnamicare.codeplex.com · C# · 401 lines · 313 code · 86 blank · 2 comment · 30 complexity · 3c129cbb8eb4cf6e71b79caee0f8539a MD5 · raw file

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. using Orchard.Caching;
  7. using Orchard.Services;
  8. namespace Orchard.Tests.Stubs {
  9. public class StubFileSystem {
  10. public class Entry {
  11. public string Name { get; set; }
  12. }
  13. public class DirectoryEntry : Entry {
  14. private readonly IClock _clock;
  15. public DirectoryEntry(IClock clock) {
  16. _clock = clock;
  17. Entries = new List<Entry>();
  18. }
  19. public IList<Entry> Entries { get; private set; }
  20. public IEnumerable<FileEntry> Files {
  21. get {
  22. return Entries.OfType<FileEntry>();
  23. }
  24. }
  25. public IEnumerable<DirectoryEntry> Directories {
  26. get {
  27. return Entries.OfType<DirectoryEntry>();
  28. }
  29. }
  30. public Entry GetEntry(string name) {
  31. if (string.IsNullOrEmpty(name))
  32. throw new ArgumentException();
  33. if (name.Contains(Path.DirectorySeparatorChar) || name.Contains(Path.AltDirectorySeparatorChar))
  34. throw new ArgumentException();
  35. return Entries.FirstOrDefault(e => StringComparer.OrdinalIgnoreCase.Equals(e.Name, name));
  36. }
  37. public DirectoryEntry CreateDirectory(string name) {
  38. var entry = GetEntry(name);
  39. if (entry == null) {
  40. entry = new DirectoryEntry(_clock) { Name = name };
  41. this.Entries.Add(entry);
  42. }
  43. if (!(entry is DirectoryEntry)) {
  44. throw new InvalidOperationException(string.Format("Can't create directory \"{0}\": not a directory.", name));
  45. }
  46. return (DirectoryEntry)entry;
  47. }
  48. public FileEntry CreateFile(string name) {
  49. var entry = GetEntry(name);
  50. if (entry == null) {
  51. entry = new FileEntry (_clock) { Name = name };
  52. this.Entries.Add(entry);
  53. }
  54. if (!(entry is FileEntry)) {
  55. throw new InvalidOperationException(string.Format("Can't create file \"{0}\": not a file.", name));
  56. }
  57. return (FileEntry)entry;
  58. }
  59. }
  60. public class FileEntry : Entry {
  61. private readonly IClock _clock;
  62. public FileEntry(IClock clock) {
  63. _clock = clock;
  64. LastWriteTimeUtc = _clock.UtcNow;
  65. Content = new List<byte>();
  66. }
  67. public List<byte> Content { get; private set; }
  68. public DateTime LastWriteTimeUtc { get; set; }
  69. }
  70. public class Token : IVolatileToken {
  71. private readonly StubFileSystem _stubFileSystem;
  72. private readonly string _path;
  73. private bool _isCurrent;
  74. public Token(StubFileSystem stubFileSystem, string path) {
  75. _stubFileSystem = stubFileSystem;
  76. _path = path;
  77. _isCurrent = true;
  78. }
  79. public bool IsCurrent { get { return _isCurrent; } }
  80. public void OnChange() {
  81. _isCurrent = false;
  82. _stubFileSystem.DetachToken(_path);
  83. }
  84. }
  85. public class FileEntryWriteStream : Stream {
  86. private readonly Token _token;
  87. private readonly FileEntry _entry;
  88. private readonly IClock _clock;
  89. private long _position;
  90. public FileEntryWriteStream(Token token, StubFileSystem.FileEntry entry, IClock clock) {
  91. _token = token;
  92. _entry = entry;
  93. _clock = clock;
  94. }
  95. public override void Flush() {
  96. }
  97. public override long Seek(long offset, SeekOrigin origin) {
  98. throw new NotImplementedException();
  99. }
  100. public override void SetLength(long value) {
  101. throw new NotImplementedException();
  102. }
  103. public override int Read(byte[] buffer, int offset, int count) {
  104. throw new NotImplementedException();
  105. }
  106. public class ArrayWrapper<T> : ICollection<T> {
  107. private readonly T[] _buffer;
  108. private readonly int _offset;
  109. private readonly int _count;
  110. public ArrayWrapper(T[] buffer, int offset, int count) {
  111. _buffer = buffer;
  112. _offset = offset;
  113. _count = count;
  114. }
  115. public IEnumerator<T> GetEnumerator() {
  116. for (int i = _offset; i < _count; i++)
  117. yield return _buffer[i];
  118. }
  119. IEnumerator IEnumerable.GetEnumerator() {
  120. return GetEnumerator();
  121. }
  122. public void Add(T item) {
  123. throw new NotImplementedException();
  124. }
  125. public void Clear() {
  126. throw new NotImplementedException();
  127. }
  128. public bool Contains(T item) {
  129. throw new NotImplementedException();
  130. }
  131. public void CopyTo(T[] array, int arrayIndex) {
  132. Array.Copy(_buffer, _offset, array, arrayIndex, _count);
  133. }
  134. public bool Remove(T item) {
  135. throw new NotImplementedException();
  136. }
  137. public int Count {
  138. get { return _count; }
  139. }
  140. public bool IsReadOnly {
  141. get { return true; }
  142. }
  143. }
  144. public override void Write(byte[] buffer, int offset, int count) {
  145. if (count == 0)
  146. return;
  147. var wrapper = new ArrayWrapper<byte>(buffer, offset, count);
  148. _entry.Content.AddRange(wrapper);
  149. _entry.LastWriteTimeUtc = _clock.UtcNow;
  150. if (_token != null)
  151. _token.OnChange();
  152. _position += count;
  153. }
  154. public override bool CanRead {
  155. get { return false; }
  156. }
  157. public override bool CanSeek {
  158. get { return false; }
  159. }
  160. public override bool CanWrite {
  161. get { return true; }
  162. }
  163. public override long Length {
  164. get { return _entry.Content.Count; }
  165. }
  166. public override long Position {
  167. get { return _position; }
  168. set { throw new NotImplementedException(); }
  169. }
  170. }
  171. public class FileEntryReadStream : Stream {
  172. private readonly FileEntry _entry;
  173. private readonly IClock _clock;
  174. private int _position;
  175. public FileEntryReadStream(FileEntry entry, IClock clock) {
  176. _entry = entry;
  177. _clock = clock;
  178. }
  179. public FileEntry FileEntry { get { return _entry; } }
  180. public override void Flush() {
  181. throw new NotImplementedException();
  182. }
  183. public override long Seek(long offset, SeekOrigin origin) {
  184. switch (origin) {
  185. case SeekOrigin.Begin:
  186. _position = (int)offset;
  187. break;
  188. case SeekOrigin.Current:
  189. _position += (int) offset;
  190. break;
  191. case SeekOrigin.End:
  192. _position = _entry.Content.Count - (int) offset;
  193. break;
  194. default:
  195. throw new ArgumentOutOfRangeException("origin");
  196. }
  197. return _position;
  198. }
  199. public override void SetLength(long value) {
  200. throw new NotImplementedException();
  201. }
  202. public override int Read(byte[] buffer, int offset, int count) {
  203. int remaingCount = _entry.Content.Count - _position;
  204. count = Math.Min(count, remaingCount);
  205. _entry.Content.CopyTo(_position, buffer, offset, count);
  206. _position += count;
  207. return count;
  208. }
  209. public override void Write(byte[] buffer, int offset, int count) {
  210. throw new NotImplementedException();
  211. }
  212. public override bool CanRead {
  213. get { return true; }
  214. }
  215. public override bool CanSeek {
  216. get { return true; }
  217. }
  218. public override bool CanWrite {
  219. get { return false; }
  220. }
  221. public override long Length {
  222. get { return _entry.Content.Count; }
  223. }
  224. public override long Position {
  225. get { return _position; }
  226. set { _position = (int) value; }
  227. }
  228. }
  229. private readonly IClock _clock;
  230. private readonly DirectoryEntry _root;
  231. private readonly Dictionary<string, Weak<Token>> _tokens;
  232. public StubFileSystem(IClock clock) {
  233. _clock = clock;
  234. _root = new DirectoryEntry(_clock);
  235. _tokens = new Dictionary<string, Weak<Token>>(StringComparer.OrdinalIgnoreCase);
  236. }
  237. public DirectoryEntry GetDirectoryEntry(string path) {
  238. // Root is a special case: it has no name.
  239. if (string.IsNullOrEmpty(path))
  240. return _root;
  241. path = path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
  242. var current = _root;
  243. foreach (var name in path.Split(Path.DirectorySeparatorChar)) {
  244. current = current.GetEntry(name) as DirectoryEntry;
  245. if (current == null)
  246. break;
  247. }
  248. return current;
  249. }
  250. public DirectoryEntry CreateDirectoryEntry(string path) {
  251. // Root is a special case: it has no name.
  252. if (string.IsNullOrEmpty(path))
  253. return _root;
  254. path = path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
  255. var current = _root;
  256. foreach (var name in path.Split(Path.DirectorySeparatorChar)) {
  257. current = current.CreateDirectory(name);
  258. }
  259. return current;
  260. }
  261. public FileEntry GetFileEntry(string path) {
  262. var directoryName = Path.GetDirectoryName(path);
  263. var fileName = Path.GetFileName(path);
  264. var directory = GetDirectoryEntry(directoryName);
  265. if (directory == null)
  266. return null;
  267. return directory.GetEntry(fileName) as StubFileSystem.FileEntry;
  268. }
  269. public FileEntry CreateFileEntry(string path) {
  270. var directoryName = Path.GetDirectoryName(path);
  271. var fileName = Path.GetFileName(path);
  272. return CreateDirectoryEntry(directoryName).CreateFile(fileName);
  273. }
  274. public IVolatileToken WhenPathChanges(string path) {
  275. Token token = GetToken(path);
  276. if (token == null) {
  277. token = new Token(this, path);
  278. _tokens.Add(path, new Weak<Token>(token));
  279. }
  280. return token;
  281. }
  282. private Token GetToken(string path) {
  283. Token token = null;
  284. Weak<Token> weakRef;
  285. if (_tokens.TryGetValue(path, out weakRef))
  286. token = weakRef.Target;
  287. return token;
  288. }
  289. private void DetachToken(string path) {
  290. _tokens.Remove(path);
  291. }
  292. public Stream CreateFile(string path) {
  293. var entry = CreateFileEntry(path);
  294. entry.Content.Clear();
  295. return new FileEntryWriteStream(GetToken(path), entry, _clock);
  296. }
  297. public Stream OpenFile(string path) {
  298. var entry = GetFileEntry(path);
  299. if (entry == null)
  300. throw new InvalidOperationException();
  301. return new FileEntryReadStream(entry, _clock);
  302. }
  303. public void DeleteFile(string path) {
  304. var directoryName = Path.GetDirectoryName(path);
  305. var fileName = Path.GetFileName(path);
  306. var directory = GetDirectoryEntry(directoryName);
  307. if (directory == null)
  308. return;
  309. var entry = directory.GetEntry(fileName);
  310. if (entry == null)
  311. return;
  312. directory.Entries.Remove(entry);
  313. }
  314. }
  315. }