10101010101001

0101010101001

Archive for February 2009

C# Fluent Interface with Type Safe Polymorphic Chain Calls

with 2 comments

Presuming you already know what fluent interfaces are I’ll skip to a certain case that caused me troubles:

Let’s say you have two Order classes: NormalOrder and SpecialOrder. Both of them inherit a base Order class. fluent_interfaces_order_class_hierarchy

I wanted to built a fluent interface for unit testing them so that I could say something like:

New.DefaultNormalOrder.AddItem(10).AddFreeShipping()   
.....
New.DefaultSpecialOrder.AddItem(15).AddLargeSpecialTaxes();

I thought it would be easy, just use the a builder pattern or extension methods in C#.
But there’s a big inconvenience with both of them that I’ll illustrate below:

Builder Problem:

    public class OrderBuilder
    {
        private Order o = new Order();

        public virtual OrderBuilder AddItem(int item)
        {
            o.AddItem(item);
            return this;
        }
    }

    public class NormalOrderBuilder : OrderBuilder
    {
        private NormalOrder no = new NormalOrder();

        public NormalOrderBuilder AddFreeShipping()
        {
            no.ShippingCost = 0;
            return this;
        }
    }

    public class SpecialOrderBuilder : OrderBuilder
    {
        private SpecialOrder so = new SpecialOrder();

        public SpecialOrderBuilder AddLargeSpecialTaxes()
        {
            so.SpecialTaxes = Int32.MaxValue;
            return this;
        }
    }

Can you spot the problem presuming that the New.DefaultNormalOrder returns a NormalOrderBuilder and that New.DefaultSpecialOrder returns a SpecialOrderBuilder?
Here it is if you try to write a small example using those builders:

//This works
New.DefaultNormalOrder.AddFreeShipping().AddItem(10);  
//This doesn't
New.DefaultNormalOrder.AddItem(10).AddFreeShipping();  

This is because AddItem is polymorphic and returns the base class OrderBuilder. Even though New.DefaultNormalOrder.AddItem(10) is actually a NormalOrderBuilder at runtime, you can’t call AddFreeShipping() because Include item returns the base OrderBuilder class breaking our chain calls :(

The same problem happens when using extension methods.

One solution would be to leave all the chain calls to the base class at the end, but that doesn’t solve the problem because there might be times when we really need to call the methods from the base class first.
Another solution would be to cast, but those won’t be fluent interfaces in my opinion :(
The fluency is gone when you have to put ugly paranthesis and casts where there’s no real need for them.
Of course you may say that all I need is override the AddItem method, call the base.AddItem and do a cast on to the NormalOrderBuilder class on the result:

    public class NormalOrderBuilder : OrderBuilder
    {
.....

        public override NormalOrderBuilder AddItem(int item)
        {
            return (NormalOrderBuilder)base.AddItem(item);
        }
    }

But this doesn’t scale well because if you have lots of methods in the base OrderBuilder class you’ll have to do this casting for all of them in all your inheritors. OK, maybe this is an exaggeration, but you’ve got to admit it’s plumbing code :)
The only solution that I’ve found is to use a combination of the Builder pattern with generics and extension methods with generics:

    public class OrderBuilder<T> where T:Order
    {
        protected internal T order = default(T);

        public OrderBuilder(T o)
        {
            order = o;
        }

        public OrderBuilder AddItem(int item)
        {
            order.AddItem(item);
            return this;
        }
    }


    public static class OrderBuilderExtensionMethods
    {
        public static OrderBuilder<T> AddFreeShipping<T>(this OrderBuilder<T> ob) where T:NormalOrder
        {
            ((NormalOrder) ob.order).ShippingCost = 0;
            return ob;
        }

        public static OrderBuilder<T> AddLargeSpecialTaxes<T>(this OrderBuilder<T> ob) where T:SpecialOrder
        {
            ((SpecialOrder) ob.order).SpecialTaxes = Int32.MaxValue;
            return ob;
        }
    }

    public class New
    {
        public static OrderBuilder<NormalOrder> DefaultNormalOrder = new OrderBuilder<NormalOrder>(new NormalOrder());
        public static OrderBuilder<SpecialOrder> DefaultSpecialOrder = new OrderBuilder<SpecialOrder>(new SpecialOrder());
    }

The compiler will enforce the constraints we specified at compile time like: “where T:NormalOrder”, so here goes the example again:

//This works
New.DefaultNormalOrder.AddFreeShipping().AddItem(10);  
//This works :D
New.DefaultNormalOrder.AddItem(10).AddFreeShipping();  
//This doesn't, the compiler screams in pain because of the generic constraints :)
New.DefaultSpecialOrder.AddItem(10).AddFreeShipping();

This is cool, because now our call chain doesn’t break and it’s type safe so we can’t make invalid call chains :D

In the end though, I’m pretty sure I wouldn’t recommend this approach because code is less readable, but it was a good exercise for generic extension methods with generic constraints.

Advertisements

Written by Liviu Trifoi

February 16, 2009 at 1:56 pm

xsl number format localization

leave a comment »

These days I’ve had to do some xslt stylesheets that are processed by ibex pdf creator.
One of the things that I found little information on was how to format a number based on current regional settings of the user.
Example: I have the decimal number 1234.56.
What I want is that if the current regional settings point to English (US) culture then display 1,234.56 and if the current regional settings point to some other culture, let’s say Romanian culture, then display 1.234,56

Approach 1) – That doesn’t work.
Initially I thought I could use the built-in format-number function to format numbers like this:

  <xsl:decimal-format name="en-US" decimal-separator="." grouping-separator="," />
  <xsl:decimal-format name="ro-RO" decimal-separator="," grouping-separator=" " />
....
  <xsl:variable name="df" select="//NumberDecimalSeparator/."></xsl:variable>
...
 <xsl:value-of select='format-number(1234.56, "###,##0.00",$df)' />

I would only have to serialize from the calling program an element called NumberDecimalSeparator that would contain the correct format name.
But the xslt decimal format cannot be dynamic and the xslt processor complains about it :(
So this approach is no use.

Approach 2) – Works but it’s ugly

You can have a static decimal format and modify it from the calling program just before calling your xslt processor.
In my case before calling ibex from C# I had to modify the following xml called ‘DecimalSeparators.xsl’:

 <?xml version="1.0" ?>
 <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

   <xsl:decimal-format name="i" decimal-separator="." grouping-separator="," />
  </xsl:stylesheet>

And our initial xsl looks like this.

  <xsl:include href="DecimalSeparators.xsl"/>
  <xsl:variable name="ts" select="//NumberGroupSeparator/."></xsl:variable>
  <xsl:variable name="ds" select="//NumberDecimalSeparator/."></xsl:variable>

  <xsl:value-of select="format-number(1234.56, concat('###',$ts,'##0',$ds,'00'),'i')" />

So because we’re modifying the DecimalSeparators.xsl before runtime with the correct separators and because we’re also inserting the correct separators in the input xml everything will be just fine.

And you could use this piece of code to modify DecimalSeparators.xsl from C#

        private static XmlNamespaceManager CreateNsMgr(XmlDocument doc)
        {
            XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
            foreach (XmlAttribute attr in doc.SelectSingleNode("/*").Attributes)
                if (attr.Prefix == "xmlns") nsmgr.AddNamespace(attr.LocalName, attr.Value);
            return nsmgr;
        }

        public void GenerateCultureSpecificDecimalFormats()
        {
            string numberGroupSeparator = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator;
            string numberDecimalSeparator = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;

            XmlDocument doc = new XmlDocument();
            doc.Load("DecimalFormats.xsl");
            XmlElement node = (XmlElement)doc.SelectSingleNode("//xsl:decimal-format", CreateNsMgr(doc));
            node.SetAttribute("name", "i");
            node.SetAttribute("decimal-separator", numberDecimalSeparator);
            node.SetAttribute("grouping-separator", numberGroupSeparator);
            XmlTextWriter writer = new XmlTextWriter("DecimalFormats.xsl", null);
            doc.WriteTo(writer);
            writer.Close();
        }

But it’s a very ugly solution.

So here goes
Approach 3) Elegant.

You can write a custom .net function and call it from the xsl using the extension-objects mechanism provided by xsl.

Here’s the .net class that has that custom function:

    /// <summary>
    /// Provides number formatting methods used by an xslt processor at runtime.
    /// </summary>
    public class XsltNumberFormatter
    {

        /// <summary>
        /// Formats provided number using a culture specific transformation of the provided format
        /// as the format.
        /// </summary>
        ///
<param name="numberFormat">The number format in the en-US culture.</param>
        ///
<param name="strNumber">The decimal number as string.</param>
        /// <returns>
        /// Formatted <see cref="strNumber"/> using a culture specific transformation of <see cref="numberFormat"/>
        /// as the format.
        /// If
<paramref name="strNumber"/> is invalid it returns <see cref="Properties.Resources.NaN"/>
        /// </returns>
        /// <example>
        /// FormatNumber('1234.56','###,##0.00')
        /// will return 1,234.56 if the current culture is en-US or 1.234,56 if the current culture is ro-RO
        /// </example>
        public string FormatNumber(string strNumber, string numberFormat)
        {
            try
            {
                string stringDotFormatArg = "{0:" + numberFormat + "}";
                decimal number = Decimal.Parse(strNumber, new CultureInfo("en-US").NumberFormat);
                string result = String.Format(stringDotFormatArg, number);

                return result;
            }
            catch (Exception ex)
            {
                return "NaN";
            }

        }

    }

You would plug it into ibex xslt processor using this .net code:

using System;
using System.IO;
using System.Collections;

using ibex4;


public class test {

   static void Main( string[] args ) {

        FODocument doc = new FODocument();

        FileStream xml = new FileStream( "exslt.xml", FileMode.Open, FileAccess.Read );
        FileStream xsl = new FileStream( "exslt.xsl", FileMode.Open, FileAccess.Read );
        FileStream pdf = new FileStream( "exslt.pdf", FileMode.Create, FileAccess.Write );

        Hashtable xslArgs = new Hashtable();

        xslArgs.Add("http://www.yourcompany.com/numbers", new XsltNumberFormatter());

        doc.generate( xml, xsl, pdf, true, xslArgs );
   }

}

All what is left is to call it from your xsl file :)

<?xml version='1.0' encoding='utf-8'?>

<xsl:stylesheet version="2.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
	xmlns:fo="http://www.w3.org/1999/XSL/Format"
	xmlns:ibex="http://www.xmlpdf.com/2003/ibex/Format"
        xmlns:my-num="http://www.yourcompany.com/numbers"  exclude-result-prefixes="my-num">

        <xsl:value-of select="my-num:FormatNumber('1234.56','###,###,##0.00')"/>

</xsl:stylesheet>

So now whenever you need to format something you just specify it’s format using en-US culture formats in your xsl (with ‘,’ ‘.’ as separators) and everything will be translated into the current culture format by our custom FormatNumber function.

Enjoy! :)

Written by Liviu Trifoi

February 11, 2009 at 4:49 pm