// 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;'