Skip to content

Writing case based conditional mappings

ldfallas edited this page Aug 17, 2015 · 2 revisions

There are situations when the conversion of an APIs depend on the arguments or the way the member is invoked.

Conditional mappings allows the deconstruction of expressions.

In this document we will try to migrate some uses of the IsolatedStorageFile.OpenFile method. This method can be used to open a file for reading or writing with several options. The equivalent UWP API we want to convert to is the OpenStreamForWriteAsync and OpenStreamForReadAsyncIStorageFile extension methods . As you can see we have at least two cases to address here the case when 'OpenFile' is called for reading a file or when is opened for writing.

For example:

Given the following code:

-- Windows Phone Silverlight --

fileStream = myIsolatedStorage.OpenFile("myFile.txt", FileMode.Open, FileAccess.Write);

We want to convert it to:

-- Windows UWP --

fileStream = await myIsolatedStorage.OpenStreamForWriteAsync("myFile.txt", Windows.Storage.CreationCollisionOption.ReplaceExisting);

Also we will also want to convert from:

-- Windows Phone Silverlight --

fileStream = myIsolatedStorage.OpenFile("myFile.txt", FileMode.Open, FileAccess.Read);

We want to convert it to:

-- Windows UWP --

fileStream = await myIsolatedStorage.OpenStreamForReadAsync("myFile.txt");

Conversion

A special kind of code mapping action is available of these scenarios. This code mapping action is called "Conditional" and its functionality is similar to a 'switch' statement is C#. The shape this mapper is simmilar to this:

-- UWP Conversion tool code mapping --

<map:Conditional>
   <!-- Case #1 -->
   <map:Case>

      <map:Case.Condition>
          ...
      </map:Case.Condition>
      <map:Case.Action>
         ...
      </map:Case.Action>
   </map:Case>
   <!-- Case #2 -->
   <map:Case>
      <map:Case.Condition>
          ...
      </map:Case.Condition>
      <map:Case.Action>
         ...
      </map:Case.Action>
   </map:Case>

   ...

   <!-- Default action  -->
   <map:Default>
      ...
   </map:Default>
</map:Conditional>

Here a conditional mapping allows us to write different actions for different conditions. When first condition succeeds the action is applied and the execution of the mapping operation is resumed. If no condition applies the action inside the Default element will be executed.

Execution by example #1

This section presents the execution of the conditional mapping against a mapping occurrence.

In this case we want to show the execution of the mapping of the following expression:

-- Windows Phone Silverlight --

f = isoStore.OpenFile("file.txt", FileMode.Open, FileAccess.Write);

The mapping we want to apply is defined by the following code:

-- UWP Converter code mapping --

...
<map:CodeMap Kind="Call" MemberName="OpenFile">
   <map:Conditional>
      <map:Case>
         <map:Case.Condition>
            <map:WithMethodCall>
               <map:ArgumentCount>3</map:ArgumentCount>
               <map:WithArgument Position="0">
                  <map:AssignName>$fileNameExpression</map:AssignName>
               </map:WithArgument>
               <map:WithArgument Position="1">
                  <map:Equals>FileMode.Open</map:Equals>
               </map:WithArgument>
               <map:WithArgument Position="2">
                  <map:Equals>FileAccess.Read</map:Equals>
               </map:WithArgument>
               <map:WithCalledMethodExpression>
                  <map:WithLeftSideOfDottedAccess>
                     <map:AssignName>$folderReference</map:AssignName>
                  </map:WithLeftSideOfDottedAccess>
               </map:WithCalledMethodExpression>
            </map:WithMethodCall>
         </map:Case.Condition>
         <map:Case.Action>
            <map:ReplaceWithTemplate>
               await $folderReference.OpenStreamForReadAsync($fileNameExpression)
            </map:ReplaceWithTemplate>
         </map:Case.Action>
      </map:Case>
      <map:Case>
         <map:Case.Condition>
            <map:WithMethodCall>
               <map:ArgumentCount>3</map:ArgumentCount>
               <map:WithArgument Position="0">
                  <map:AssignName>$fileNameExpression</map:AssignName>
               </map:WithArgument>
               <map:WithArgument Position="1">
                  <map:AnyConditionApplies>
                     <map:Equals>FileMode.Truncate</map:Equals>
                     <map:Equals>FileMode.Open</map:Equals>
                  </map:AnyConditionApplies>
               </map:WithArgument>
               <map:WithArgument Position="2">
                  <map:Equals>FileAccess.Write</map:Equals>
               </map:WithArgument>
               <map:WithCalledMethodExpression>
                  <map:WithLeftSideOfDottedAccess>
                     <map:AssignName>$folderReference</map:AssignName>
                  </map:WithLeftSideOfDottedAccess>
               </map:WithCalledMethodExpression>
            </map:WithMethodCall>
         </map:Case.Condition>
         <map:Case.Action>
            <map:ReplaceWithTemplate>
               await $folderReference.OpenStreamForWriteAsync($fileNameExpression, Windows.Storage.CreationCollisionOption.ReplaceExisting)
            </map:ReplaceWithTemplate>
         </map:Case.Action>
      </map:Case>
      <map:Default>
         <map:Keep/>
      </map:Default>
   </map:Conditional>
</map:CodeMap>
...

Step #1

The first step starts when the mappings mechanism determined that a mapping for the OpenFile method should be applied.

The following code highlights the expression being inspected:

-- Windows Phone Silverlight --

f = isoStore.OpenFile("file.txt", FileMode.Open, FileAccess.Write);
    |____________________________________________________________|

Since we are starting the application of the conditional mapping action, the first case is the current and the first part of the condition is being tested.

-- UWP Converter code mapping --

...
<map:CodeMap Kind="Call" MemberName="OpenFile">
   <map:Conditional>
      <map:Case>
         <map:Case.Condition>
|---------------------------------------------------------------------|
           <map:WithMethodCall>
               <map:ArgumentCount>3</map:ArgumentCount>
               <map:WithArgument Position="0">
                  <map:AssignName>$fileNameExpression</map:AssignName>
               </map:WithArgument>
               <map:WithArgument Position="1">
                  <map:Equals>FileMode.Open</map:Equals>
               </map:WithArgument>
               <map:WithArgument Position="2">
                  <map:Equals>FileAccess.Read</map:Equals>
               </map:WithArgument>
               <map:WithCalledMethodExpression>
                  <map:WithLeftSideOfDottedAccess>
                     <map:AssignName>$folderReference</map:AssignName>
                  </map:WithLeftSideOfDottedAccess>
               </map:WithCalledMethodExpression>
            </map:WithMethodCall>
|---------------------------------------------------------------------|
         </map:Case.Condition>
         <map:Case.Action>
            <map:ReplaceWithTemplate>
               await $folderReference.OpenStreamForReadAsync($fileNameExpression)
            </map:ReplaceWithTemplate>
         </map:Case.Action>
      </map:Case>
      <map:Case>
         ...
      </map:Case>
      <map:Default>
         ...
      </map:Default>
   </map:Conditional>
</map:CodeMap>
...

Since the current expression is a method call the WithMethodCall partially proceeds to test inner predicates.


Step #2

In this step the current expression being tested is the same:

-- Windows Phone Silverlight --

f =  isoStore.OpenFile("file.txt", FileMode.Open, FileAccess.Write);
     |____________________________________________________________|

Now the ArgumentCount condition is being tested. In this case it must verify that the argument count is 3.

-- UWP Converter code mapping --

...
<map:CodeMap Kind="Call" MemberName="OpenFile">
   <map:Conditional>
      <map:Case>
         <map:Case.Condition>
            <map:WithMethodCall>
|---------------------------------------------------------------------|
               <map:ArgumentCount>3</map:ArgumentCount>
|---------------------------------------------------------------------|
               <map:WithArgument Position="0">
                  <map:AssignName>$fileNameExpression</map:AssignName>
               </map:WithArgument>
               <map:WithArgument Position="1">
                  <map:Equals>FileMode.Open</map:Equals>
               </map:WithArgument>
               <map:WithArgument Position="2">
                  <map:Equals>FileAccess.Read</map:Equals>
               </map:WithArgument>
               <map:WithCalledMethodExpression>
                  <map:WithLeftSideOfDottedAccess>
                     <map:AssignName>$folderReference</map:AssignName>
                  </map:WithLeftSideOfDottedAccess>
               </map:WithCalledMethodExpression>
            </map:Case.Condition>
         <map:Case.Action>
            <map:ReplaceWithTemplate>
               await $folderReference.OpenStreamForReadAsync($fileNameExpression)
            </map:ReplaceWithTemplate>
         </map:Case.Action>
      </map:Case>
      <map:Case>
         ...
      </map:Case>
      <map:Default>
         ...
      </map:Default>
   </map:Conditional>
</map:CodeMap>
...

Step #3

In this step the current expression being tested is the first argument:

-- Windows Phone Silverlight --

f = isoStore.OpenFile("file.txt", FileMode.Open, FileAccess.Write);
                      |________|

Now the ArgumentCount condition is being tested. In this case it must verify that the argument count is 3.

-- UWP Converter code mapping --

...
<map:CodeMap Kind="Call" MemberName="OpenFile">
   <map:Conditional>
      <map:Case>
         <map:Case.Condition>
            <map:WithMethodCall>
               <map:ArgumentCount>3</map:ArgumentCount>
|---------------------------------------------------------------------|
               <map:WithArgument Position="0">
                  <map:AssignName>$fileNameExpression</map:AssignName>
               </map:WithArgument>
|---------------------------------------------------------------------|
               <map:WithArgument Position="1">
                  <map:Equals>FileMode.Open</map:Equals>
               </map:WithArgument>
               <map:WithArgument Position="2">
                  <map:Equals>FileAccess.Read</map:Equals>
               </map:WithArgument>
               <map:WithCalledMethodExpression>
                  <map:WithLeftSideOfDottedAccess>
                     <map:AssignName>$folderReference</map:AssignName>
                  </map:WithLeftSideOfDottedAccess>
               </map:WithCalledMethodExpression>
            </map:Case.Condition>
         <map:Case.Action>
            <map:ReplaceWithTemplate>
               await $folderReference.OpenStreamForReadAsync($fileNameExpression)
            </map:ReplaceWithTemplate>
         </map:Case.Action>
      </map:Case>
      <map:Case>
         ...
      </map:Case>
      <map:Default>
         ...
      </map:Default>
   </map:Conditional>
</map:CodeMap>
...

In this case the condition also succeeds since we are only capturing the value of the first argument.


Step #4

In this step the current expression being tested is the second argument:

-- Windows Phone Silverlight --

f = isoStore.OpenFile("file.txt",FileMode.Open, FileAccess.Write);
                                 |____________|

The following condition verifies that the argument value is File.Open .

-- UWP Converter code mapping --

...
<map:CodeMap Kind="Call" MemberName="OpenFile">
   <map:Conditional>
      <map:Case>
         <map:Case.Condition>
            <map:WithMethodCall>
               <map:ArgumentCount>3</map:ArgumentCount>
               <map:WithArgument Position="0">
                  <map:AssignName>$fileNameExpression</map:AssignName>
               </map:WithArgument>
|---------------------------------------------------------------------|
               <map:WithArgument Position="1">
                  <map:Equals>FileMode.Open</map:Equals>
               </map:WithArgument>
|---------------------------------------------------------------------|
               <map:WithArgument Position="2">
                  <map:Equals>FileAccess.Read</map:Equals>
               </map:WithArgument>
               <map:WithCalledMethodExpression>
                  <map:WithLeftSideOfDottedAccess>
                     <map:AssignName>$folderReference</map:AssignName>
                  </map:WithLeftSideOfDottedAccess>
               </map:WithCalledMethodExpression>
            </map:Case.Condition>
         <map:Case.Action>
            <map:ReplaceWithTemplate>
               await $folderReference.OpenStreamForReadAsync($fileNameExpression)
            </map:ReplaceWithTemplate>
         </map:Case.Action>
      </map:Case>
      <map:Case>
         ...
      </map:Case>
      <map:Default>
         ...
      </map:Default>
   </map:Conditional>
</map:CodeMap>
...

At this point the condition also succeeds since the value of the second argument is File.Open .


Step #5

In this step the current expression being tested is the third argument:

-- Windows Phone Silverlight --

f = isoStore.OpenFile("file.txt",FileMode.Open, FileAccess.Write);
                                                |______________|

The following condition verifies that the argument value is File.Open .

-- UWP Converter code mapping --

...
<map:CodeMap Kind="Call" MemberName="OpenFile">
   <map:Conditional>
      <map:Case>
         <map:Case.Condition>
            <map:WithMethodCall>
               <map:ArgumentCount>3</map:ArgumentCount>
               <map:WithArgument Position="0">
                  <map:AssignName>$fileNameExpression</map:AssignName>
               </map:WithArgument>
               <map:WithArgument Position="1">
                  <map:Equals>FileMode.Open</map:Equals>
               </map:WithArgument>
|---------------------------------------------------------------------|
               <map:WithArgument Position="2">
                  <map:Equals>FileAccess.Read</map:Equals>
               </map:WithArgument>
|---------------------------------------------------------------------|
               <map:WithCalledMethodExpression>
                  <map:WithLeftSideOfDottedAccess>
                     <map:AssignName>$folderReference</map:AssignName>
                  </map:WithLeftSideOfDottedAccess>
               </map:WithCalledMethodExpression>
            </map:Case.Condition>
         <map:Case.Action>
            <map:ReplaceWithTemplate>
               await $folderReference.OpenStreamForReadAsync($fileNameExpression)
            </map:ReplaceWithTemplate>
         </map:Case.Action>
      </map:Case>
      <map:Case>
         ...
      </map:Case>
      <map:Default>
         ...
      </map:Default>
   </map:Conditional>
</map:CodeMap>
...

At this point the condition fails since the value of the argument is not the expected one.

Since this condition failed the mappings mechanism will proceed to test the next Case . Since the code of this case is very similar to the one we just executed we will fast forward to the point where we failed


Step #6

We moved to the next case (also we fast forward to the point the second case differs from the first one). The expression being tested is:

-- Windows Phone Silverlight --

f = isoStore.OpenFile("file.txt",FileMode.Open,  FileAccess.Write);
                                                |________________|

The following condition verifies that the argument value is File.Write .

-- UWP Converter code mapping --

...
<map:CodeMap Kind="Call" MemberName="OpenFile">
   <map:Conditional>
      <map:Case>
         ...
      </map:Case>
      <map:Case>
         <map:Case.Condition>
            <map:WithMethodCall>
               <map:ArgumentCount>3</map:ArgumentCount>
               <map:WithArgument Position="0">
                  <map:AssignName>$fileNameExpression</map:AssignName>
               </map:WithArgument>
               <map:WithArgument Position="1">
                  <map:AnyConditionApplies>
                     <map:Equals>FileMode.Truncate</map:Equals>
                     <map:Equals>FileMode.Open</map:Equals>
                  </map:AnyConditionApplies>
               </map:WithArgument>
|---------------------------------------------------------------------|
               <map:WithArgument Position="2">
                  <map:Equals>FileAccess.Write</map:Equals>
               </map:WithArgument>
|---------------------------------------------------------------------|
               <map:WithCalledMethodExpression>
                  <map:WithLeftSideOfDottedAccess>
                     <map:AssignName>$folderReference</map:AssignName>
                  </map:WithLeftSideOfDottedAccess>
               </map:WithCalledMethodExpression>
            </map:WithMethodCall>
         </map:Case.Condition>
         <map:Case.Action>
            <map:ReplaceWithTemplate>
               await $folderReference.OpenStreamForWriteAsync($fileNameExpression, Windows.Storage.CreationCollisionOption.ReplaceExisting)
            </map:ReplaceWithTemplate>
         </map:Case.Action>
      </map:Case>
      <map:Default>
         ...
      </map:Default>
   </map:Conditional>
</map:CodeMap>
...

This condition succeeds and we proceed with the last part of the condition.


Step #7

We examine the last part of the condition of the second case:

-- Windows Phone Silverlight --

f = isoStore.OpenFile("file.txt",FileMode.Open, FileAccess.Write);
   |_________________|

Since the condition uses the WithCalledMethodExpression condition then the current part of the expression being tested is the method reference itself.

-- UWP Converter code mapping --

...
<map:CodeMap Kind="Call" MemberName="OpenFile">
   <map:Conditional>
      <map:Case>
         ...
      </map:Case>
      <map:Case>
         <map:Case.Condition>
            <map:WithMethodCall>
               <map:ArgumentCount>3</map:ArgumentCount>
               <map:WithArgument Position="0">
                  <map:AssignName>$fileNameExpression</map:AssignName>
               </map:WithArgument>
               <map:WithArgument Position="1">
                  <map:AnyConditionApplies>
                     <map:Equals>FileMode.Truncate</map:Equals>
                     <map:Equals>FileMode.Open</map:Equals>
                  </map:AnyConditionApplies>
               </map:WithArgument>

               <map:WithArgument Position="2">
                  <map:Equals>FileAccess.Write</map:Equals>
               </map:WithArgument>


               <map:WithCalledMethodExpression>
                  <map:WithLeftSideOfDottedAccess>
                     <map:AssignName>$folderReference</map:AssignName>
                  </map:WithLeftSideOfDottedAccess>
               </map:WithCalledMethodExpression>


            </map:WithMethodCall>
         </map:Case.Condition>
         <map:Case.Action>
            <map:ReplaceWithTemplate>
               await $folderReference.OpenStreamForWriteAsync($fileNameExpression, Windows.Storage.CreationCollisionOption.ReplaceExisting)
            </map:ReplaceWithTemplate>
         </map:Case.Action>
      </map:Case>
      <map:Default>
         ...
      </map:Default>
   </map:Conditional>
</map:CodeMap>
...

This condition succeeds and we proceed with the last part of the condition.


Step #8

We examine the last part of the condition of the second case:

-- Windows Phone Silverlight --

f = isoStore.OpenFile("file.txt",FileMode.Open, FileAccess.Write);
    |_______|

Since the condition uses the WithCalledMethodExpression condition then the current part of the expression being tested is the method reference itself.

-- UWP Converter code mapping --

...
<map:CodeMap Kind="Call" MemberName="OpenFile">
   <map:Conditional>
      <map:Case>
         ...
      </map:Case>
      <map:Case>
         <map:Case.Condition>
            <map:WithMethodCall>
               <map:ArgumentCount>3</map:ArgumentCount>
               <map:WithArgument Position="0">
                  <map:AssignName>$fileNameExpression</map:AssignName>
               </map:WithArgument>
               <map:WithArgument Position="1">
                  <map:AnyConditionApplies>
                     <map:Equals>FileMode.Truncate</map:Equals>
                     <map:Equals>FileMode.Open</map:Equals>
                  </map:AnyConditionApplies>
               </map:WithArgument>
               <map:WithArgument Position="2">
                  <map:Equals>FileAccess.Write</map:Equals>
               </map:WithArgument>
|---------------------------------------------------------------------|
               <map:WithCalledMethodExpression>
                  <map:WithLeftSideOfDottedAccess>
                     <map:AssignName>$folderReference</map:AssignName>
                  </map:WithLeftSideOfDottedAccess>
               </map:WithCalledMethodExpression>
|---------------------------------------------------------------------|
            </map:WithMethodCall>
         </map:Case.Condition>
         <map:Case.Action>
            <map:ReplaceWithTemplate>
               await $folderReference.OpenStreamForWriteAsync($fileNameExpression, Windows.Storage.CreationCollisionOption.ReplaceExisting)
            </map:ReplaceWithTemplate>
         </map:Case.Action>
      </map:Case>
      <map:Default>
         ...
      </map:Default>
   </map:Conditional>
</map:CodeMap>
...

This condition succeeds and we proceed to execute the action related to this Case.

Overview

Writing mappings

Code Mapping Actions

Code Mapping Conditions

XAML mapping actions

XAML mapping conditions

Misc

Clone this wiki locally