Sunday, September 6, 2015

Enjoying Breeze after Refactoring

Recently I have done some refactoring in one of my c# projects. To show the process, I will give example of a class which was a huge *** 1880 line code with a long train of 22 else-if blocks. After refactoring it became 517 line code and much more manageable. Reflection, delegation, generics and lambda are used in this refactoring. By the way, I've omitted considerable amount of code from the example for readability, so these examples are not in workable state. I will not discuss these examples in detail.....he he.... just going to throw some code to you.

Here is the class before refactoring:

 namespace WebIMCP.GeoToolBox.XMLDOs  
 {  
   public class DefaultLayerBuilder  
   {  
     private DefaultLayerBuilder()  
     {  
     }  
     public static DefaultLayerBuilder Instance(string name, string filePrefix, Type layerType)  
     {  
       DefaultLayerBuilder instance = new DefaultLayerBuilder();  
       instance.name = name;  
       instance.filePrefix = filePrefix;  
       instance.layerType = layerType;  
       instance.addScaleRangeThemesToThemeDefination = true;  
       instance.addSelectionThemeToThemeDefination = true;  
       instance.populateThemeDefination = true;  
       instance.enableThematicScheme = false;  
       return instance;  
     }  
     public DefaultLayerBuilder Enable(bool val)  
     {  
       this.enabled = val;  
       return this;  
     }  
     public DefaultLayerBuilder Description(string val)  
     {  
       this.desc = val;  
       return this;  
     }  
     public ConfigurationLayer Build()  
     {  
       ConfigurationLayer cl = new ConfigurationLayer();  
       defaultScaleRangesData = getDefaultScaleRangesData();  
       defaultPointThemeData = getDefaultPointThemeData();  
       defaultPolylineThemeData = getDefaultPolylineThemeData();  
       defaultPolygonThemeData = getDefaultPolygonThemeData();  
       defaultPolylineLayerRoutingData = getDefaultPolylineLayerRoutingData();  
       defaultPolylineDirectionInfoData = getDefaultPolylineDirectionInfoData();  
       
       if (layerType == typeof(ConfigurationLayerPolygonLayer))  
       {  
         cl.PolygonLayer = new ConfigurationLayerPolygonLayer();  
         this.loadBasicInfoInTypedLayer(cl.PolygonLayer);  
         if (this.enableThematicScheme)  
         {  
           cl.PolygonLayer.ThematicScheme = new List<ConfigurationLayerPolygonLayerThematicScheme>();  
           cl.PolygonLayer.ThematicScheme.Add(new ConfigurationLayerPolygonLayerThematicScheme());  
           cl.PolygonLayer.ThematicScheme[0].Name = "PolygonLayer_DEFAULT_THEMATICSCHEME";  
           cl.PolygonLayer.ThematicScheme[0].ThematicScaleRanges =  
             defaultScaleRangesData  
             .Replace("<ThematicScaleRanges>", "<" + typeof(ConfigurationLayerPolygonLayerThematicSchemeThematicScaleRanges).Name + ">")  
             .Replace("</ThematicScaleRanges>", "</" + typeof(ConfigurationLayerPolygonLayerThematicSchemeThematicScaleRanges).Name + ">")  
             .XMLDeSerialize<ConfigurationLayerPolygonLayerThematicSchemeThematicScaleRanges>();  
           cl.PolygonLayer.ThematicScheme[0].SchemeSelectionTheme = cl.PolygonLayer.ThematicScheme[0].Name + "_SelectionTheme";  
         }  
         else  
         {  
           cl.PolygonLayer.Scheme = new List<ConfigurationLayerPolygonLayerScheme>();  
           cl.PolygonLayer.Scheme.Add(new ConfigurationLayerPolygonLayerScheme());  
           cl.PolygonLayer.Scheme[0].Name = "PolygonLayer_DEFAULT_SCHEME";  
           cl.PolygonLayer.Scheme[0].ScaleRanges =  
             defaultScaleRangesData  
             .Replace("<ScaleRanges>", "<" + typeof(ConfigurationLayerPolygonLayerSchemeScaleRanges).Name + ">")  
             .Replace("</ScaleRanges>", "</" + typeof(ConfigurationLayerPolygonLayerSchemeScaleRanges).Name + ">")  
             .XMLDeSerialize<ConfigurationLayerPolygonLayerSchemeScaleRanges>();  
           cl.PolygonLayer.Scheme[0].SchemeSelectionTheme =  
                         cl.PolygonLayer.Scheme[0].Name + "_SelectionTheme";  
         }  
         if (this.populateThemeDefination)  
         {  
           cl.PolygonLayer.ThemeDefinition = new List<ConfigurationLayerPolygonLayerPolygonTheme>();  
           if (this.addScaleRangeThemesToThemeDefination)  
           {  
             ConfigurationLayerPolygonLayerPolygonTheme defaultRangeTheme =  
               defaultPolygonThemeData  
               .Replace("<Theme", "<" + typeof(ConfigurationLayerPolygonLayerPolygonTheme).Name + "")  
               .Replace("</Theme>", "</" + typeof(ConfigurationLayerPolygonLayerPolygonTheme).Name + ">")  
               .Replace("<DisplayProperties", "<" + layerType.Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
               .XMLDeSerialize<ConfigurationLayerPolygonLayerPolygonTheme>();  
             defaultRangeTheme.Name = cl.PolygonLayer.Scheme[0].ScaleRanges.DefaultRange.DefaultType.Theme;  
             cl.PolygonLayer.ThemeDefinition.Add(defaultRangeTheme);  
             foreach (ConfigurationLayerPolygonLayerSchemeScaleRangesScaleRange scaleRange  
                     in cl.PolygonLayer.Scheme[0].ScaleRanges.ScaleRange)  
             {  
               ConfigurationLayerPolygonLayerPolygonTheme scaleRangeTheme =  
               defaultPolygonThemeData  
               .Replace("<Theme", "<" + typeof(ConfigurationLayerPolygonLayerPolygonTheme).Name + "")  
               .Replace("</Theme>", "</" + typeof(ConfigurationLayerPolygonLayerPolygonTheme).Name + ">")  
               .Replace("<DisplayProperties", "<" + layerType.Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
               .XMLDeSerialize<ConfigurationLayerPolygonLayerPolygonTheme>();  
               scaleRangeTheme.Name = scaleRange.DefaultType.Theme;  
               cl.PolygonLayer.ThemeDefinition.Add(scaleRangeTheme);  
             }  
           }  
           if (this.addSelectionThemeToThemeDefination)  
           {  
             ConfigurationLayerPolygonLayerPolygonTheme selectionTheme =  
               defaultPolygonThemeData  
               .Replace("<Theme", "<" + typeof(ConfigurationLayerPolygonLayerPolygonTheme).Name + "")  
               .Replace("</Theme>", "</" + typeof(ConfigurationLayerPolygonLayerPolygonTheme).Name + ">")  
               .Replace("<DisplayProperties", "<" + layerType.Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
               .XMLDeSerialize<ConfigurationLayerPolygonLayerPolygonTheme>();  
             if (this.enableThematicScheme)  
             {  
               selectionTheme.Name = cl.PolygonLayer.ThematicScheme[0].SchemeSelectionTheme;  
             }  
             else  
             {  
               selectionTheme.Name = cl.PolygonLayer.Scheme[0].SchemeSelectionTheme;  
             }  
             cl.PolygonLayer.ThemeDefinition.Add(selectionTheme);  
           }  
         }  
       }  
       else if (layerType == typeof(ConfigurationLayerPolylineLayer))  
       {  
         cl.PolylineLayer = new ConfigurationLayerPolylineLayer();  
         this.loadBasicInfoInTypedLayer(cl.PolylineLayer);  
         cl.PolylineLayer.Scheme = new List<ConfigurationLayerPolylineLayerScheme>();  
         cl.PolylineLayer.Scheme.Add(new ConfigurationLayerPolylineLayerScheme());  
         cl.PolylineLayer.Scheme[0].Name = "PolylineLayer_DEFAULT_SCHEME";  
         cl.PolylineLayer.Scheme[0].ScaleRanges =  
           defaultScaleRangesData  
           .Replace("<ScaleRanges>", "<" + typeof(ConfigurationLayerPolylineLayerSchemeScaleRanges).Name + ">")  
           .Replace("</ScaleRanges>", "</" + typeof(ConfigurationLayerPolylineLayerSchemeScaleRanges).Name + ">")  
           .XMLDeSerialize<ConfigurationLayerPolylineLayerSchemeScaleRanges>();  
         cl.PolylineLayer.Scheme[0].SchemeSelectionTheme =  
                       cl.PolylineLayer.Scheme[0].Name + "_SelectionTheme";  
         if (this.populateThemeDefination)  
         {  
           cl.PolylineLayer.ThemeDefinition = new List<ConfigurationLayerPolylineLayerPolylineTheme>();  
           if (this.addScaleRangeThemesToThemeDefination)  
           {  
             ConfigurationLayerPolylineLayerPolylineTheme defaultRangeTheme =  
               defaultPolylineThemeData  
               .Replace("<Theme", "<" + typeof(ConfigurationLayerPolylineLayerPolylineTheme).Name + "")  
               .Replace("</Theme>", "</" + typeof(ConfigurationLayerPolylineLayerPolylineTheme).Name + ">")  
               .Replace("<DisplayProperties", "<" + layerType.Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
               .XMLDeSerialize<ConfigurationLayerPolylineLayerPolylineTheme>();  
             defaultRangeTheme.Name = cl.PolylineLayer.Scheme[0].ScaleRanges.DefaultRange.DefaultType.Theme;  
             cl.PolylineLayer.ThemeDefinition.Add(defaultRangeTheme);  
             foreach (ConfigurationLayerPolylineLayerSchemeScaleRangesScaleRange scaleRange  
                     in cl.PolylineLayer.Scheme[0].ScaleRanges.ScaleRange)  
             {  
               ConfigurationLayerPolylineLayerPolylineTheme scaleRangeTheme =  
               defaultPolylineThemeData  
               .Replace("<Theme", "<" + typeof(ConfigurationLayerPolylineLayerPolylineTheme).Name + "")  
               .Replace("</Theme>", "</" + typeof(ConfigurationLayerPolylineLayerPolylineTheme).Name + ">")  
               .Replace("<DisplayProperties", "<" + layerType.Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
               .XMLDeSerialize<ConfigurationLayerPolylineLayerPolylineTheme>();  
               scaleRangeTheme.Name = scaleRange.DefaultType.Theme;  
               cl.PolylineLayer.ThemeDefinition.Add(scaleRangeTheme);  
             }  
           }  
           if (this.addSelectionThemeToThemeDefination)  
           {  
             ConfigurationLayerPolylineLayerPolylineTheme selectionTheme =  
               defaultPolylineThemeData  
               .Replace("<Theme", "<" + typeof(ConfigurationLayerPolylineLayerPolylineTheme).Name + "")  
               .Replace("</Theme>", "</" + typeof(ConfigurationLayerPolylineLayerPolylineTheme).Name + ">")  
               .Replace("<DisplayProperties", "<" + layerType.Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
               .XMLDeSerialize<ConfigurationLayerPolylineLayerPolylineTheme>();  
             selectionTheme.Name = cl.PolylineLayer.Scheme[0].SchemeSelectionTheme;  
             cl.PolylineLayer.ThemeDefinition.Add(selectionTheme);  
           }  
           cl.PolylineLayer.DirectionInfo = defaultPolylineDirectionInfoData  
               .XMLDeSerialize<ConfigurationLayerPolylineLayerDirectionInfo>();  
           cl.PolylineLayer.Routing = defaultPolylineLayerRoutingData  
               .XMLDeSerialize<ConfigurationLayerPolylineLayerRouting>();  
         }  
       }  
       else if (layerType == typeof(ConfigurationLayerPointLayer))  
       {  
         cl.PointLayer = new ConfigurationLayerPointLayer();  
         this.loadBasicInfoInTypedLayer(cl.PointLayer);  
         if (this.enableThematicScheme)  
         {  
           cl.PointLayer.ThematicScheme = new List<ConfigurationLayerPointLayerThematicScheme>();  
           cl.PointLayer.ThematicScheme.Add(new ConfigurationLayerPointLayerThematicScheme());  
           cl.PointLayer.ThematicScheme[0].Name = "PointLayer_DEFAULT_THEMATICSCHEME";  
           cl.PointLayer.ThematicScheme[0].ThematicScaleRanges =  
             defaultScaleRangesData  
             .Replace("<ThematicScaleRanges>", "<" + typeof(ConfigurationLayerPointLayerThematicSchemeThematicScaleRanges).Name + ">")  
             .Replace("</ThematicScaleRanges>", "</" + typeof(ConfigurationLayerPointLayerThematicSchemeThematicScaleRanges).Name + ">")  
             .XMLDeSerialize<ConfigurationLayerPointLayerThematicSchemeThematicScaleRanges>();  
           cl.PointLayer.ThematicScheme[0].SchemeSelectionTheme = cl.PointLayer.ThematicScheme[0].Name + "_SelectionTheme";  
         }  
         else  
         {  
           cl.PointLayer.Scheme = new List<ConfigurationLayerPointLayerScheme>();  
           cl.PointLayer.Scheme.Add(new ConfigurationLayerPointLayerScheme());  
           cl.PointLayer.Scheme[0].Name = "PointLayer_DEFAULT_SCHEME";  
           cl.PointLayer.Scheme[0].ScaleRanges =  
             defaultScaleRangesData  
             .Replace("<ScaleRanges>", "<" + typeof(ConfigurationLayerPointLayerSchemeScaleRanges).Name + ">")  
             .Replace("</ScaleRanges>", "</" + typeof(ConfigurationLayerPointLayerSchemeScaleRanges).Name + ">")  
             .XMLDeSerialize<ConfigurationLayerPointLayerSchemeScaleRanges>();  
           cl.PointLayer.Scheme[0].SchemeSelectionTheme =  
                         cl.PointLayer.Scheme[0].Name + "_SelectionTheme";  
         }  
         if (this.populateThemeDefination)  
         {  
           cl.PointLayer.ThemeDefinition = new List<ConfigurationLayerPointLayerPointTheme>();  
           if (this.addScaleRangeThemesToThemeDefination)  
           {  
             ConfigurationLayerPointLayerPointTheme defaultRangeTheme =  
               defaultPointThemeData  
               .Replace("<Theme", "<" + typeof(ConfigurationLayerPointLayerPointTheme).Name + "")  
               .Replace("</Theme>", "</" + typeof(ConfigurationLayerPointLayerPointTheme).Name + ">")  
               .Replace("<DisplayProperties", "<" + layerType.Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
               .XMLDeSerialize<ConfigurationLayerPointLayerPointTheme>();  
             defaultRangeTheme.Name = cl.PointLayer.Scheme[0].ScaleRanges.DefaultRange.DefaultType.Theme;  
             cl.PointLayer.ThemeDefinition.Add(defaultRangeTheme);  
             foreach (ConfigurationLayerPointLayerSchemeScaleRangesScaleRange scaleRange  
                     in cl.PointLayer.Scheme[0].ScaleRanges.ScaleRange)  
             {  
               ConfigurationLayerPointLayerPointTheme scaleRangeTheme =  
               defaultPointThemeData  
               .Replace("<Theme", "<" + typeof(ConfigurationLayerPointLayerPointTheme).Name + "")  
               .Replace("</Theme>", "</" + typeof(ConfigurationLayerPointLayerPointTheme).Name + ">")  
               .Replace("<DisplayProperties", "<" + layerType.Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
               .XMLDeSerialize<ConfigurationLayerPointLayerPointTheme>();  
               scaleRangeTheme.Name = scaleRange.DefaultType.Theme;  
               cl.PointLayer.ThemeDefinition.Add(scaleRangeTheme);  
             }  
           }  
           if (this.addSelectionThemeToThemeDefination)  
           {  
             ConfigurationLayerPointLayerPointTheme selectionTheme =  
               defaultPointThemeData  
               .Replace("<Theme", "<" + typeof(ConfigurationLayerPointLayerPointTheme).Name + "")  
               .Replace("</Theme>", "</" + typeof(ConfigurationLayerPointLayerPointTheme).Name + ">")  
               .Replace("<DisplayProperties", "<" + layerType.Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
               .XMLDeSerialize<ConfigurationLayerPointLayerPointTheme>();  
             if (this.enableThematicScheme)  
             {  
               selectionTheme.Name = cl.PointLayer.ThematicScheme[0].SchemeSelectionTheme;  
             }  
             else  
             {  
               selectionTheme.Name = cl.PointLayer.Scheme[0].SchemeSelectionTheme;  
             }  
             cl.PointLayer.ThemeDefinition.Add(selectionTheme);  
           }  
         }  
       }  
       return cl;  
     }    
   }  
 }  


And here is the same class after refactoring:


 namespace WebIMCP.GeoToolBox  
 {  
   class DefaultLayerBuilder  
   {  
     private DefaultLayerBuilder()  
     {  
     }  
     public static DefaultLayerBuilder Instance(string name, string filePrefix, Type layerType)  
     {  
       DefaultLayerBuilder instance = new DefaultLayerBuilder();  
       instance.name = name;  
       instance.filePrefix = filePrefix;  
       instance.layerType = layerType;  
       instance.addScaleRangeThemesToThemeDefination = true;  
       instance.addSelectionThemeToThemeDefination = true;  
       instance.populateThemeDefination = true;  
       instance.enableThematicScheme = false;  
       instance.load();  
       return instance;  
     }  
     public DefaultLayerBuilder Enable(bool val)  
     {  
       this.enabled = val;  
       return this;  
     }  
     public DefaultLayerBuilder Description(string val)  
     {  
       this.desc = val;  
       return this;  
     }  
     public ConfigurationLayer Build()  
     {  
       if (this.layerType == null)  
       {  
         throw new ArgumentNullException("layerType");  
       }   
       if(!this.LayerFunctionMap.ContainsKey(this.layerType))   
       {  
         throw new ArgumentException("layer type not found", "layerType");  
       }  
       ConfigurationLayer cl = new ConfigurationLayer();  
       string schemeName = this.layerType.Name.Replace("ConfigurationLayer", "")   
                 + (this.enableThematicScheme ? "_DEFAULT_THEMATICSCHEME" : "_DEFAULT_SCHEME");  
       dynamic typedLayer = this.LayerFunctionMap[this.layerType](schemeName);  
       PropertyInfo[] conLayerproInfoList = cl.GetType().GetProperties();  
       PropertyInfo conLayerproInfo = conLayerproInfoList.SingleOrDefault(i => i.PropertyType == this.layerType);  
       conLayerproInfo.SetValue(cl, typedLayer);  
       return cl;  
     }  
     private void load()  
     {  
       this.defaultScaleRangesData = getDefaultScaleRangesData();  
       this.defaultPointThemeData = getDefaultPointThemeData();  
       this.defaultPolylineThemeData = getDefaultPolylineThemeData();  
       this.defaultPolygonThemeData = getDefaultPolygonThemeData();  
       this.defaultPolylineLayerRoutingData = getDefaultPolylineLayerRoutingData();  
       this.defaultPolylineDirectionInfoData = getDefaultPolylineDirectionInfoData();  

       this.LayerFunctionMap = new Dictionary<Type, Func<string, dynamic>>();
       LayerFunctionMap.Add(typeof(ConfigurationLayerPolygonLayer),  
       schemeName => this.getTypedLayer<ConfigurationLayerPolygonLayer, ConfigurationLayerPolygonLayerScheme, ConfigurationLayerPolygonLayerSchemeScaleRanges,  
                   ConfigurationLayerPolygonLayerThematicScheme, ConfigurationLayerPolygonLayerThematicSchemeThematicScaleRanges,  
                   ConfigurationLayerPolygonLayerPolygonTheme>(schemeName, this.defaultPolygonThemeData));  
       LayerFunctionMap.Add(typeof(ConfigurationLayerPolylineLayer),  
       schemeName => this.getTypedLayer<ConfigurationLayerPolylineLayer, ConfigurationLayerPolylineLayerScheme, ConfigurationLayerPolylineLayerSchemeScaleRanges,  
                   ConfigurationLayerPolylineLayerPolylineTheme, ConfigurationLayerPolylineLayerDirectionInfo, ConfigurationLayerPolylineLayerRouting>  
                   (schemeName, this.defaultPolylineThemeData, this.defaultPolylineDirectionInfoData, this.defaultPolylineLayerRoutingData));  
       LayerFunctionMap.Add(typeof(ConfigurationLayerPointLayer),  
       schemeName => this.getTypedLayer<ConfigurationLayerPointLayer, ConfigurationLayerPointLayerScheme, ConfigurationLayerPointLayerSchemeScaleRanges,   
                   ConfigurationLayerPointLayerThematicScheme, ConfigurationLayerPointLayerThematicSchemeThematicScaleRanges,   
                   ConfigurationLayerPointLayerPointTheme>(schemeName, this.defaultPointThemeData));  
     }  
     private dynamic getTypedLayer<L, S, R, T, D, RT>(string schemeName, string defaultThemeData,  
                 string defaultDirectionInfoData, string defaultRoutingData)  
       where L : new()  
       where S : new()  
       where R : new()  
       where T : new()  
       where D : new()  
       where RT : new()  
     {  
       dynamic Layer = new L();  
       this.loadBasicInfoInTypedLayer(Layer);  
       Layer.Scheme = new List<S>();  
       Layer.Scheme.Add(new S());  
       Layer.Scheme[0].Name = schemeName;  
       Layer.Scheme[0].ScaleRanges =  
         defaultScaleRangesData  
         .Replace("<ScaleRanges>", "<" + typeof(R).Name + ">")  
         .Replace("</ScaleRanges>", "</" + typeof(R).Name + ">")  
         .XMLDeSerialize<R>();  
       Layer.Scheme[0].SchemeSelectionTheme = Layer.Scheme[0].Name + "_SelectionTheme";  
       if (this.populateThemeDefination)  
       {  
         Layer.ThemeDefinition = new List<T>();  
         if (this.addScaleRangeThemesToThemeDefination)  
         {  
           dynamic defaultRangeTheme =  
             defaultThemeData  
             .Replace("<Theme", "<" + typeof(T).Name + "")  
             .Replace("</Theme>", "</" + typeof(T).Name + ">")  
             .Replace("<DisplayProperties", "<" + typeof(L).Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
             .XMLDeSerialize<T>();  
           defaultRangeTheme.Name = Layer.Scheme[0].ScaleRanges.DefaultRange.DefaultType.Theme;  
           Layer.ThemeDefinition.Add(defaultRangeTheme);  
           foreach (dynamic scaleRange in Layer.Scheme[0].ScaleRanges.ScaleRange)  
           {  
             dynamic scaleRangeTheme =  
             defaultThemeData  
             .Replace("<Theme", "<" + typeof(T).Name + "")  
             .Replace("</Theme>", "</" + typeof(T).Name + ">")  
             .Replace("<DisplayProperties", "<" + typeof(L).Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
             .XMLDeSerialize<T>();  
             scaleRangeTheme.Name = scaleRange.DefaultType.Theme;  
             Layer.ThemeDefinition.Add(scaleRangeTheme);  
           }  
         }  
         if (this.addSelectionThemeToThemeDefination)  
         {  
           dynamic selectionTheme =  
           defaultThemeData  
           .Replace("<Theme", "<" + typeof(T).Name + "")  
           .Replace("</Theme>", "</" + typeof(T).Name + ">")  
           .Replace("<DisplayProperties", "<" + typeof(L).Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
           .XMLDeSerialize<T>();  
           selectionTheme.Name = Layer.Scheme[0].SchemeSelectionTheme;  
           Layer.ThemeDefinition.Add(selectionTheme);  
         }  
         Layer.DirectionInfo = defaultDirectionInfoData.XMLDeSerialize<D>();  
         Layer.Routing = defaultRoutingData.XMLDeSerialize<RT>();  
       }  
       return Layer;  
     }  
     private dynamic getTypedLayer<L, S, R, TS, TR, T>(string schemeName, string defaultThemeData)  
       where L : new()  
       where S : new()  
       where R : new()  
       where TS : new()  
       where TR : new()  
       where T : new()  
     {  
       dynamic Layer = new L();  
       this.loadBasicInfoInTypedLayer(Layer);  
       if (this.enableThematicScheme)  
       {  
         Layer.ThematicScheme = new List<TS>();  
         Layer.ThematicScheme.Add(new TS());  
         Layer.ThematicScheme[0].Name = schemeName;  
         Layer.ThematicScheme[0].ThematicScaleRanges =  
           defaultScaleRangesData  
           .Replace("<ThematicScaleRanges>", "<" + typeof(TR).Name + ">")  
           .Replace("</ThematicScaleRanges>", "</" + typeof(TR).Name + ">")  
           .XMLDeSerialize<TR>();  
         Layer.ThematicScheme[0].SchemeSelectionTheme = Layer.ThematicScheme[0].Name + "_SelectionTheme";  
       }  
       else  
       {  
         Layer.Scheme = new List<S>();  
         Layer.Scheme.Add(new S());  
         Layer.Scheme[0].Name = schemeName;  
         Layer.Scheme[0].ScaleRanges =  
           defaultScaleRangesData  
           .Replace("<ScaleRanges>", "<" + typeof(R).Name + ">")  
           .Replace("</ScaleRanges>", "</" + typeof(R).Name + ">")  
           .XMLDeSerialize<R>();  
         Layer.Scheme[0].SchemeSelectionTheme = Layer.Scheme[0].Name + "_SelectionTheme";  
       }  
       if (this.populateThemeDefination)  
       {  
         Layer.ThemeDefinition = new List<T>();  
         if (this.addScaleRangeThemesToThemeDefination)  
         {  
           dynamic defaultRangeTheme =  
             defaultThemeData  
             .Replace("<Theme", "<" + typeof(T).Name + "")  
             .Replace("</Theme>", "</" + typeof(T).Name + ">")  
             .Replace("<DisplayProperties", "<" + typeof(L).Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
             .XMLDeSerialize<T>();  
           defaultRangeTheme.Name = Layer.Scheme[0].ScaleRanges.DefaultRange.DefaultType.Theme;  
           Layer.ThemeDefinition.Add(defaultRangeTheme);  
           foreach (dynamic scaleRange in Layer.Scheme[0].ScaleRanges.ScaleRange)  
           {  
             dynamic scaleRangeTheme =  
             defaultThemeData  
             .Replace("<Theme", "<" + typeof(T).Name + "")  
             .Replace("</Theme>", "</" + typeof(T).Name + ">")  
             .Replace("<DisplayProperties", "<" + typeof(L).Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
             .XMLDeSerialize<T>();  
             scaleRangeTheme.Name = scaleRange.DefaultType.Theme;  
             Layer.ThemeDefinition.Add(scaleRangeTheme);  
           }  
         }  
         if (this.addSelectionThemeToThemeDefination)  
         {  
           dynamic selectionTheme =  
             defaultThemeData  
             .Replace("<Theme", "<" + typeof(T).Name + "")  
             .Replace("</Theme>", "</" + typeof(T).Name + ">")  
             .Replace("<DisplayProperties", "<" + layerType.Name.Replace("Configuration", "").Replace("Layer", "") + "Properties")  
             .XMLDeSerialize<T>();  
           if (this.enableThematicScheme)  
           {  
             selectionTheme.Name = Layer.ThematicScheme[0].SchemeSelectionTheme;  
           }  
           else  
           {  
             selectionTheme.Name = Layer.Scheme[0].SchemeSelectionTheme;  
           }  
           Layer.ThemeDefinition.Add(selectionTheme);  
         }  
       }  
       return Layer;  
     }  
   }  
 }  

Sunday, June 14, 2015

XML in Java: Marshal (Part-1)

XML is a tag based format to describe data. Describing data is done by two ways using Element and Attribute. XML documents must contain one root element which is parent of all other elements. Moreover, XML can be validated against a set of rules. The document that holds those rules are called DTD. A DTD is the schema or grammar of the XML document which means that the XML document's elements and attributes are declared in that DTD and follow the grammatical rules that the DTD specifies for them. Also, There is another way of setting rules for XML document which is called XSD or XML schema. Today it gains more popularity over DTD, as it itself is in XML format unlike the DTD. Furthermore, There is XSL which is more like CSS for XML and used for displaying XML data. It transform an XML document into HTML. There are also other related technologies like XPath and XML Parsers. For more information please checkout w3schools.  

XML is heavily used for data storage and transfer. Websites have large usage of XML. SOA based web services and REST services, which closely depend on XML are also increasing rapidly in number. Therefore, it's essential to developers regardless of which  platform they work in, to have a depth knowledge in XML such as xmlns for defining namespace to avoid conflict among XML Elements, XSD, DTD for xml validation, XSL for presenting, parsers like browser supported DOM parser for manipulating XML data in front end and special parser for handling large XML files.

Developers also need to know about Marshaling/Serializing to convert Object to XML, Unmarshaling/Deserializing to convert XML to Object, and usage of XSD to auto-generate Class that represents the xml. The last part is especially important while the XML itself is large.  In Java, JAXP library gives the support of different parsers and JAXB gives the XML binding facility with Marshaling and Unmarshaling support. In .Net, XML Serializing and Deserializing support are given by System.Xml.Serialization Namespace about which I'll discuss in later post. Today, our focus will be on XML binding support of Java and usage of JAXB library.

Here, I've used JDK 1.7 and netbeans 7.4 for my application. As JDK1.6 or above already include the JAXB library, we do not need to add the library in our application explicitly. You can download the source code from here.


Now, let me show how Marshaling is done by creating a simple class as example.

Student.java

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Student {
   
    private int id;
    private String firstName;
    private String lastName;
    private String email;

    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    public String getFirstName() { return firstName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public String getLastName() { return lastName; }
    public void setLastName(String lastName) { this.lastName = lastName; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}

While creating a class for marshaling we have to set @XmlRootElement annotation as every XML has a root element. Here I've used @XmlRootElement annotation to define "Student" as root element of the generated XML.

Code to marshal the above class: 

JAXBTutorial.java

    public static void main(String[] args) {
        Student student = new Student();
        student.setId(1);
        student.setFirstName("Pervez");
        student.setLastName("Sajjad");
        student.setEmail("pervez.cse@gmail.com");
        String result = marshal(student);
        System.out.println(result);
    }
   
    public static String marshal(Student student) {
        String result = null;
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            JAXBContext jaxbContext = JAXBContext.newInstance(Student.class);
            Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
            jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); // pretty output
            jaxbMarshaller.marshal(student, out);
            result = out.toString();
        } catch (JAXBException e) {
            e.printStackTrace();
        }
        return result;
    }

You will find JAXBContext, Marshaller classes under javax.xml.bind package.
The output after the marshaling is :

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<student>
    <email>pervez.cse@gmail.com</email>
    <firstName>Pervez</firstName>
    <id>1</id>
    <lastName>Sajjad</lastName>
</student>

XML Marshaling and Annotations

Some common Annotations that are available while Marshaling are:
  • @XmlAttribute: This member will be marshaled as an XML attribute.
  • @XmlElement: The field will be marshaled as an XML element.
  • @XmlTransient: Field will be ignored while marshaling.
  • @XmlType to indicate special options like appearance order of the sibling elements in the XML. 

@XmlElement:
If we require different xml tag name from the instance variable name we can introduce the @XmlElement annotation to it in the class structure.

@XmlRootElement
public class Student {
   
    private int id;
    private String firstName;
    private String lastName;
    private String email;

    public int getId() { return id; }
   
    @XmlElement(name = "given name")
    public String getFirstName() { return firstName; }
   
    @XmlElement(name = "surname")
    public String getLastName() { return lastName; }
    public String getEmail() { return email; }
   
    public void setId(int id) { this.id = id; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public void setLastName(String lastName) { this.lastName = lastName; }
    public void setEmail(String email) { this.email = email; }
}

@XmlElement(name = "given name") specifies that the instance variable firstName will be marshaled with the tag name "given name" in the XML. It help us to map between the XML tag name and the instance variable name. Like this you can also change the name of XML attribute and XML root element.

The produced XML output with the custom tag name is:

<student>
    <email>pervez.cse@gmail.com</email>
    <given name>Pervez</given name>
    <id>1</id>
    <surname>Sajjad</surname>
</student>

@XmlAttribute:
If we want that the instance variable id should occur as the attribute for the tag student then we should use @XmlAttribute. @XmlAttribute serializes the instance variable as the attribute for the parent tag.
The following code illustrates the functionality:

@XmlRootElement
public class Student {
   
    private int id;
    private String firstName;
    private String lastName;
    private String email;

    @XmlAttribute(name = "identification")
    public int getId() { return id; }
    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }
    public String getEmail() { return email; }
   
    public void setId(int id) { this.id = id; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public void setLastName(String lastName) { this.lastName = lastName; }
    public void setEmail(String email) { this.email = email; }
}

The produced XML output for the code will be:

<student identification="1">
    <email>pervez.cse@gmail.com</email>
    <firstName>Pervez</firstName>
    <lastName>Sajjad</lastName>
</student>

Since the getId() method is annotated by @XmlAttribute(name = "identification") therefore the instance variable id is included as an attribute named "identification" for the parent tag student.

@XmlTransient:
By default, all instance variables with public read/write access are marshalled by the Marshaller . That is, the value of each publicly accessible variable is persisted as an XML element or XML attribute in an XML-document instance. In order to override this property apply @XmlTransient annotation to it. This will remove the element from the XML.
The code below explains the following:

@XmlRootElement
public class Student {
   
    private int id;
    private String firstName;
    private String lastName;
    private String email;

    public int getId() { return id; }
   
    @XmlTransient
    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }
    public String getEmail() { return email; }
   
    public void setId(int id) { this.id = id; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public void setLastName(String lastName) { this.lastName = lastName; }
    public void setEmail(String email) { this.email = email; }
}

Here we can see that the getFirstName() method is annotated by @XmlTransient. The produced XML output will not contain the firstName tag in it.

<student>
    <email>pervez.cse@gmail.com</email>
    <id>1</id>
    <lastName>Sajjad</lastName>
</student>


@XmlType:
By using @XmlType annotation we can define the appearance order of the sibling elements in the XML.
The code below explains the following:

@XmlRootElement
@XmlType(propOrder = {"id", "firstName", "lastName", "email"})
public class Student {
   
    private int id;
    private String firstName;
    private String lastName;
    private String email;

    public int getId() { return id; }
    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }
    public String getEmail() { return email; }
   
    public void setId(int id) { this.id = id; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public void setLastName(String lastName) { this.lastName = lastName; }
    public void setEmail(String email) { this.email = email; }
}

The produced XML output of the code is as follows:

<student>
    <id>1</id>
    <firstName>Pervez</firstName>
    <lastName>Sajjad</lastName>
    <email>pervez.cse@gmail.com</email>
</student>

Here we see the elements are appeared as it is defined by the @XmlType annotation.


Marshaling of a class with instance variable of other class object

If we have a class structure such that a class contains an object of other class and we want to include that class object also for serialization.
Let's see the following example :

@XmlRootElement
public class Student {
   
    private int id;
    private String firstName;
    private String lastName;
    private String email;
    private Department department;

    public int getId() { return id; }
    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }
    public String getEmail() { return email; }
    public Department getDepartment() { return department; }   
    public void setId(int id) { this.id = id; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public void setLastName(String lastName) { this.lastName = lastName; }
    public void setEmail(String email) { this.email = email; }
    public void setDepartment(Department department) { this.department = department; }
}

public class Department {
   
    private int id;
    private String name;
    private String school;

    public int getId() { return id; }
    public String getName() { return name; }
    public String getSchool() { return school; }
    public void setName(String name) { this.name = name; }
    public void setId(int id) { this.id = id; }
    public void setSchool(String school) { this.school = school; }
}

This is how the Student class will be serialized:

<student>
    <department>
        <id>1</id>
        <name>cse</name>
        <school>Applied Science</school>
    </department>
    <email>pervez.cse@gmail.com</email>
    <firstName>Pervez</firstName>
    <id>1</id>
    <lastName>Sajjad</lastName>
</student>

Now, consider the following XML. It is a bit complex compared to the the previous one. Please check the department elements of both XMLs.

<student>
    <department id="1" school="Applied Science">cse</department>
    <email>pervez.cse@gmail.com</email>
    <firstName>Pervez</firstName>
    <id>1</id>
    <lastName>Sajjad</lastName>
</student>


How can we generate this type of XML? The following code is the answer which can generate this XML.

public class Department {
   
    private int id;
    private String name;
    private String school;

    @XmlAttribute
    public int getId() { return id; }
    @XmlValue
    public String getName() { return name; }
    @XmlAttribute
    public String getSchool() { return school; }
   

    public void setName(String name) { this.name = name; }
    public void setId(int id) { this.id = id; }
    public void setSchool(String school) { this.school = school; }
}


There is no change in Student class. However, in Department class we've used @XmlValue to add the department name as the InnerText to the tag "department". One element can have only one InnerText.

Marshaling of a class containing collection of objects

To marshal List like collection we need two annotations which are: @XmlElementWrapper and @XmlElement. @XmlElementWrapper is used to define the tag-name of the list in XML. On the other hand, @XmlElement is used to define the tag-name of the individual members in that list.
Let's check the code:

@XmlRootElement
public class Student {
  
    private int id;
    private String firstName;
    private String lastName;
    private String email;
    private List<Course> courseList;

    public Student() {
        this.courseList = new ArrayList<>();
    }

    public int getId() { return id; }
    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }
    public String getEmail() { return email; }


    @XmlElementWrapper(name = "course-list")
    @XmlElement(name = "course")
    public List<Course> getCourseList() { return courseList; }
  
    public void setId(int id) { this.id = id; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public void setLastName(String lastName) { this.lastName = lastName; }
    public void setEmail(String email) { this.email = email; }   

    public void setCourseList(List<Course> courseList) { this.courseList = courseList; }
}


public class Course {
    private int id;
    private String name;
    private double credit;

    public int getId() { return id; }
    public String getName() { return name; }
    public double getCredit() { return credit; }
    public void setName(String name) { this.name = name; }   
    public void setId(int id) { this.id = id; }
    public void setCredit(double credit) { this.credit = credit; }
}


 The generated XML output is as follows:

 <student>
    <course-list>
        <course>
            <credit>3.0</credit>
            <id>1</id>
            <name>CSE-101</name>
        </course>
        <course>
            <credit>4.0</credit>
            <id>2</id>
            <name>CSE-102</name>
        </course>
    </course-list>
    <email>pervez.cse@gmail.com</email>
    <firstName>Pervez</firstName>
    <id>1</id>
    <lastName>Sajjad</lastName>
</student>


XML Unmarshal

In the 2nd part we'll discuss how to unmarshal XML to Object. Please keep in touch

Conclusion

 JAXB provides us a very effective way to convert XML to object and vice versa. Hence, it is used in other api extensively for XML binding support. One of them is JAX-WS library which facilitates SOAP based web service support and is part of the JEE stack.

Useful Links

1. http://www.codeproject.com/Articles/483055/XML-Serialization-and-Deserialization-Part-1
2. http://www.javacodegeeks.com/2014/12/jaxb-tutorial-xml-binding.html




  

Monday, August 19, 2013

BB10 / Qt : How to serialize any user defined QObject class to QByteArray using QDataStream

Today I am going to write about how to serialize any QObject class and then write the object to a byte array and desalinize it back from the byte array. This is crucial if you want to send any object over the net or save it in a file or database.

First, we need a base class which make all of it's subclass serializable.

SerializableClass.h
#ifndef SerializableClass_H_
#define SerializableClass_H_
#include <qt4/QtCore/qobject.h>
#include <qt4/QtCore/qdatastream.h>
class SerializableClass : public QObject
{
    Q_OBJECT
public:
    explicit SerializableClass(QObject *parent = 0);

};
QDataStream &operator<<(QDataStream &ds, const SerializableClass &obj);
QDataStream &operator>>(QDataStream &ds, SerializableClass &obj) ;
#endif /* SerializableClass_H_ */
SerializableClass.cpp
#include "SerializableClass.h"
#include <qt4/Qt/qvariant.h>
#include <qt4/QtCore/qmetaobject.h>
SerializableClass::SerializableClass(QObject *parent) :
    QObject(parent)
{
}
QDataStream &operator<<(QDataStream &ds, const SerializableClass &obj) {
    for(int i=0; i<obj.metaObject()->propertyCount(); ++i)
    {
        if(obj.metaObject()->property(i).isStored(&obj))
        {
            ds << obj.metaObject()->property(i).read(&obj);
        }
    }
    return ds;
}
QDataStream &operator>>(QDataStream &ds, SerializableClass &obj) {
    QVariant var;
    for(int i=0; i<obj.metaObject()->propertyCount(); ++i)
    {
        if(obj.metaObject()->property(i).isStored(&obj))
        {
            ds >> var;
            obj.metaObject()->property(i).write(&obj, var);
        }
    }
    return ds;
}

Now, you need to extend this class to make your class serializable. Let's see as example.

Student.h
#ifndef STUDENT_H_
#define STUDENT_H_
#include "SerializableClass.h" 
class Student : public SerializableClass
{
    Q_OBJECT
    Q_PROPERTY(int id READ getId WRITE setId)
    Q_PROPERTY(QString Name READ getName WRITE setName)
public:
    Student();
    int getId() const { return id; }
    void setId(int newId) { id = newId; }
    QString getName() const { return Name; }
    void setName(const QString &newName) { Name = newName; }
private:
    int id;
    QString Name;
}; 
#endif /* STUDENT_H_ */

Student.cpp
#include "Student.h"
Student::Student()
{

Here Q_OBJECT, Q_PROPERTY macros are required to take care of generating proper metaObject for your class. Now we are going to put these classes in action at main() function.

main.cpp
Q_DECL_EXPORT int main(int argc, char **argv)
{
    Student G;
    Student G2;
   G.setId(30);
   G.setName("pervez");
   qDebug()<<G.getId()<<G.getName();
   QByteArray byteArray;
   QDataStream out(&byteArray, QIODevice::WriteOnly);
   out <<G;
   qDebug()<<G2.getId()<<G2.getName();
   QDataStream in(byteArray);
   in >> G2;
   qDebug()<<G2.getId()<<G2.getName();

   Application app(argc, argv);
   new ApplicationUI(&app);
   return Application::exec();
}

Here QDataStream out writes the G object in byteArray at "out <<G;" statement. that's why we use >> and << operator in SerializableClass.h header file


Wednesday, June 26, 2013

Android : Overview on WebRTC android Client with peer-to-peer support (libjingle)

WebRTC code, located in https://webrtc.googlecode.com/svn/trunk, contains only the video and audio codec, the RTP stack. On the other hand, the libjingle project has the stacks of XMPP, STUN and ICE implementation. It also uses the API of WebRTC. Libjingle project bundle has a android ndk project AppRTCDemo which can be used as application (.apk) on android device. Unlike WebRTCDemo (android project from WebRTC), this AppRTCDemo android project has libjingle's Peer-Connection module attached to it. So this android app can communicate with each other from different networks via p2p connection. To initiate p2p connection with other client, it communicate with http://apprtc.appspot.com server using Google App Engine Channel API. 
If you want to write your own server you may try node.js and replace the google's Channel 
api code with your own signaling code in AppRTCDemo. The AppRTCDemo can be built on ubuntu by following the instructions on this link. Make sure JAVA_HOME is properly configured in the class-path. It also provides the info on how to install the app on android device.

Sunday, April 21, 2013

Android : How to build latest WebRTC revision for Android (on Linux) and run on device

WebRTC is a free, open project that enables web browsers with Real-Time Communications (RTC) capabilities via simple Javascript APIs. It also has a android ndk project WebRTCDemo which can be used as application (.apk) on android device. But one thing I should mention that this demo client does not support peer-connection feature. So it can not connect with http://apprtc.appspot.com. If you need 
an android client that can connect http://apprtc.appspot.com then you need to check out 
libjingle project. I am going to post on this issue in near future.

The WebRTCDemo can be built on ubuntu if you go through the following steps (use terminal to execute command):

  • Download the latest Android SDK and NDK. edit ~/.bashrc to add the paths accordingly. 
export ANDROID_SDK="<path to your Android SDK directory>"
export ANDROID_NDK="<path to your Android NDK directory>"
export PATH="$PATH:$ANDROID_SDK/tools:$ANDROID_SDK/platform-tools:$ANDROID_NDK"
  • Download depottools and edit ~/.bashrc to add path.
  • source ~/.bashrc
  • install (sudo apt-get install...):
      g++ (>= 4.2)
      python (>= 2.4)
      libasound2-dev
      libpulse-dev
      libjpeg62-dev
      libxv-dev
      libgtk2.0-dev
      libexpat1-dev

    For 32-bit builds on a 64-bit system:
      lib32asound2-dev
      ia32-libs
  • create a folder where you can download source code from trunk and go to the folder from terminal.
  • gclient config https://webrtc.googlecode.com/svn/trunk
  • vim .gclient 
  • add this line ";target_os = ["android", "unix"]" to .gclient file.
  • gclient sync
  • cd trunk
  • source ./build/android/envsetup.sh
  • gclient runhooks 
  • sudo ./build/install-build-deps-android.sh
  • vim ./webrtc/video_engine/test/android/build_demo.py
  • [replace return 'source %s && %s' % (_ANDROID_ENV_SCRIPT, cmd) with return '/bin/bash -c "source %s" && %s' % (_ANDROID_ENV_SCRIPT, cmd)]
  • ninja -C out/Debug
  • You will find your build file (.apk file) in ./webrtc/video_engine/test/android/bin folder.
Here are the steps to run the application on android device:
  • run apk on both android devices.
  • configure remote ip address in settings tab on both android devices.(Note: as it does not support peer-connection so these devices need to know each other directly without any server interaction. So devices need to be in same network)
  • press StartCall button in Main tab on both android devices.