Click here to Skip to main content
15,867,991 members
Articles / Desktop Programming / WPF

Zip My Code

Rate me:
Please Sign up or sign in to vote.
4.78/5 (17 votes)
20 Dec 2009CPOL3 min read 71.4K   2K   48   29
A utility stripping your source code to the essential core and then compressing it to a nice CodeProject article attachment.

Image 1

Image 2

Contents

Introduction

This article will present you with a utility that may come in handy if you are a contributor on CodeProject. The utility eases stripping down the source to the pure essential of what article code attachments should be. It is also very handy to do so, using simple drag-and-drop or the console interface. If enough people find it useful, it may earn its place as an author tool on the Free Tools for Authors page, which according to me is rather scant.

I am aware that this article won't get any good ratings or popularity, it is after all a very basic application. It focuses on solving a minor problem rather than presenting you with really cool code. There are, however, some grains of gold buried within the application that might interest some of you.

Credits

  • First and foremost credit goes to CodeProject member SAKryukov for taking his time to review the article and give extremely constructive feedback.
  • MADEBITS for their NETZ, a utility I use for compressing and packing ZipMyCode into one assembly.
  • The ICSharpCode team for their SharpZipLib, an assembly I use for Zip compression.
  • CodeProject member Peter Palotas for his Plossum command line parser.

How to Use the Utility

The UI Part

  1. Type or press the Browse... button and select the source path.
  2. Press Start zipping my code.
  3. A compressed zip archive will be created alongside your source.

You have the option to add, edit, and remove exclude patterns by expanding Options.

The Console Part

The application has, in addition to the UI, a command line interface:

C:\>ZipMyCode /?
ZipMyCode  version 1.0.2.0
Copyright © 2009

Usage:
   ZipMyCode path [/c:file] [/e:value[+value]] [/o:file]
   ZipMyCode /u

Strip and compress:
   /c,                 Specifies the configuration file with exclude
   /configuration      patterns, one pattern per line. If file is
                       specified, the default exclude patterns are ignored.
   /e, /exclude        Specifies the list of exclude patterns. If patterns are
                       specified, the default exclude patterns are ignored.
   /o, /output         Specifies the name of the compressed output file.

Uninstallation:
   /u, /uninstall      Removes saved settings, i.e. cleans
                       up the application footprint.

Help:
   /?, /h, /help       Displays this help text.

The interface deliberately takes a single path as argument, because you are then capable of dropping any folder on top of the application (or shortcut, if you make one) using Windows drag-and-drop, since the first argument passed to the application is the dropped folder path.

Grains of Gold

Built Using MVVM and the Dialog Service

The application is built using MVVM, which seems to be on everybody's lips for the moment. Opening dialogs is accomplished with the dialog service from my previous article: Showing Dialogs When Using the MVVM Pattern. This time, the service features a FolderBrowserDialog instead of a OpenFileDialog. The idea of a having a service opening the dialogs still holds for these small applications, no clouds on the horizon yet. Feel free to give the article a glance, and give me your opinion on the idea.

New Way of Writing Properties

The ViewModels are implemented with a new way of writing properties. We usually write a ViewModel property like this:

C#
private string someText;

public string SomeText
{
  get { return someText; }
  set
  {
    if (someText != value)
    {
      someText = value;
      OnPropertyChanged("SomeText");
    }
  }
}

With a little help from some methods, we now can write:

C#
public string SomeText
{
  get { return Property(() => SomeText); }
  set { Property(() => SomeText, value); }
}

We no longer have to write the repetitive setter code, and Visual Studio IntelliSense is better at picking up this format than the first containing a string in the OnPropertyChanged method.

The code supporting this new format exists in ViewModelBase:

C#
/// <summary>
/// Gets the value of a property matching the given expression.
/// </summary>
/// <typeparam name="T">The property type.</typeparam>
/// <param name="nameExpression">The expression pointing to the property.</param>
/// <returns>The property value if existing; otherwise default.</returns>
protected T Property<T>(Expression<Func<T>> nameExpression)
{
  PropertyItem p;
  if (properties.TryGetValue(nameExpression.ToString(), out p))
  {
    return (T)p.Value;
  }

  return default(T);
}

/// <summary>
/// Sets the value of a property matching the given expression.
/// </summary>
/// <typeparam name="T">The property type.</typeparam>
/// <param name="nameExpression">The expression pointing to the property.</param>
/// <param name="value">The value to set.</param>
protected void Property<T>(Expression<Func<T>> nameExpression, T value)
{
  // Get the key of the property
  string key = nameExpression.ToString();

  PropertyItem p;
  if (properties.TryGetValue(key, out p))
  {
    // Make sure the property value has changed
    if ((p.Value == null && value == null) || 
        (p.Value != null && p.Value.Equals(value)))
    {
      return;
    }

    // Set the new value
    p.Value = value;
  }
  else
  {
    // Create the new property item
    p = new PropertyItem
    {
      Name = GetPropertyName(nameExpression),
      Value = value
    };

    // Add the new propery item
    properties.Add(key, p);
  }

  // Raise property changed event
  OnPropertyChanged(new PropertyChangedEventArgs(p.Name));
}

/// <summary>
/// Gets the property name of the expression.
/// </summary>
/// <typeparam name="T">The property type.</typeparam>
/// <param name="nameExpression">The name expression.</param>
/// <returns>The property name of the expression.</returns>
private static string GetPropertyName<T>(Expression<Func<T>> nameExpression)
{
  UnaryExpression unaryExpression = nameExpression.Body as UnaryExpression;

  // Convert name expression into MemberExpression
  MemberExpression memberExpression = unaryExpression != null ?
    (MemberExpression)unaryExpression.Operand :
    (MemberExpression)nameExpression.Body;

  return memberExpression.Member.Name;
}

/// <summary>
/// Class wrapping up the essential parts of a property.
/// </summary>
class PropertyItem
{
  /// <summary>
  /// Gets or sets the name.
  /// </summary>
  public string Name { get; set; }

  /// <summary>
  /// Gets or sets the value.
  /// </summary>

  public object Value { get; set; }
}

Algorithm Searching Files and Folders with Exclude Pattern

The .NET framework supports searching for files and folders using DirectoryInfo.GetFiles(string searchPattern) and DirectoryInfo.GetDirectories(string searchPattern), but it doesn't feature one to search using an exclude pattern. When it came to deciding what files and folders are acceptable in a compressed source attachment, I decided that it was easier for a user to state what he/she didn't want rather than specifying what he/she did want.

The following code exists in GetFilesTask and uses LINQ to solve the problem:

C#
/// <summary>
/// Recursive method finding all files from a directory and its sub-directory.
/// </summary>
/// <param name="directory">The directory.</param>
/// <param name="excludePatterns">The file and directory exclude pattern.</param>
private IEnumerable<string> RecursiveGetFiles(DirectoryInfo directory,
  string[] excludePatterns)
{
  // Find files not matching the exclude patterns
  IEnumerable<FileInfo> files =
    (from file in directory.GetFiles()
     select file)
    .Except(GetExcludedFiles(directory, excludePatterns), new FileInfoEqualityComparer());

  // Yield files not matching the exclude patterns
  foreach (FileInfo file in files)
  {
    yield return file.FullName;
  }

  // Find directories not matching the exclude patterns
  IEnumerable<DirectoryInfo> subDirectories =
    (from subDirectory in directory.GetDirectories()
     select subDirectory)
    .Except(GetExcludedDirectories(directory, excludePatterns),
      new DirectoryInfoEqualityComparer());

  // Search files in sub-directories not matching the exclude pattern
  foreach (DirectoryInfo subDirectory in subDirectories)
  {
    // Yield all files not matching the exclude patterns
    foreach (string file in RecursiveGetFiles(subDirectory, excludePatterns))
    {
      yield return file;
    }
  }
}

/// <summary>
/// Gets the excluded files in a specified directory.
/// </summary>
/// <param name="directory">The directory.</param>
/// <param name="excludePatterns">The file exclude pattern.</param>
private IEnumerable<FileInfo> GetExcludedFiles(DirectoryInfo directory,
  string[] excludePatterns)
{
  return
    from excludePattern in excludePatterns
    from filesMatchingExcludePattern in directory.GetFiles(excludePattern)
    select filesMatchingExcludePattern;
}

/// <summary>

/// Gets the excluded sub-directories in a specified directory.
/// </summary>
/// <param name="directory">The directory.</param>
/// <param name="excludePatterns">The directory exclude pattern.</param>
private IEnumerable<DirectoryInfo> GetExcludedDirectories(DirectoryInfo directory,
  string[] excludePatterns)
{
  return
    from excludePattern in excludePatterns
    from directoriesMatchingExcludePattern in directory.GetDirectories(excludePattern)
    select directoriesMatchingExcludePattern;
}

class FileInfoEqualityComparer : IEqualityComparer<FileInfo>

{
  public bool Equals(FileInfo x, FileInfo y)
  {
    return x.FullName.Equals(y.FullName);
  }

  public int GetHashCode(FileInfo obj)
  {
    return obj.FullName.GetHashCode();
  }
}

class DirectoryInfoEqualityComparer : IEqualityComparer<DirectoryInfo>
{
  public bool Equals(DirectoryInfo x, DirectoryInfo y)
  {
    return x.FullName.Equals(y.FullName);
  }

  public int GetHashCode(DirectoryInfo obj)
  {
    return obj.FullName.GetHashCode();
  }
}

History

  • 1.0.2.0 (20 December 2009)
    • Console
      • Implemented a console interface
      • Implemented uninstall
    • UI
      • Source path is editable and handles validation
      • Changed default path
      • Fixed initial focus
      • Added additional default ignore patterns
  • 1.0.1.0 (25 October 2009)
  • 1.0.0.0 (7 July 2009)
    • Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Axis Communications
Sweden Sweden
Got my first computer in the 90's and loved it even though it sounded like a coffeemaker.

Now getting paid for designing cool applications, and drinks the coffee instead of listening to it being made.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Sergey Alexandrovich Kryukov9-Jan-11 13:58
mvaSergey Alexandrovich Kryukov9-Jan-11 13:58 
GeneralRe: My vote of 5 Pin
FantasticFiasco10-Jan-11 7:56
FantasticFiasco10-Jan-11 7:56 
GeneralAlternative to Plossum Pin
Sergey Alexandrovich Kryukov9-Jan-11 13:56
mvaSergey Alexandrovich Kryukov9-Jan-11 13:56 
GeneralRe: Alternative to Plossum Pin
FantasticFiasco10-Jan-11 7:55
FantasticFiasco10-Jan-11 7:55 
GeneralRe: Alternative to Plossum Pin
Sergey Alexandrovich Kryukov10-Jan-11 13:32
mvaSergey Alexandrovich Kryukov10-Jan-11 13:32 
QuestionMisuse of SVN? Pin
Sergey Alexandrovich Kryukov25-Oct-09 14:34
mvaSergey Alexandrovich Kryukov25-Oct-09 14:34 
AnswerRe: Misuse of SVN? Pin
FantasticFiasco26-Oct-09 1:45
FantasticFiasco26-Oct-09 1:45 
GeneralRe: Misuse of SVN? Pin
Sergey Alexandrovich Kryukov10-Nov-09 8:32
mvaSergey Alexandrovich Kryukov10-Nov-09 8:32 
Please don't get me wrong.
I do agree your software has significant value.

To re-formulate my concern: I simply think your point in the article text about removing revision control artifacts does not advertise your work well; if you will, your work itself is better than the way you describe it.

Back to my point, using your application would not help those people who misuse SVN by trying to submit working directories and those who do not understand which files are source file and which are not. (Believe me, this is a real-life and common problem.) By advertising this feature, you implicitly and unwillingly approve some sloppy practices of the people who may get benefit of your software. But this advertisement does not reach your goals because 1) it does not help those poor users anyway, 2) and because good users may mentally associate yourself with those poor users (as I did before your reply) because you kind of encouraged poor usage. This kind of advertising is quite legitimate; only less claim often advertise better then more...

Do you see my point? I am thankful for your work, but I would also prefer good work to be presented well, to everyone's benefit.

Sergey A Kryukov

GeneralRe: Misuse of SVN? Pin
FantasticFiasco11-Nov-09 8:22
FantasticFiasco11-Nov-09 8:22 
GeneralRe: Misuse of SVN? Pin
Sergey Alexandrovich Kryukov11-Nov-09 12:26
mvaSergey Alexandrovich Kryukov11-Nov-09 12:26 
GeneralWhat to improve [modified] Pin
Sergey Alexandrovich Kryukov12-Nov-09 6:05
mvaSergey Alexandrovich Kryukov12-Nov-09 6:05 
GeneralRe: What to improve Pin
FantasticFiasco21-Nov-09 9:02
FantasticFiasco21-Nov-09 9:02 
GeneralRe: What to improve Pin
FantasticFiasco20-Dec-09 11:53
FantasticFiasco20-Dec-09 11:53 
GeneralVery nice (Re: What to improve) Pin
Sergey Alexandrovich Kryukov27-Dec-09 16:19
mvaSergey Alexandrovich Kryukov27-Dec-09 16:19 
GeneralRe: Very nice (Re: What to improve) Pin
FantasticFiasco28-Dec-09 5:23
FantasticFiasco28-Dec-09 5:23 
GeneralTwo more improvements Pin
Sergey Alexandrovich Kryukov12-Nov-09 6:57
mvaSergey Alexandrovich Kryukov12-Nov-09 6:57 
GeneralRe: Two more improvements Pin
FantasticFiasco12-Nov-09 9:17
FantasticFiasco12-Nov-09 9:17 
GeneralRe: Two more improvements Pin
FantasticFiasco21-Nov-09 9:03
FantasticFiasco21-Nov-09 9:03 
Generalthe analog Pin
drweb8620-Jul-09 2:30
drweb8620-Jul-09 2:30 
GeneralRe: the analog Pin
FantasticFiasco20-Jul-09 2:55
FantasticFiasco20-Jul-09 2:55 
Generalworks BUT complicated!! Pin
SurlyCanuck14-Jul-09 8:36
professionalSurlyCanuck14-Jul-09 8:36 
GeneralRe: works BUT complicated!! Pin
FantasticFiasco14-Jul-09 9:06
FantasticFiasco14-Jul-09 9:06 
GeneralRe: works BUT complicated!! Pin
SurlyCanuck14-Jul-09 10:46
professionalSurlyCanuck14-Jul-09 10:46 
GeneralFails to download Pin
Johnno9-Jul-09 8:54
Johnno9-Jul-09 8:54 
GeneralRe: Fails to download Pin
FantasticFiasco9-Jul-09 9:53
FantasticFiasco9-Jul-09 9:53 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.