Wednesday, December 17, 2008

Using the Proxy Pattern to Write to Multiple TextWriters

I was working on a data synchronizing application the other day.  I needed to write to a file for the export, write to a string builder for logging and analysis, and write to the console for debugging.  I know that it's pretty common that I'll need to write to more than 1 text stream at the same time, so I figured I could write a quick proxy application to write to a collection of TextWriters.Please comment on this post and let me know how this TextWriterProxy article helped you.Here's what I came up with:
    1 using System.Collections.Generic;
    2 using System.Text;
    3 using System.IO;
    4 
    5 namespace ESG.Utilities
    6 {
    7     public class TextWriterProxy : TextWriter
    8     {
    9         // store TextWriters here
   10         private List<TextWriter> _writers = new List<TextWriter>();
   11 
   12         #region Properties
   13 
   14         /// <summary>
   15         /// This property returns Encoding.Default.  The TextWriters in the
   16         /// TextWriterProxy collection can have any encoding.  However, this
   17         /// property is required.
   18         /// </summary>
   19         public override Encoding Encoding { get { return Encoding.Default; } }
   20 
   21         /// <summary>
   22         /// Gets or sets the line terminator string used by the TextWriters in
   23         /// the TextWriterProxy collection.
   24         /// </summary>
   25         public override string NewLine
   26         {
   27             get
   28             {
   29                 return base.NewLine;
   30             }
   31 
   32             set
   33             {
   34                 foreach (TextWriter tw in _writers)
   35                     tw.NewLine = value;
   36 
   37                 base.NewLine = value;
   38             }
   39         }
   40 
   41         #endregion
   42 
   43         #region Methods
   44 
   45         /// <summary>
   46         /// Add a new TextWriter to the TextWriterProxy collection.  Setting properties 
   47         /// or calling methods on the TextWriterProxy will perform the same action on 
   48         /// each TextWriter in the collection.
   49         /// </summary>
   50         /// <param name="writer">The TextWriter to add to the collection</param>
   51         public void Add(TextWriter writer)
   52         {
   53             // don't add a TextWriter that's already in the collection
   54             if (!_writers.Contains(writer))
   55                 _writers.Add(writer);
   56         }
   57 
   58         /// <summary>
   59         /// Remove a TextWriter from the TextWriterProxy collection.
   60         /// </summary>
   61         /// <param name="writer">The TextWriter to remove from the collection</param>
   62         /// <returns>True if the TextWriter was found and removed; False if not.</returns>
   63         public bool Remove(TextWriter writer)
   64         {
   65             return _writers.Remove(writer);
   66         }
   67 
   68 
   69         // this is the only Write method that needs to be overridden
   70         // because all of the Write methods in a TextWriter ultimately
   71         // end up calling Write(char)
   72 
   73         /// <summary>
   74         /// Write a character to the text stream of each TextWriter in the 
   75         /// TextWriterProxy collection.
   76         /// </summary>
   77         /// <param name="value">The char to write</param>
   78         public override void Write(char value)
   79         {
   80             foreach (TextWriter tw in _writers)
   81                 tw.Write(value);
   82 
   83             base.Write(value);
   84         }
   85 
   86         /// <summary>
   87         /// Closes the TextWriters in the TextWriterProxy as well as the 
   88         /// TextWriterProxy instance and releases any system resources
   89         /// associated with them.
   90         /// </summary>
   91         public override void Close()
   92         {
   93             foreach (TextWriter tw in _writers)
   94                 tw.Close();
   95 
   96             base.Close();
   97         }
   98 
   99         /// <summary>
  100         /// Releases all resources used by the TextWriterProxy and by the 
  101         /// TextWriters in the TextWriterProxy collection. 
  102         /// </summary>
  103         /// <param name="disposing">Pertains only to the TextWriterProxy instance: 
  104         /// true to release both managed and unmanaged resources; false to release 
  105         /// only unmanaged resources.</param>
  106         protected override void Dispose(bool disposing)
  107         {
  108             foreach (TextWriter tw in _writers)
  109                 tw.Dispose();
  110 
  111             base.Dispose(disposing);
  112         }
  113 
  114         /// <summary>
  115         /// Clears all buffers for each TextWriter in the TextWriterProxy 
  116         /// collection and causes all buffered data to be written
  117         /// to the underlying device.
  118         /// </summary>
  119         public override void Flush()
  120         {
  121             foreach (TextWriter tw in _writers)
  122                 tw.Flush();
  123 
  124             base.Flush();
  125         }
  126 
  127         #endregion
  128     }
  129 }

So far, it works great. It cleans up a lot of my code and gives me the option to write to any number of TextWriters with only one call. Further, if you are calling a method that takes a TextWriter as a parameter, you can pass the TextWriterProxy to it because it extends the TextWriter class. Here's what the usage syntax looks like:
    1 // create a TextWriterProxy instance
    2 TextWriterProxy proxy = new TextWriterProxy();
    3 
    4 // add the Console.Out TextWriter
    5 proxy.Add(Console.Out);
    6 
    7 // you can still write directly to console
    8 Console.WriteLine(string.Empty.PadRight(80, '='));
    9 
   10 // add a StreamWriter for a FileStream
   11 FileStream fs = new FileStream("C:\\TestExportFileAutoGen.abx", FileMode.Create);
   12 StreamWriter resultWriter = new StreamWriter(fs);
   13 proxy.Add(resultWriter);
   14 
   15 // add a StringWriter for a StringBuilder
   16 StringBuilder sb = new StringBuilder();
   17 StringWriter resultStringWriter = new StringWriter(sb);
   18 proxy.Add(resultStringWriter);
   19 
   20 // call a method that takes a TextWriter
   21 ClientSync.GenerateSessionDataExport("Sync.ServerExport", proxy);
   22 
   23 // write directly to the TextWriterProxy
   24 proxy.WriteLine("Export Complete!");
   25 
   26 // close all of my writers
   27 proxy.Close();

And there you have it. A TextWriterProxy class to write to multiple TextWriters at once.
I really appreciate comments so please feel free to comment on my posts. Whether you agree or disagree, I'd love to hear from you. Also, feel free to link back to your own blog in your comments. You can even subscribe to an RSS feed of the comments on this thread.

© 2008 — , D. Patrick Caldwell, President, Autopilot Consulting, LLC

Thursday, December 4, 2008

An Online Image Thumbnailer

Hey folks,

I've published a small online image thumbnail utility. If it proves useful (without bogging down our servers), I'll leave it up for public consumption. If you're interested in reading a little more about it, you can find the story below. If you'd just like to see the utility, visit D. Patrick Caldwell's Image Thumbnailer.

So, there I was, trying to get a background off of my Picasa Web Albums to put on my iPhone. In the iPhone Safari browser, you can save images by holding your finger on the image until a save dialog box comes up. Problem is, Picasa has somehow (somewhy) disabled it. So, I figured a bookmarklet would help me out. I could then link directly to the image and all of the Picasa scripts would be gone.

I found a few bookmarklets that displayed all of the images in the page, but they all open in the current window and I wanted a new window (and some nice formatting wouldn't hurt either). So, I looked for an online image thumbnailer and couldn't find one. About 30 minutes later, I had a thumbnailer. An hour after that, I had my bookmarklets (and a new background incidentally). I added a little error handling and some logging and had a fully functional service in about 2 hours.

Then I spent about 8 hours styling the welcome page :).

In any event, here's what my Thumbnailer does. I can take any image anywhere on the net (well, most anywhere . . . I have to have access to the image), and produce a thumbnail with one or two constraints: maximum width and maximum height. Here's an example of the same image scaled to 4 different heights:








The same thing works for widths:








Finally, you can specify both width and height and it'll use the most restrictive parameter.
I really appreciate comments so please feel free to comment on my posts. Whether you agree or disagree, I'd love to hear from you. Also, feel free to link back to your own blog in your comments. You can even subscribe to an RSS feed of the comments on this thread.

© 2008 — , D. Patrick Caldwell, President, Autopilot Consulting, LLC