Salesforce.com SOAP API Gotchas Part 3

This is the third part in a series about tricky parts of the Salesforce.com SOAP Partner API (see Part 1, Part 2, and Part 4).

Dependent Picklists

In Salesforce.com’s object model, two of the possible datatypes for fields are picklist and multipicklist. A picklist is perhaps best described as a drop down menu: the user gets to pick one of a predefined list of values. A multipicklist is just like a picklist except that multiple values (out of the predefined list) may be selected simultaneously. A picklist or multipicklist field may also be part of a controlling field / dependent field pair. (Both the controlling field and the dependent field must be of type picklist or multipicklist.) I found the documentation on controlling and dependent picklist fields to be confusing, so I’ll provide some clarification to supplement the documentation that already exists on the PicklistEntry object.

The selected entry in the controlling field defines what entries are available in the dependent field. As an example, suppose we have a controlling field “Model” with picklist options “Alpha” and “Beta” and a dependent field “Color” with options “Blue”, “Red” and “Green”. Model Alpha only comes in Red and Green and Model Beta only comes in Blue and Green. If you were editing an object with these fields in the Salesforce.com web app, if Model was set to Beta, the Color picklist would only have Blue and Green as choices.

When you describe an SObject type (via describeSObjects()), one of the things you get back is a list of Field objects that contain metadata about each of the fields like the type of the field, the display label of the field, etc. By examining the Field objects through the API for Model and Color, we can discover the following:

  • Color has a controllerName of “Model”
  • Color has dependentPicklist set to true

To tell via the API which dependent picklist entries are valid for which controlling picklist entries, we can examine the validFor byte[] field in each of PicklistEntry objects, which we get from the Field object for the dependent field. The encoding of the relationship between dependent picklist entries and controlling picklist entries is a little tricky, so I’m going to explain it in detail.

First, we’ll need a more complex and precise example to better illustrate the encoding. Suppose we have a controlling picklist with entries alpha through india (NATO codes for the first nine letters of the alphabet) and a dependent picklist with entries a through i (the letters corresponding to those NATO codes). Each letter is valid for its NATO code, the letter ‘e’ is valid for every code, and every letter is valid for the ‘hotel’ code. Therefore, ‘bravo’ would have options ‘b’ and ‘e’ show up in the dependent picklist, and so forth.

alpha bravo charlie delta echo foxtrot golf hotel india
a x x
b x x
c x x
d x x
e x x x x x x x x x
f x x
g x x
h x
i x x

Painstaking Deconstruction of the byte[] validFor data

If you’re only interested in how to interpret the validFor data, you can skip to the next section. On the other hand, if you want to know the details, read on!

According to the SOAP specification, byte[] data is encoded with Base64 for transmission. Here is the validFor field for the picklist entries ‘a’ – ‘i’ in Base64 as sent by Salesforce.com as well as its hexadecimal and raw binary representation. (The binary is written with the most significant bit on the left, as is customary.)

Letter Base64 Hex Binary
a gQAA 0x810000 10000001 00000000 …
b QQAA 0x410000 01000001 00000000 …
c IQAA 0x210000 00100001 00000000 …
d EQAA 0x110000 00010001 00000000 …
e /4AA 0xff8000 11111111 10000000 …
f BQAA 0x050000 00000101 00000000 …
g AwAA 0x030000 00000011 00000000 …
h AQAA 0x010000 00000001 00000000 …
i AYAA 0x018000 00000001 10000000 …

You may have noticed that the pattern of 1’s in the binary representation looks like the x’s in the previous table. However, it only matches the pattern because the binary was written with the most significant bit on the left. It’s written that way because RFC 2045 defines the Base64 encoding of an arbitrary stream of bits, including the way that the bit stream is to be broken up into bytes. Specifically, it says:

When encoding a bit stream via the base64 encoding, the bit stream must be presumed to be ordered with the most-significant-bit first. That is, the first bit in the stream will be the high-order bit in the first 8bit byte, and the eighth bit will be the low-order bit in
the first 8bit byte, and so on.

What this means in this context is that although the binary data visually matches the first table, when accessed as bits in a byte, the ‘first’ bit in the stream of binary is actually the high order bit (aka most significant bit or MSB) in the byte. To see if entry ‘c’ is valid for entry ‘charlie’, we would look at the byte b = 0x21 (00100001 in binary, MSB first). A reasonable assumption would be that to get the validFor entry for the third controlling picklist, you’d want the third bit in the byte. The third bit can be extracted with boolean bit = ((b >> 2) & 1) == 1. That’s a right shift of the 3rd bit into the first bit position (the >> 2 part) followed with a binary AND against 1 (the & 1 part) to select the bit that was just shifted into the first bit. However, because the first bit in the bit stream is the high order bit according to the Base64 spec, that means that the third bit in the stream is the third from high order bit, so the amount we actually want to right shift is 7 - 2 = 5. A right shift of 7 would be used for the high bit, so a right shift of 5 will be the third highest bit. So, though the bytes are intuitively ordered, the ordering of the bits in each byte is the opposite of what you might expect.

Extracting the validFor bits in the proper order

Generalizing what we figured out in the previous section, the following loop (in Java) will extract the bits in the proper order.

int numBits = validForBytes.length * 8;
 
for (int i = 0; i < numBits; i++) {
    // the byte to pull the bit from
    int byteIndex = i / 8;
    // the position in that byte for the current bit -- bits are reversed in each byte
    int bitIndexInByte = 7 - (i % 8);
 
    byte b = validForBytes[byteIndex];
    boolean bit = ((b >> bitIndexInByte) & 1) == 1;
    // do something with the bit
}

You could put them back into another byte[], but you may wish to create a java.util.BitSet instead for easier access to individual bits. Note that the BitSet class is mutable, so be careful to not accidentally modify your BitSet instance after populating it with the byte[].

Haven’t had enough Salesforce yet? Read on in Part 4.

  • Digg
  • StumbleUpon
  • del.icio.us
  • Facebook
  • Twitter
  • Google Bookmarks
  • DZone
  • HackerNews
  • LinkedIn
  • Reddit