// Copyright (c) 2007 Omer Rauchwerger (a.k.a rauchy) (omer@rauchy.net) // All rights reserved. // // This file is part of Regionerate. // // This program is free software; you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 3 of the License, // or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see . using System; using System.Collections.Generic; using System.Text; namespace Rauchy.Regionerate.Shared.Components { /// /// Provides some basic utilities for working with text. /// public static class TextUtilities { #region Methods (7)  // Public Methods (7)  /// /// Decodes the output of . /// /// /// When a curly brace is found inside a string or char literal, it is always transformed into the /// following format: "{?}". The value ? inside the brace pair will be an angle bracket pointing /// to the original bracket which was there before encoding. Check out the examples. This prevents a /// situation where an uneven pair of brackets inside a literal tips the bracket balance of the containing member. /// This decode method reverts "{?}" back to the original bracket value. /// /// /// 1. Decoding /// private string _foo = "{<}"; /// will return /// private string _foo = "{"; /// 2. Decoding /// private string _bar = '}'; /// will return /// private string _bar = '}'; /// 3. Decoding /// private string _koo = "{>}{<}"; /// will return /// private string _koo = "}{"; /// public static string DecodeBrackets( this string code ) { int i = 0; bool insideString = false; while ( i < code.Length ) { if ( code[ i ] == '"' ) { insideString = !insideString; } else if ( insideString ) { if ( code[ i ] == '\n' ) { // String literals cannot span multiple lines. // This is probably a comment with an opening quotation mark that someone forgot to close, like // /// I"m an idiot insideString = false; } if ( ( i + 3 ) <= code.Length ) // Make sure we can count aheaed for {?} { string nextThreeCharacters = code.Substring( i, 3 ); if ( nextThreeCharacters == "{<}" ) { code = code.Remove( i, 3 ); code = code.Insert( i, "{" ); } else if ( nextThreeCharacters == "{>}" ) { code = code.Remove( i, 3 ); code = code.Insert( i, "}" ); } } } else if ( code[ i ] == '\'' ) { if ( ( i + 5 ) <= code.Length ) // Make sure we can count aheaed for '{' or '}' { string nextFiveCharacters = code.Substring( i, 5 ); if ( nextFiveCharacters == "'{<}'" ) { code = code.Remove( i, 5 ); code = code.Insert( i, "'{'" ); i += 2; // Advance the cursor by 2 instead of 3 because it will be advanced by another one at the end of the loop. } else if ( nextFiveCharacters == "'{>}'" ) { code = code.Remove( i, 5 ); code = code.Insert( i, "'}'" ); i += 2; // Advance the cursor by 2 instead of 3 because it will be advanced by another one at the end of the loop. } } } i++; } return code; } /// /// Encodes all curly brackets inside strings char literals into a temporary /// string that does not corrupt C#'s natural curly brace balance. Use to /// get the original value back from this method's output. /// /// /// When a curly brace is found inside a string or char literal, it is always transformed into the /// following format: "{?}". The value ? inside the brace pair will be an angle bracket pointing /// to the original bracket which was there before encoding. Check out the examples. This prevents a /// situation where an uneven pair of brackets inside a literal tips the bracket balance of the containing member. /// /// /// 1. Encoding /// private string _foo = "{"; /// will return /// private string _foo = "{<}"; /// 2. Encoding /// private string _bar = '}'; /// will return /// private string _bar = '}'; /// 3. Encoding /// private string _koo = "}{"; /// will return /// private string _koo = "{>}{<}"; /// public static string EncodeBrackets( this string code ) { int i = 0; bool insideString = false; while ( i < code.Length ) { if ( code[ i ] == '"' ) { insideString = !insideString; i++; } else if ( insideString ) { if ( code[ i ] == '{' ) { code = code.Remove( i, 1 ); code = code.Insert( i, "{<}" ); i += 3; } else if ( code[ i ] == '}' ) { code = code.Remove( i, 1 ); code = code.Insert( i, "{>}" ); i += 3; } else if ( code[ i ] == '\n' ) { // String literals cannot span multiple lines. // This is probably a comment with an opening quotation mark that someone forgot to close, like // /// I"m an idiot insideString = false; i++; } else { i++; } } else if ( code[ i ] == '\'' ) { // Often people use the apostrophe (') sign as a punctuation mark inside text and // not as a holder for char literals. // For example: // // int interval = 5; // The form's refresh rate. // // In such case, the apostrophe does not declare a char literal and will never be closed. // The only situation that is of any interest to us is when we encounter '{' or '}', // any other situation can be ignored. if ( ( i + 3 ) <= code.Length ) // Make sure we can count aheaed for '{' or '}' { string nextThreeCharacters = code.Substring( i, 3 ); if ( nextThreeCharacters == "'{'" ) { code = code.Remove( i, 3 ); code = code.Insert( i, "'{<}'" ); i += 5; } else if ( nextThreeCharacters == "'}'" ) { code = code.Remove( i, 3 ); code = code.Insert( i, "'{>}'" ); i += 5; } else { // There are at least 3 characters ahead, but they are not '{' or '}', // It can be any other character literal or punctuation mark that does not need to be encoded. i++; } } else { i++; } } else { i++; } } return code; } // [rgn] Public Methods (3) /// /// Breaks down the code into lines. /// /// /// I chose not to implement this with string.Split as it caused problems /// with line breaks and returned results that did not match the original text. /// /// The code. public static IList GetLines( this string code ) { IList lines = new List(); StringBuilder line = new StringBuilder(); foreach ( char c in code ) { line.Append( c ); if ( line.ToString().EndsWith( Environment.NewLine ) ) { lines.Add( line.ToString() ); line = new StringBuilder(); } } if ( line.Length > 0 ) { // Add the last remaining line. lines.Add( line.ToString() ); } return lines; } /// /// Creates a that is constructed from a specified amount of tabs. /// public static string GetTabString(CodeLayout.RenderingOptions renderingOptions) { StringBuilder stringBuilder = new StringBuilder(); if (renderingOptions.UseTabs) { stringBuilder.Append('\t', renderingOptions.TabAmount); } else { stringBuilder.Append(' ', renderingOptions.SpaceAmount); } return stringBuilder.ToString(); } /// /// Indents the specified text according to a specific amount of tabs. /// /// /// Overrides any tab characters already present in the beginning of the text. /// /// /// Calling Indent("hello", 2) will return " hello". /// Calling Indent(" hello", 2 will also return " hello". /// public static string Indent( this string text, CodeLayout.RenderingOptions renderingOptions ) { string flatText = text.TrimStart( '\t', ' ' ); string tabString = GetTabString(renderingOptions); return tabString + flatText; } /// /// Removes all occurances of newline characters from the end of a string. /// public static string TrimNewLinesFromEnd( this string text ) { StringBuilder trimmedText = new StringBuilder(); int lastWhitespace; // Contains the location of the last non-whitespace character in 'text'. for ( lastWhitespace = text.Length - 1; lastWhitespace >= 0; lastWhitespace-- ) { char c = text[ lastWhitespace ]; if ( c == '\t' || c == ' ' ) { trimmedText.Append( c ); } else if ( c != '\r' && c != '\n' ) { break; } } // Copy the remaining substring from 'text'. Until 'lastNonWhitespaceLocation'. StringBuilder result = new StringBuilder(); result.Append( text.Substring( 0, lastWhitespace + 1 ) ); result.AppendLine(); result.Append( trimmedText ); return result.ToString(); } /// /// Removes all occurances of newline characters from the beginning of a string. /// public static string TrimNewLinesFromStart( this string text ) { StringBuilder trimmedText = new StringBuilder(); int firstNonWhitespaceLocation = 0; // Contains the location of the first non-whitespace character in 'text'. foreach ( char c in text ) { if ( c == '\t' || c == ' ' ) { trimmedText.Append( c ); } else if ( c != '\r' && c != '\n' ) { break; } firstNonWhitespaceLocation++; } // Copy the remaining substring from 'text'. Start from the 'firstNonWhitespaceLocation'. string remainingText = text.Substring( firstNonWhitespaceLocation ); trimmedText.Append( remainingText ); return trimmedText.ToString(); } #endregion Methods  } } // '\r\n\t\tprivate string x;'