Skip to Content

Welcome to part three on creating data access extensions (DAE) for Lumira Desktop. As mentioned in my first blog the DAE takes input in the form of command line arguments and outputs both a data structure and a data block.

In my second blog I showed code that creates valid output to Lumira to demonstrate how to pass data to Lumira.

For this blog I am going to handle the input parameters and add utility functions to improve code reuse for future blogs.

Please note I am aware of formatting issues and possible wording issues which I am working on. I have decided that it was more important to get this code out sooner than later. I will do my best to fix any formatting and grammar mistakes later. Some issues have been words from one sentence showing up in a previous line in a random spot… not sure why. So please be gentle in feedback! 😛

So what are the input parameters we have to handle?

Lumira passes information to the DAE through command line arguments. The current arguments we need to handle are: mode, locale, size and params. Please see my first blog for more detailed information on them.

A quick note on the code.

The majority of the code is to create functions to handle the input and output as a Name-Value Pair structure. (http://en.wikipedia.org/wiki/Attribute–value_pair). This code adds more complexity now but vastly simplifies future work.

Let’s get to the code!

1. We are going to start with the DAESHELL sample we created in part two. If you like you can download my copy from the previous blog.

2. At the top of the project, add these two lines to the “using” section

using System.Collections;
using System.Collections.Specialized;

3. Under the Main function add these two new function to convert the command line parameters into name-value pairs:

static void ConvertCmdLineArgsToNVPairs (string [] args, ref NameValueCollection dsInfo)
{
     // args will be -argName1 argValue1 -argName2 argValue2
     // We want to put the name and the value into the name value pair
     //except for the arg -params which we need to handle seperately

     // TO DO: What if the Name Value Pairs already exist?

     for (int i = 0; i < args.Length; i++)
     {
           // check to ensure it is not the params block
          if (args[i] != “-params”)
          {
               // Add the current item without the – as name, increment to next item and add as value
               dsInfo.Add(args[i].TrimStart(‘-‘), args[++i]);
           }
          else
          {
               // get the params block and convert to name value pairs
               ConvertParamsToNVPairs(args[++i], ref dsInfo);
           }
      }
}

static void ConvertParamsToNVPairs(string Params, ref NameValueCollection dsInfo)
{
     // params are in the form of semi-colon delimited string like this:
     // argName1=argValue1;argName2=argValue2

     // TO DO: What if the Name Valu Pairs already exist?

     //First we split the string by semi colons
     string [] splitParams = Params.Split(‘;’);
      foreach (string param in splitParams)
      {
          // Param is now in the form of argName=argValue so
          // we need to split it on ‘=’ and add to dsInfo
          string[] nvPair = param.Split(new Char[] { ‘=’},2);
               
          // Ensure that this did split into two items (
          if (nvPair.Count () == 2)
               dsInfo.Add(nvPair[0], nvPair[1]);
     }
}
}

4. Now we add three more functions to write the DSInfo block from the Name-Value paris and sets default values.

static bool ContainsKey(NameValueCollection collection, string key)
{
     // Function to check if key exists or not.

     // Obtained from StackOverFlow.com

     if (collection.Get(key) == null)
     {
          return collection.AllKeys.Contains(key);
     }

     return true;
}

static void SetBasicDSInfoDefaultValues(ref NameValueCollection dsInfo)
{
     // If mode is preview default values should be blank
     // but best not to take a chance so we will look to see
     // if default values exist and if not assign them.
     // Values are hard coded at this point.
           
     // TO DO: Read defaults from config file and check
     //        keys for null values

     if (!ContainsKey (dsInfo, “csv_separator”))
     {
          dsInfo.Add (“csv_separator”, “,”);
     }
     else if (!ContainsKey (dsInfo, “csv_date_format”))
     {
          dsInfo.Add (“csv_date_format”, “yyyy-m-d”);
     }
     else if (!ContainsKey (dsInfo, “csv_number_grouping”))
     {
          dsInfo.Add (“csv_number_grouping”, “,”);
      }
     else if (!ContainsKey (dsInfo, “csv_number_decimal”))
     {
          dsInfo.Add (“csv_number_decimal”, “.”);
     }
     else if (!ContainsKey (dsInfo, “csv_separator”))
     {
          dsInfo.Add (“csv_first_row_has_column_names”, “true”);
     }
}

static void WriteDSInfo(NameValueCollection dsInfo)
{
     // Keys we do not want to write out
     string [] keysNotToInclude =  {“mode”, “size”, “locale”};
           
     // Write the DSInfo Block Describing the data.
     Console.WriteLine(“beginDSInfo”);
     foreach (string key in dsInfo)
     {
          // check to see if this is one of the items not to include
          if (! keysNotToInclude.Contains (key))
               Console.WriteLine(key + “;” + dsInfo[key] + “;true”);
     }
     Console.WriteLine(“endDSInfo”);
}

5. Finally let’s update the main function to put this all together by updating the main function to contain this code:

static void Main(string[] args)
{
     // Instantiate Namve Value collection to hold dsInfo
     // and all other parameters being sent to and from Lumira.
     NameValueCollection dsInfo = new NameValueCollection ();
           
     // Convert the command line arguments to Name Value Pairs
     ConvertCmdLineArgsToNVPairs(args, ref dsInfo);

     // Now we set the minimum required default values in DSInfo
     // This function does not do anything if values are set
     // in ConvertCmdLineArgs
     SetBasicDSInfoDefaultValues(ref dsInfo);

     // Now we handle the mode command parameter
     // so we are performing the function required
     // by Lumira.
     //
     // We are still working with hard coded data
     // so we will return a single line of data for
     // preview and edit modes, and three lines for
     // refresh mode.

           
     if (dsInfo[“mode”] == “preview”)
     {
          // First we start with the header:
          Console.WriteLine(“beginData”);
          Console.WriteLine(“Name, Age, Date”);

          // Now the data
          Console.WriteLine(“Preview Data, 24, 2024-10-31”);
               
          // Finally signal end of data block
          Console.WriteLine(“endData”);
     }
     else if (dsInfo[“mode”] == “edit”)
     {
          // First we start with the header:
          Console.WriteLine(“beginData”);
          Console.WriteLine(“Name, Age, Date”);

     

          // Now the data
          Console.WriteLine(“Edit Data, 24, 2024-10-31”);
               
          // Finally signal end of data block
          Console.WriteLine(“endData”);
     }
     else if (dsInfo[“mode”] == “refresh”)
     {
          // As this is a basic extension we are writing only two rows of static values. Name, Age and Data
          // First we start with the header:
          Console.WriteLine(“beginData”);
          Console.WriteLine(“Name, Age, Date”);

     

          // Now the data
          Console.WriteLine(“Refresh Data, 42, 2124-01-12”);
          Console.WriteLine(“Robyn, 24, 2024-10-31”);
          Console.WriteLine(“Owen, 30, 2010-12-25”);

     

          // Finally signal end of data block
          Console.WriteLine(“endData”);
     }
}

6. Finally copy this DAE to its working directory, for example C:\Program Files\SAP Lumira\Desktop\daextensions\DAEShell  and try it in Lumira. See steps 3+ in part two if you are unsure. You will see the data will contain at least one row that tells you if a preview, refresh or edit mode was handled.

My notes on this part:

I realize I could have simplified the code by using strongly named variables as opposed to the Name Value Pair collection much like the official documentation does. This may have also made it easier to follow. My reasons with going with the slightly more complicated code are:

  1. When it becomes time to customize this for different database types, the Name-Value collection will allow setting and reading custom values with minimal code.
  2. When future versions of Lumira add more parameters they will be automatically populated in the Name-Value pairs. If these parameters are required to be sent back to Lumira this code does it automatically. This enables the Extension to be future compatible.
  3. From now on, most of this code should never have to be touched to add new functionality. In the next part the if-then-else statement that handles the mode function will be modified, and once modified program.cs could forever remain static. Any new code we will add in a new file.*
  4. Finally I hope that by adding ample comments throughout it should help clarify the code.

*The important word is could. Exception and error handling isn’t currently present in the main code sequence and a few other to-do items.

So what’s next?

The next part is exciting because we will be adding code to read data from a CSV data source! Yes I know Lumira has CSV support already. But I am going for simplicity. And it will pave the way for some cool features for part four!

ℹ Project is attached, just unzip the file, rename it from .txt to .zip and unzip it again!

The Other Parts:

Part 1  My Personal guide to SAP Lumira Data Access Extensions.Data Access Extensions Part Two: The Simplest Lumira Data Access Extension possible

Part 2  Data Access Extensions Part Two: The Simplest Lumira Data Access Extension possible

Part 4 Coming soon: What could it be? Hint let’s add a CSV file…

To report this post you need to login first.

Be the first to leave a comment

You must be Logged on to comment or reply to a post.

Leave a Reply