/Framework.Common/Html/MixedCodeDocument.cs

# · C# · 453 lines · 260 code · 63 blank · 130 comment · 16 complexity · 2ff21dd9a6254302df18f86ed27db3c2 MD5 · raw file

  1. using System;
  2. using System.IO;
  3. using System.Text;
  4. namespace Framework.Common.Html
  5. {
  6. /// <summary>
  7. /// Represents a document with mixed code and text. ASP, ASPX, JSP, are good example of such documents.
  8. /// </summary>
  9. public class MixedCodeDocument
  10. {
  11. #region Fields
  12. private int _c;
  13. internal MixedCodeDocumentFragmentList _codefragments;
  14. private MixedCodeDocumentFragment _currentfragment;
  15. internal MixedCodeDocumentFragmentList _fragments;
  16. private int _index;
  17. private int _line;
  18. private int _lineposition;
  19. private ParseState _state;
  20. private Encoding _streamencoding;
  21. internal string _text;
  22. internal MixedCodeDocumentFragmentList _textfragments;
  23. /// <summary>
  24. /// Gets or sets the token representing code end.
  25. /// </summary>
  26. public string TokenCodeEnd = "%>";
  27. /// <summary>
  28. /// Gets or sets the token representing code start.
  29. /// </summary>
  30. public string TokenCodeStart = "<%";
  31. /// <summary>
  32. /// Gets or sets the token representing code directive.
  33. /// </summary>
  34. public string TokenDirective = "@";
  35. /// <summary>
  36. /// Gets or sets the token representing response write directive.
  37. /// </summary>
  38. public string TokenResponseWrite = "Response.Write ";
  39. private string TokenTextBlock = "TextBlock({0})";
  40. #endregion
  41. #region Constructors
  42. /// <summary>
  43. /// Creates a mixed code document instance.
  44. /// </summary>
  45. public MixedCodeDocument()
  46. {
  47. _codefragments = new MixedCodeDocumentFragmentList(this);
  48. _textfragments = new MixedCodeDocumentFragmentList(this);
  49. _fragments = new MixedCodeDocumentFragmentList(this);
  50. }
  51. #endregion
  52. #region Properties
  53. /// <summary>
  54. /// Gets the code represented by the mixed code document seen as a template.
  55. /// </summary>
  56. public string Code
  57. {
  58. get
  59. {
  60. string s = "";
  61. int i = 0;
  62. foreach (MixedCodeDocumentFragment frag in _fragments)
  63. {
  64. switch (frag._type)
  65. {
  66. case MixedCodeDocumentFragmentType.Text:
  67. s += TokenResponseWrite + string.Format(TokenTextBlock, i) + "\n";
  68. i++;
  69. break;
  70. case MixedCodeDocumentFragmentType.Code:
  71. s += ((MixedCodeDocumentCodeFragment) frag).Code + "\n";
  72. break;
  73. }
  74. }
  75. return s;
  76. }
  77. }
  78. /// <summary>
  79. /// Gets the list of code fragments in the document.
  80. /// </summary>
  81. public MixedCodeDocumentFragmentList CodeFragments
  82. {
  83. get { return _codefragments; }
  84. }
  85. /// <summary>
  86. /// Gets the list of all fragments in the document.
  87. /// </summary>
  88. public MixedCodeDocumentFragmentList Fragments
  89. {
  90. get { return _fragments; }
  91. }
  92. /// <summary>
  93. /// Gets the encoding of the stream used to read the document.
  94. /// </summary>
  95. public Encoding StreamEncoding
  96. {
  97. get { return _streamencoding; }
  98. }
  99. /// <summary>
  100. /// Gets the list of text fragments in the document.
  101. /// </summary>
  102. public MixedCodeDocumentFragmentList TextFragments
  103. {
  104. get { return _textfragments; }
  105. }
  106. #endregion
  107. #region Public Methods
  108. /// <summary>
  109. /// Create a code fragment instances.
  110. /// </summary>
  111. /// <returns>The newly created code fragment instance.</returns>
  112. public MixedCodeDocumentCodeFragment CreateCodeFragment()
  113. {
  114. return (MixedCodeDocumentCodeFragment) CreateFragment(MixedCodeDocumentFragmentType.Code);
  115. }
  116. /// <summary>
  117. /// Create a text fragment instances.
  118. /// </summary>
  119. /// <returns>The newly created text fragment instance.</returns>
  120. public MixedCodeDocumentTextFragment CreateTextFragment()
  121. {
  122. return (MixedCodeDocumentTextFragment) CreateFragment(MixedCodeDocumentFragmentType.Text);
  123. }
  124. /// <summary>
  125. /// Loads a mixed code document from a stream.
  126. /// </summary>
  127. /// <param name="stream">The input stream.</param>
  128. public void Load(Stream stream)
  129. {
  130. Load(new StreamReader(stream));
  131. }
  132. /// <summary>
  133. /// Loads a mixed code document from a stream.
  134. /// </summary>
  135. /// <param name="stream">The input stream.</param>
  136. /// <param name="detectEncodingFromByteOrderMarks">Indicates whether to look for byte order marks at the beginning of the file.</param>
  137. public void Load(Stream stream, bool detectEncodingFromByteOrderMarks)
  138. {
  139. Load(new StreamReader(stream, detectEncodingFromByteOrderMarks));
  140. }
  141. /// <summary>
  142. /// Loads a mixed code document from a stream.
  143. /// </summary>
  144. /// <param name="stream">The input stream.</param>
  145. /// <param name="encoding">The character encoding to use.</param>
  146. public void Load(Stream stream, Encoding encoding)
  147. {
  148. Load(new StreamReader(stream, encoding));
  149. }
  150. /// <summary>
  151. /// Loads a mixed code document from a stream.
  152. /// </summary>
  153. /// <param name="stream">The input stream.</param>
  154. /// <param name="encoding">The character encoding to use.</param>
  155. /// <param name="detectEncodingFromByteOrderMarks">Indicates whether to look for byte order marks at the beginning of the file.</param>
  156. public void Load(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks)
  157. {
  158. Load(new StreamReader(stream, encoding, detectEncodingFromByteOrderMarks));
  159. }
  160. /// <summary>
  161. /// Loads a mixed code document from a stream.
  162. /// </summary>
  163. /// <param name="stream">The input stream.</param>
  164. /// <param name="encoding">The character encoding to use.</param>
  165. /// <param name="detectEncodingFromByteOrderMarks">Indicates whether to look for byte order marks at the beginning of the file.</param>
  166. /// <param name="buffersize">The minimum buffer size.</param>
  167. public void Load(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int buffersize)
  168. {
  169. Load(new StreamReader(stream, encoding, detectEncodingFromByteOrderMarks, buffersize));
  170. }
  171. /// <summary>
  172. /// Loads a mixed code document from a file.
  173. /// </summary>
  174. /// <param name="path">The complete file path to be read.</param>
  175. public void Load(string path)
  176. {
  177. Load(new StreamReader(path));
  178. }
  179. /// <summary>
  180. /// Loads a mixed code document from a file.
  181. /// </summary>
  182. /// <param name="path">The complete file path to be read.</param>
  183. /// <param name="detectEncodingFromByteOrderMarks">Indicates whether to look for byte order marks at the beginning of the file.</param>
  184. public void Load(string path, bool detectEncodingFromByteOrderMarks)
  185. {
  186. Load(new StreamReader(path, detectEncodingFromByteOrderMarks));
  187. }
  188. /// <summary>
  189. /// Loads a mixed code document from a file.
  190. /// </summary>
  191. /// <param name="path">The complete file path to be read.</param>
  192. /// <param name="encoding">The character encoding to use.</param>
  193. public void Load(string path, Encoding encoding)
  194. {
  195. Load(new StreamReader(path, encoding));
  196. }
  197. /// <summary>
  198. /// Loads a mixed code document from a file.
  199. /// </summary>
  200. /// <param name="path">The complete file path to be read.</param>
  201. /// <param name="encoding">The character encoding to use.</param>
  202. /// <param name="detectEncodingFromByteOrderMarks">Indicates whether to look for byte order marks at the beginning of the file.</param>
  203. public void Load(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks)
  204. {
  205. Load(new StreamReader(path, encoding, detectEncodingFromByteOrderMarks));
  206. }
  207. /// <summary>
  208. /// Loads a mixed code document from a file.
  209. /// </summary>
  210. /// <param name="path">The complete file path to be read.</param>
  211. /// <param name="encoding">The character encoding to use.</param>
  212. /// <param name="detectEncodingFromByteOrderMarks">Indicates whether to look for byte order marks at the beginning of the file.</param>
  213. /// <param name="buffersize">The minimum buffer size.</param>
  214. public void Load(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks, int buffersize)
  215. {
  216. Load(new StreamReader(path, encoding, detectEncodingFromByteOrderMarks, buffersize));
  217. }
  218. /// <summary>
  219. /// Loads the mixed code document from the specified TextReader.
  220. /// </summary>
  221. /// <param name="reader">The TextReader used to feed the HTML data into the document.</param>
  222. public void Load(TextReader reader)
  223. {
  224. _codefragments.Clear();
  225. _textfragments.Clear();
  226. // all pseudo constructors get down to this one
  227. StreamReader sr = reader as StreamReader;
  228. if (sr != null)
  229. {
  230. _streamencoding = sr.CurrentEncoding;
  231. }
  232. _text = reader.ReadToEnd();
  233. reader.Close();
  234. Parse();
  235. }
  236. /// <summary>
  237. /// Loads a mixed document from a text
  238. /// </summary>
  239. /// <param name="html">The text to load.</param>
  240. public void LoadHtml(string html)
  241. {
  242. Load(new StringReader(html));
  243. }
  244. /// <summary>
  245. /// Saves the mixed document to the specified stream.
  246. /// </summary>
  247. /// <param name="outStream">The stream to which you want to save.</param>
  248. public void Save(Stream outStream)
  249. {
  250. StreamWriter sw = new StreamWriter(outStream, GetOutEncoding());
  251. Save(sw);
  252. }
  253. /// <summary>
  254. /// Saves the mixed document to the specified stream.
  255. /// </summary>
  256. /// <param name="outStream">The stream to which you want to save.</param>
  257. /// <param name="encoding">The character encoding to use.</param>
  258. public void Save(Stream outStream, Encoding encoding)
  259. {
  260. StreamWriter sw = new StreamWriter(outStream, encoding);
  261. Save(sw);
  262. }
  263. /// <summary>
  264. /// Saves the mixed document to the specified file.
  265. /// </summary>
  266. /// <param name="filename">The location of the file where you want to save the document.</param>
  267. public void Save(string filename)
  268. {
  269. StreamWriter sw = new StreamWriter(filename, false, GetOutEncoding());
  270. Save(sw);
  271. }
  272. /// <summary>
  273. /// Saves the mixed document to the specified file.
  274. /// </summary>
  275. /// <param name="filename">The location of the file where you want to save the document.</param>
  276. /// <param name="encoding">The character encoding to use.</param>
  277. public void Save(string filename, Encoding encoding)
  278. {
  279. StreamWriter sw = new StreamWriter(filename, false, encoding);
  280. Save(sw);
  281. }
  282. /// <summary>
  283. /// Saves the mixed document to the specified StreamWriter.
  284. /// </summary>
  285. /// <param name="writer">The StreamWriter to which you want to save.</param>
  286. public void Save(StreamWriter writer)
  287. {
  288. Save((TextWriter) writer);
  289. }
  290. /// <summary>
  291. /// Saves the mixed document to the specified TextWriter.
  292. /// </summary>
  293. /// <param name="writer">The TextWriter to which you want to save.</param>
  294. public void Save(TextWriter writer)
  295. {
  296. writer.Flush();
  297. }
  298. #endregion
  299. #region Internal Methods
  300. internal MixedCodeDocumentFragment CreateFragment(MixedCodeDocumentFragmentType type)
  301. {
  302. switch (type)
  303. {
  304. case MixedCodeDocumentFragmentType.Text:
  305. return new MixedCodeDocumentTextFragment(this);
  306. case MixedCodeDocumentFragmentType.Code:
  307. return new MixedCodeDocumentCodeFragment(this);
  308. default:
  309. throw new NotSupportedException();
  310. }
  311. }
  312. internal Encoding GetOutEncoding()
  313. {
  314. if (_streamencoding != null)
  315. return _streamencoding;
  316. return Encoding.UTF8;
  317. }
  318. #endregion
  319. #region Private Methods
  320. private void IncrementPosition()
  321. {
  322. _index++;
  323. if (_c == 10)
  324. {
  325. _lineposition = 1;
  326. _line++;
  327. }
  328. else
  329. _lineposition++;
  330. }
  331. private void Parse()
  332. {
  333. _state = ParseState.Text;
  334. _index = 0;
  335. _currentfragment = CreateFragment(MixedCodeDocumentFragmentType.Text);
  336. while (_index < _text.Length)
  337. {
  338. _c = _text[_index];
  339. IncrementPosition();
  340. switch (_state)
  341. {
  342. case ParseState.Text:
  343. if (_index + TokenCodeStart.Length < _text.Length)
  344. {
  345. if (_text.Substring(_index - 1, TokenCodeStart.Length) == TokenCodeStart)
  346. {
  347. _state = ParseState.Code;
  348. _currentfragment.Length = _index - 1 - _currentfragment.Index;
  349. _currentfragment = CreateFragment(MixedCodeDocumentFragmentType.Code);
  350. SetPosition();
  351. continue;
  352. }
  353. }
  354. break;
  355. case ParseState.Code:
  356. if (_index + TokenCodeEnd.Length < _text.Length)
  357. {
  358. if (_text.Substring(_index - 1, TokenCodeEnd.Length) == TokenCodeEnd)
  359. {
  360. _state = ParseState.Text;
  361. _currentfragment.Length = _index + TokenCodeEnd.Length - _currentfragment.Index;
  362. _index += TokenCodeEnd.Length;
  363. _lineposition += TokenCodeEnd.Length;
  364. _currentfragment = CreateFragment(MixedCodeDocumentFragmentType.Text);
  365. SetPosition();
  366. continue;
  367. }
  368. }
  369. break;
  370. }
  371. }
  372. _currentfragment.Length = _index - _currentfragment.Index;
  373. }
  374. private void SetPosition()
  375. {
  376. _currentfragment.Line = _line;
  377. _currentfragment._lineposition = _lineposition;
  378. _currentfragment.Index = _index - 1;
  379. _currentfragment.Length = 0;
  380. }
  381. #endregion
  382. #region Nested type: ParseState
  383. private enum ParseState
  384. {
  385. Text,
  386. Code
  387. }
  388. #endregion
  389. }
  390. }