You've already forked linux-packaging-mono
							
							
		
			
	
	
		
			215 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			215 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|   | // <copyright> | |||
|  | //   Copyright (c) Microsoft Corporation.  All rights reserved. | |||
|  | // </copyright> | |||
|  | 
 | |||
|  | namespace System.Activities.Debugger | |||
|  | { | |||
|  |     using System.Collections.Generic; | |||
|  |     using System.IO; | |||
|  | 
 | |||
|  |     //  | |||
|  |     internal partial class CharacterSpottingTextReader : TextReader | |||
|  |     { | |||
|  |         // These 'special characters' couple with the fact that we are working on XML. | |||
|  |         private const char StartAngleBracket = '<'; | |||
|  |         private const char EndAngleBracket = '>'; | |||
|  |         private const char SingleQuote = '\''; | |||
|  |         private const char DoubleQuote = '"'; | |||
|  |         private const char EndLine = '\n'; | |||
|  |         private const char CarriageReturn = '\r'; | |||
|  | 
 | |||
|  |         private TextReader underlyingReader; | |||
|  |         private int currentLine; | |||
|  |         private int currentPosition; | |||
|  |         private List<DocumentLocation> startAngleBrackets; | |||
|  |         private List<DocumentLocation> endAngleBrackets; | |||
|  |         private List<DocumentLocation> singleQuotes; | |||
|  |         private List<DocumentLocation> doubleQuotes; | |||
|  |         private List<DocumentLocation> endLines; | |||
|  | 
 | |||
|  |         public CharacterSpottingTextReader(TextReader underlyingReader) | |||
|  |         { | |||
|  |             UnitTestUtility.Assert(underlyingReader != null, "underlyingReader should not be null and should be ensured by caller."); | |||
|  |             this.underlyingReader = underlyingReader; | |||
|  |             this.currentLine = 1; | |||
|  |             this.currentPosition = 1; | |||
|  |             this.startAngleBrackets = new List<DocumentLocation>(); | |||
|  |             this.endAngleBrackets = new List<DocumentLocation>(); | |||
|  |             this.singleQuotes = new List<DocumentLocation>(); | |||
|  |             this.doubleQuotes = new List<DocumentLocation>(); | |||
|  |             this.endLines = new List<DocumentLocation>(); | |||
|  |         } | |||
|  | 
 | |||
|  |         // CurrentLocation consists of the current line number and the current position on the line. | |||
|  |         //  | |||
|  |         // The current position is like a cursor moving along the line. For example, a string "abc" ending with "\r\n": | |||
|  |         //  | |||
|  |         //    abc\r\n | |||
|  |         //  | |||
|  |         // the current position, depicted as | below, moves from char to char: | |||
|  |         //  | |||
|  |         //    |a|b|c|\r|\n | |||
|  |         //  | |||
|  |         // When we are at the beginning of the line, the current position is 1. After we read the first char,  | |||
|  |         // we advance the current position to 2, and so on: | |||
|  |         //  | |||
|  |         //    1 2 3 4  | |||
|  |         //    |a|b|c|\r|\n | |||
|  |         //  | |||
|  |         // As we reach the end-of-line character on the line, which can be \r, \r\n or \n, we move to the next line and reset the current position to 1. | |||
|  |         private DocumentLocation CurrentLocation | |||
|  |         { | |||
|  |             get | |||
|  |             { | |||
|  |                 return new DocumentLocation(this.currentLine, this.currentPosition); | |||
|  |             } | |||
|  |         } | |||
|  | 
 | |||
|  |         public override void Close() | |||
|  |         { | |||
|  |             this.underlyingReader.Close(); | |||
|  |         } | |||
|  | 
 | |||
|  |         public override int Peek() | |||
|  |         { | |||
|  |             // This character is not consider read, therefore we don't need to analyze this. | |||
|  |             return this.underlyingReader.Peek(); | |||
|  |         } | |||
|  | 
 | |||
|  |         public override int Read() | |||
|  |         { | |||
|  |             int result = this.underlyingReader.Read(); | |||
|  |             if (result != -1) | |||
|  |             { | |||
|  |                 result = this.AnalyzeReadData((char)result); | |||
|  |             } | |||
|  | 
 | |||
|  |             return result; | |||
|  |         } | |||
|  | 
 | |||
|  |         internal DocumentLocation FindCharacterStrictlyAfter(char c, DocumentLocation afterLocation) | |||
|  |         { | |||
|  |             List<DocumentLocation> locationList = this.GetLocationList(c); | |||
|  |             UnitTestUtility.Assert(locationList != null, "We should always find character for special characters only"); | |||
|  | 
 | |||
|  |             // Note that this 'nextLocation' may not represent a real document location (we could hit an end line character here so that there is no next line | |||
|  |             // position. This is merely used for the search algorithm below: | |||
|  |             DocumentLocation nextLocation = new DocumentLocation(afterLocation.LineNumber, new OneBasedCounter(afterLocation.LinePosition.Value + 1)); | |||
|  |             BinarySearchResult result = locationList.MyBinarySearch(nextLocation); | |||
|  |             if (result.IsFound) | |||
|  |             { | |||
|  |                 // It is possible that the next location is a quote itself, or | |||
|  |                 return nextLocation; | |||
|  |             } | |||
|  |             else if (result.IsNextIndexAvailable) | |||
|  |             { | |||
|  |                 // Some other later position is the quote, or | |||
|  |                 return locationList[result.NextIndex]; | |||
|  |             } | |||
|  |             else | |||
|  |             { | |||
|  |                 // in the worst case no quote can be found. | |||
|  |                 return null; | |||
|  |             } | |||
|  |         } | |||
|  | 
 | |||
|  |         internal DocumentLocation FindCharacterStrictlyBefore(char c, DocumentLocation documentLocation) | |||
|  |         { | |||
|  |             List<DocumentLocation> locationList = this.GetLocationList(c); | |||
|  |             UnitTestUtility.Assert(locationList != null, "We should always find character for special characters only"); | |||
|  | 
 | |||
|  |             BinarySearchResult result = locationList.MyBinarySearch(documentLocation); | |||
|  |             if (result.IsFound) | |||
|  |             { | |||
|  |                 if (result.FoundIndex > 0) | |||
|  |                 { | |||
|  |                     return locationList[result.FoundIndex - 1]; | |||
|  |                 } | |||
|  |                 else | |||
|  |                 { | |||
|  |                     return null; | |||
|  |                 } | |||
|  |             } | |||
|  |             else if (result.IsNextIndexAvailable) | |||
|  |             { | |||
|  |                 if (result.NextIndex > 0) | |||
|  |                 { | |||
|  |                     return locationList[result.NextIndex - 1]; | |||
|  |                 } | |||
|  |                 else | |||
|  |                 { | |||
|  |                     return null; | |||
|  |                 } | |||
|  |             } | |||
|  |             else if (locationList.Count > 0) | |||
|  |             { | |||
|  |                 return locationList[locationList.Count - 1]; | |||
|  |             } | |||
|  |             else | |||
|  |             { | |||
|  |                 return null; | |||
|  |             } | |||
|  |         } | |||
|  | 
 | |||
|  |         private List<DocumentLocation> GetLocationList(char c) | |||
|  |         { | |||
|  |             switch (c) | |||
|  |             { | |||
|  |                 case StartAngleBracket: | |||
|  |                     return this.startAngleBrackets; | |||
|  |                 case EndAngleBracket: | |||
|  |                     return this.endAngleBrackets; | |||
|  |                 case SingleQuote: | |||
|  |                     return this.singleQuotes; | |||
|  |                 case DoubleQuote: | |||
|  |                     return this.doubleQuotes; | |||
|  |                 case EndLine: | |||
|  |                     return this.endLines; | |||
|  |                 default: | |||
|  |                     return null; | |||
|  |             } | |||
|  |         } | |||
|  | 
 | |||
|  |         /// <summary> | |||
|  |         /// Process last character read, and canonicalize end line. | |||
|  |         /// </summary> | |||
|  |         /// <param name="lastCharacterRead">The last character read by the underlying reader</param> | |||
|  |         /// <returns>The last character processed</returns> | |||
|  |         private char AnalyzeReadData(char lastCharacterRead) | |||
|  |         { | |||
|  |             // XML specification requires end-of-line == '\n' or '\r' or "\r\n" | |||
|  |             // See http://www.w3.org/TR/2008/REC-xml-20081126/#sec-line-ends for details. | |||
|  |             if (lastCharacterRead == CarriageReturn) | |||
|  |             { | |||
|  |                 // if reading \r and peek next char is \n, then process \n as well | |||
|  |                 int nextChar = this.underlyingReader.Peek(); | |||
|  |                 if (nextChar == EndLine) | |||
|  |                 { | |||
|  |                     lastCharacterRead = (char)this.underlyingReader.Read(); | |||
|  |                 } | |||
|  |             } | |||
|  | 
 | |||
|  |             if (lastCharacterRead == EndLine || lastCharacterRead == CarriageReturn) | |||
|  |             { | |||
|  |                 this.endLines.Add(this.CurrentLocation); | |||
|  |                 this.currentLine++; | |||
|  |                 this.currentPosition = 1; | |||
|  | 
 | |||
|  |                 // according to XML spec, both \r\n and \r should be translated to \n | |||
|  |                 return EndLine; | |||
|  |             } | |||
|  |             else | |||
|  |             { | |||
|  |                 List<DocumentLocation> locations = this.GetLocationList(lastCharacterRead); | |||
|  |                 if (locations != null) | |||
|  |                 { | |||
|  |                     locations.Add(this.CurrentLocation); | |||
|  |                 } | |||
|  | 
 | |||
|  |                 this.currentPosition++; | |||
|  |                 return lastCharacterRead; | |||
|  |             } | |||
|  |         } | |||
|  |     } | |||
|  | } |