PageRenderTime 40ms CodeModel.GetById 1ms app.highlight 30ms RepoModel.GetById 1ms app.codeStats 0ms

/src/NUnit/UiException/CSharpParser/CSCode.cs

#
C# | 354 lines | 199 code | 57 blank | 98 comment | 30 complexity | dbeec67ca3bcbc2f605d6ebe645f7531 MD5 | raw file
  1// ****************************************************************
  2// This is free software licensed under the NUnit license. You may
  3// obtain a copy of the license at http://nunit.org
  4// ****************************************************************
  5
  6using System;
  7using System.Collections.Generic;
  8using System.Text;
  9using System.Collections;
 10using NUnit.UiException.CodeFormatters;
 11
 12namespace NUnit.UiException.CodeFormatters
 13{
 14    /// <summary>
 15    /// (Formerly named CSCode)
 16    /// 
 17    /// Implements ITextManager and adds new behaviors to provide support for basic
 18    /// syntax coloring. 
 19    /// </summary>
 20    public class FormattedCode :
 21        ITextManager
 22    {
 23        /// <summary>
 24        /// Keeps tracks of the text and the data used by the syntax coloring feature.
 25        /// </summary>
 26        protected CodeInfo _codeInfo;
 27
 28        /// <summary>
 29        /// Stores the character count of the longest line in this text.
 30        /// </summary>
 31        private int _maxLength;
 32
 33        /// <summary>
 34        /// Builds a new instance of FormattedCode.
 35        /// </summary>
 36        public FormattedCode()
 37        {
 38            _codeInfo = NewCodeInfo();
 39            _maxLength = 0;
 40
 41            return;
 42        }
 43
 44        public FormattedCode(string csharpText, int[] strIndexes, byte[] tagValues, int[] lineIndexes)
 45        {
 46            UiExceptionHelper.CheckNotNull(csharpText, "csharpText");
 47            UiExceptionHelper.CheckNotNull(strIndexes, "strIndexes");
 48            UiExceptionHelper.CheckNotNull(tagValues, "tagValues");
 49            UiExceptionHelper.CheckNotNull(lineIndexes, "lineIndexes");
 50
 51            _codeInfo = new CodeInfo();
 52
 53            _codeInfo.Text = csharpText;
 54
 55            _codeInfo.IndexArray = new List<int>();
 56            foreach (int index in strIndexes)
 57                _codeInfo.IndexArray.Add(index);
 58
 59            _codeInfo.TagArray = new List<byte>();
 60            foreach (byte tag in tagValues)
 61                _codeInfo.TagArray.Add(tag);
 62
 63            _codeInfo.LineArray = new List<int>();
 64            foreach (int line in lineIndexes)
 65                _codeInfo.LineArray.Add(line);
 66
 67            return;
 68        }
 69
 70        public static FormattedCode Empty
 71        {
 72            get { return (new FormattedCode()); }
 73        }
 74
 75        public CodeInfo CopyInfo()
 76        {
 77            FormattedCode copy;
 78
 79            copy = new FormattedCode(_codeInfo.Text, 
 80                _codeInfo.IndexArray.ToArray(),
 81                _codeInfo.TagArray.ToArray(), 
 82                _codeInfo.LineArray.ToArray());
 83
 84            return (copy._codeInfo);
 85        }
 86
 87        /// <summary>
 88        /// Builds a new instance of CodeInfo.
 89        /// </summary>
 90        public static CodeInfo NewCodeInfo()
 91        {
 92            CodeInfo res;
 93
 94            res = new CodeInfo();
 95            res.Text = "";
 96            res.IndexArray = new List<int>();
 97            res.LineArray = new List<int>();
 98            res.TagArray = new List<byte>();
 99
100            return (res);
101        }
102
103        /// <summary>
104        /// Gets the text currently managed by this object.
105        /// </summary>
106        public string Text
107        {
108            get { return (_codeInfo.Text); }           
109        }
110
111        /// <summary>
112        /// Gets the line count in the text currently
113        /// managed by this object.
114        /// </summary>
115        public int LineCount
116        {
117            get { return (_codeInfo.LineArray.Count); }
118        }
119
120        /// <summary>
121        /// Gets the character count of the longest line
122        /// in this text.
123        /// </summary>
124        public int MaxLength
125        {
126            get 
127            {
128                int i;
129
130                if (_maxLength == 0)
131                    for (i = 0; i < LineCount; ++i)
132                        _maxLength = Math.Max(_maxLength, this[i].Text.TrimEnd().Length);
133
134                return (_maxLength); 
135            }
136        }
137
138        /// <summary>
139        /// Gives access to the collection of ClassifiedToken at the specified lineIndex.
140        /// </summary>
141        /// <param name="lineIndex">A zero based startingPosition.</param>
142        /// <returns>The ClassifiedTokenCollection instance at this startingPosition.</returns>
143        public ClassifiedTokenCollection this[int lineIndex]
144        {
145            get { return (new InternalCSTokenCollection(_codeInfo, lineIndex)); }
146        }
147
148        /// <summary>
149        /// Gets the text at the specified line.
150        /// </summary>
151        /// <param name="lineIndex">A zero based startingPosition.</param>
152        /// <returns>The text at the specified line startingPosition.</returns>
153        public string GetTextAt(int lineIndex)
154        {
155            return (this[lineIndex].Text);
156        }
157
158        /// <summary>
159        /// An utility method that check data consistency. The operation
160        /// raises an exception if an error is found.
161        /// </summary>
162        public static void CheckData(FormattedCode data)
163        {
164            List<int> lines;
165            int line;
166            int bound;
167            int i;
168
169            UiExceptionHelper.CheckNotNull(data, "data");
170
171            UiExceptionHelper.CheckTrue(
172                data._codeInfo.IndexArray.Count == data._codeInfo.TagArray.Count,
173                "IndexArray.Count and TagArray.Count must match.",
174                "data");
175
176            bound = data._codeInfo.IndexArray.Count;
177            lines = data._codeInfo.LineArray;
178            for (i = 0; i < lines.Count; ++i)
179            {
180                line = lines[i];
181
182                UiExceptionHelper.CheckTrue(
183                    line >= 0 && line < bound,
184                    "Bad LineArray value at index " + i + ", value was: " + line + ", expected to be in: [0-" + bound + "[.",
185                    "data");
186
187                if (i == 0)
188                    continue;
189
190                UiExceptionHelper.CheckTrue(
191                    lines[i] > lines[i - 1],
192                    "Bad LineArray[" + i + "], value was: " + line + ", expected to be > than LineArray[" + (i - 1) + "]=" + lines[i - 1] + ".",
193                    "data");
194            }
195
196            return;
197        }
198
199        public override bool Equals(object obj)
200        {
201            FormattedCode arg;
202            int i;
203
204            if (obj == null ||
205                !(obj is FormattedCode))
206                return (false);
207
208            arg = obj as FormattedCode;
209
210            if (arg._codeInfo.Text != _codeInfo.Text ||
211                arg._codeInfo.IndexArray.Count != _codeInfo.IndexArray.Count ||
212                arg._codeInfo.TagArray.Count != _codeInfo.TagArray.Count ||
213                arg._codeInfo.LineArray.Count != _codeInfo.LineArray.Count)
214                return (false);
215
216            for (i = 0; i < arg._codeInfo.IndexArray.Count; ++i)
217                if (arg._codeInfo.IndexArray[i] != _codeInfo.IndexArray[i])
218                    return (false);
219
220            for (i = 0; i < arg._codeInfo.TagArray.Count; ++i)
221                if (arg._codeInfo.TagArray[i] != _codeInfo.TagArray[i])
222                    return (false);
223
224            for (i = 0; i < arg._codeInfo.LineArray.Count; ++i)
225                if (arg._codeInfo.LineArray[i] != _codeInfo.LineArray[i])
226                    return (false);
227
228            return (true);
229        }
230
231        public override int GetHashCode()
232        {
233            return base.GetHashCode();
234        }
235
236        public override string ToString()
237        {
238            string res;
239            string index_array;
240            string tag_array;
241            string line_array;
242            int i;
243
244            index_array = "";
245            for (i = 0; i < _codeInfo.IndexArray.Count; ++i)
246            {
247                if (i > 0)
248                    index_array += ", ";
249                index_array += "" + _codeInfo.IndexArray[i];
250            }
251
252            tag_array = "";
253            for (i = 0; i < _codeInfo.TagArray.Count; ++i)
254            {
255                if (i > 0)
256                    tag_array += ", ";
257                tag_array += "" + _codeInfo.TagArray[i];
258            }
259
260            line_array = "";
261            for (i = 0; i < _codeInfo.LineArray.Count; ++i)
262            {
263                if (i > 0)
264                    line_array += ", ";
265                line_array += "" + _codeInfo.LineArray[i];
266            }
267
268            res = String.Format(
269                "FormattedCode: [(text=[{0}], len={1}), (startingPosition=[{2}]), (tag=[{3}]), (line=[{4}])]",
270                _codeInfo.Text, _codeInfo.Text.Length,
271                index_array, tag_array, line_array);
272
273            return (res);
274        }
275
276        /// <summary>
277        /// A naive attempt to modelize a structure of data that manages the text to be
278        /// displayed and extra data to make basic syntax coloring at rendering time,
279        /// while keeping a low memory footprint.
280        /// 
281        /// At rendering time, CodeInfo is used to make a link between the text to be drawn
282        /// and the brush color to be used during the process. So it is possible to distinguishes
283        /// code, comments, keywords and strings.
284        ///   Currently, CodeInfo is used to split the text into a collection of couple of data,
285        /// where each couple is composed of:
286        ///     - a string of characters
287        ///     - a value (called tag) that classifies this string from 0 to 3.
288        ///       Where 0 corresponds to 'Code', 1 to 'Keyword' and so on.
289        ///  These couples are named 'ClassifiedToken'. At rendering time, the process can link each
290        ///  of these values to a particular System.Drawing.Brush instance and display text
291        ///  differently.
292        ///  
293        ///  However, keeping track of all these couples at any time could have a significative
294        ///  impact on memory especially for big files. Therefore, instead of storing all theses couples,
295        ///  CodeInfo stores just primitive information that allow to build ClassifiedToken instances on the fly.        
296        /// </summary>
297        public class CodeInfo
298        {
299            /// <summary>
300            /// Holds the entire text as a simple string.
301            /// </summary>
302            public string Text;
303
304            /// <summary>
305            /// Array of character indexes that refers to
306            /// the field "Text". Each value in this array
307            /// must be interpreted as the starting index position
308            /// in the string into Text.
309            /// </summary>
310            public List<int> IndexArray;
311
312            /// <summary>
313            /// Array of ClassificationTag values held as a
314            /// byte array. There is a one-to-one correspondance
315            /// with 'IndexArray'. i.e.: TagArray[0] refers to the
316            /// ClassificationTag value for string sequence at
317            /// IndexArray[0]. TagArray[1] refers value to IndexArray[1] and
318            /// so on... Hence, the following condition:
319            ///   - IndexArray.Count == TagArray.Count must be verified.
320            /// </summary>
321            public List<byte> TagArray;
322
323            /// <summary>
324            /// This index_array is used to easily locate the start of each
325            /// line of text, for instance: startingPosition[0] refers to line startingPosition 0,
326            /// startingPosition[1] refers to line startingPosition 1 and so on...
327            ///    However, there is a small indirection in that this index_array
328            /// doesn't directly hold string indexes but refers to the startingPosition
329            /// of the item to be used in 'IndexArray'.
330            ///    Therefore, LineArray[0] gives access to the startingPosition of the
331            /// IndexArray's item to be used to get the corresponding character
332            /// position. Hence, line 0 starts at: IndexArray[LineArray[0]]
333            /// line 1: IndexArray[LineArray[1]] and so on...
334            /// </summary>
335            public List<int> LineArray;
336        }
337
338        #region InternalCSTokenCollection
339
340        class InternalCSTokenCollection :
341            ClassifiedTokenCollection
342        {
343            public InternalCSTokenCollection(CodeInfo info, int lineIndex)
344            {
345                _info = info;
346                _lineIndex = lineIndex;
347
348                return;
349            }
350        }
351
352        #endregion       
353    }
354}