// 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;
using System.Text.RegularExpressions;
using Rauchy.Regionerate.Shared.Components;
using Rauchy.Regionerate.Shared.Components.Rendering;
namespace Rauchy.Regionerate.ServiceLayer.Components.Tools
{
///
/// Removes existing regions from the specified code.
///
/// The code.
/// The same code blocks, without the unwanted regions.
///
/// Thrown when code is null.
///
///
/// Handles type-level regions only, does not remove method-level or property-level regions.
///
public sealed class RegionUnpacker
{
#region Fields (2)
/////
///// Removes text from the specified code.
/////
///// The code.
///// The same code blocks, without the unwanted text.
/////
///// Thrown when code is null.
/////
/////
///// Handles type-level text only, does not remove method-level or property-level text.
/////
//public string Unpack(string code)
//{
// if (code == null)
// {
// throw new ArgumentNullException("code");
// }
// IList lines = TextUtilities.GetLines(code);
// StringBuilder filteredCode = new StringBuilder();
// foreach (string line in lines)
// {
// bool isRegionDeclaration = ShouldRemove(line);
// if (!isRegionDeclaration)
// {
// filteredCode.Append(line);
// }
// }
// return filteredCode.ToString();
//}
private readonly Symbol _symbol;
private readonly CodeLayout.UnpackOptions _unpackOptions;
#endregion Fields
#region Constructors (1)
///
/// Initializes a new instance of the class.
///
/// The symbol.
/// The unpack options.
public RegionUnpacker( Symbol symbol, CodeLayout.UnpackOptions unpackOptions )
{
_symbol = symbol;
_unpackOptions = unpackOptions;
}
#endregion Constructors
#region Methods (8)
// Public Methods (1)
///
/// Removes text from the specified code.
///
/// The code.
/// The same code blocks, without the unwanted text.
///
/// Thrown when code is null.
///
///
/// Handles type-level text only, does not remove method-level or property-level text.
///
public string Unpack( string code )
{
if ( code == null )
{
throw new ArgumentNullException( "code" );
}
IList lines = code.GetLines();
IList unpackedLines = Unpack( lines );
string unpackedCode = JoinLines( unpackedLines );
return unpackedCode;
}
// Private Methods (7)
private int FindEndRegionPosition( IList lines )
{
int currentLineNumber = 0;
while ( currentLineNumber < lines.Count )
{
string line = lines[ currentLineNumber ].Trim();
if ( line.StartsWith( "#region" ) )
{
// Skip to the line after this region ends.
List remainingLines = GetLinesStartingFrom( lines, currentLineNumber + 1 );
try
{
currentLineNumber += FindEndRegionPosition( remainingLines ) + 1;
}
catch ( ArgumentOutOfRangeException e )
{
string message = "Cannot find a matching #endregion declaration for " + line;
throw new UnpackingException( message, e );
}
}
else if ( line.StartsWith( "#endregion" ) )
{
return currentLineNumber;
}
currentLineNumber++;
}
throw new ArgumentOutOfRangeException( "Cannot locate an #endregion declaration" );
}
private static List GetLinesStartingFrom( IList lines, int from )
{
List remainingLines = new List();
for ( int i = from; i < lines.Count; i++ )
{
remainingLines.Add( lines[ i ] );
}
return remainingLines;
}
private static string JoinLines( IList lines )
{
StringBuilder jointLines = new StringBuilder();
foreach ( string line in lines )
{
jointLines.Append( line );
}
return jointLines.ToString();
}
private bool LineMatchesAnyUndesiredPattern( string line )
{
if ( _unpackOptions == null )
{
return false;
}
else if ( _unpackOptions.RegionPatterns.Count == 0 )
{
return false;
}
else if ( line.Trim().StartsWith( "#region" ) == false )
{
return false;
}
else
{
foreach ( string pattern in _unpackOptions.RegionPatterns )
{
Regex regex = new Regex( pattern );
bool isMatch = regex.IsMatch( line );
if ( isMatch )
{
return true;
}
}
// No pattern matches this line.
return false;
}
}
///
/// Determines wether a line, when not placed in a method or property (or such), should be removed.
///
///
/// If the line begins a region or ends a region, it should be removed.
///
/// The line.
private bool ShouldRemove( string line )
{
string trimmedLine = line.Trim();
if ( trimmedLine.Length == 0 )
{
return false;
}
bool symbolIsEmbedded = _symbol.IsEmbeddedIn( trimmedLine );
if ( symbolIsEmbedded )
{
return true;
}
else
{
return false;
}
}
private bool SymbolIsEmbeddedIn( string line )
{
if ( _symbol == null )
{
return false;
}
else
{
return _symbol.IsEmbeddedIn( line );
}
}
private IList Unpack( IList lines )
{
int currentLineNumber = 0;
while ( currentLineNumber < lines.Count )
{
string line = lines[ currentLineNumber ].Trim();
if ( SymbolIsEmbeddedIn( line ) )
{
lines.RemoveAt( currentLineNumber );
}
else if ( LineMatchesAnyUndesiredPattern( line ) )
{
int regionDeclarationPosition = currentLineNumber;
// Find the corresponding #endregion tag and remove both.
List remainingLines = GetLinesStartingFrom( lines, currentLineNumber + 1 );
int endRegionDeclarationPosition = 0;
try
{
int endRegionDeclarationPositionInsideRemainingLines = FindEndRegionPosition( remainingLines );
endRegionDeclarationPosition = endRegionDeclarationPositionInsideRemainingLines +
currentLineNumber;
}
catch ( ArgumentOutOfRangeException e )
{
string message = "Cannot find a matching #endregion declaration for " + line;
throw new UnpackingException( message, e );
}
// Remove both #region and #endregion.
lines.RemoveAt( regionDeclarationPosition );
lines.RemoveAt( endRegionDeclarationPosition );
}
else
{
currentLineNumber++;
}
}
return lines;
}
#endregion Methods
#region Nested Classes (1)
public class UnpackingException : Exception
{
#region Constructors (1)
public UnpackingException( string message, Exception innerException ) : base( message, innerException ) {}
#endregion Constructors
}
#endregion Nested Classes
}
}