nsString dave=fail;

I’m going to have something to say in a little while about two of my favourite topics: coping and failure, and on the meaning and implications of each.  They are linked in important ways.  However, today I’m going to model some of what I’ll say then, and to do it my subject will be the Mozilla String API.

In the lead-up to Thunderbird 3, I’m trying to get a bunch of Mac integration and enhancement bugs fixed, specifically with regard to how new mail notification works.  I’ve got most of them out of the way now, and only a few left that need review or clean-up so I can post the patches.  It’s been really smooth learning the Thunderbird code, and I’ve enjoyed myself (almost) every step of the way.

And then I started working on bug 460287.  Wouldn’t it be nice if when you got a new mail notification via Growl, it let you know who it was from?  Of course it would, and I know just how to do it.  The only thing that stands in my way is the Mozilla String API in all its labyrinthine glory.  What I need to do is get the folder that just got new mail, extract the new messages, parse their headers to find the author string, extract the name portion (if present), notify observers that I’m doing this and give them a chance to veto, take those names and insert them into a localized string, and pass that up to growl for display.  Easy.

But, my current attempt crashes Thunderbird; or, I should say, it crashes it again, because I’ve fixed a bunch of crashes caused by my string use already.  As soon as I get done writing this I’m going to take another stab at fixing things, but first I want to write from a position of failure.  I want to write from the standpoint of failure vs. success because I want to show that failure is not something to be feared, but rather a form of information.

So why are strings in Mozilla so bloody hard?  What am I doing that is so prone to error?  The problem is that a string is not simply a sequence of characters.  Strings are hard because language is hard, and when you have software that supports so many languages, you can’t make even basic assumptions based on your own understanding of what is and isn’t possible.  Further, there are different ways to represent the same thing in memory.

To go back to what I’m trying to do, let me show you the representations of the strings I’m working with at each step of the way:

  1. Get the author for the current message — char**
  2. Extract the name portion — nsACString
  3. Notify observers of this notification for the author — PRUnichar*
  4. Localize this string — PRUnichar* + nsXPIDLString
  5. Pass this to growl for display — nsAString

This simplifies it a bit, but I’m too frustrated to spend more time explaining it.  Suffice it to say, the movement from string representations, specifically from narrow (UTF8) to wide (UTF16) strings, is something I don’t have quite right yet.  I mean, if your program crashes when you get new mail, that is a sort of notification.  It just might be too strong a message.

I’ve had to deal with the String API before, and I don’t mind telling you that I’m not an expert.  It’s confused me every time I’ve used it.  Normally my use of it involves one function that needs an implicit widening/shortening of a string, and I manage to get it working after a bit of trial and error.  However, in this case my method of trial by fire meant that I was just getting burned.  The only way for me to do my work was for me to spend an evening studying the Mozilla XPCOM String Guide.  Looking at other code was no longer helpful, because I had too many variables to manage for good guesses to work.  In order to cope, I had to pause so I could really understand this stuff.

That guide is really incredible.  If it didn’t exist, I’d have no hope of doing any of this work.  Even with it I’m still confused about some things.  But for the majority of what I’m doing, it explains it quite clearly.  After reading it top to bottom and sketching out on paper what I needed to do, I was finally able to get my strings to work.

Next, I needed to get the localization of the text being shown to the user working properly.  I wanted to do something very simple, namely, create a comma separated list of names and addresses.  “Have you thought about RTL [ed. right to left] languages?”  No.  “Have you looked to see if all languages use a comma for list separation?”  No.  “Did you know you’re pluralizing your strings totally incorrectly?”  No.

Just how do you localize strings?  You work with nsIStringBundle and Property files.  In essence, what I needed to do was something like this:

biffNotification_messages=%1$S new messages from %2$S.

This says that I want to pass two strings and have them merged into this third string.  Specifically, I will pass the number of messages (%1$S) and the list of names (%2$S).  Localizers can translate this third string and move the position of those other strings around as makes sense in their target language.

But I wanted to be a bit more clever.  In JS there is some nice code to let you do pluralization of localized strings, and I wanted to do something similar, even though I still have to use a hack to special case the single message instance:

biffNotification_message=One new message from %2$S.

biffNotification_messages=%1$S new messages from %2$S.

I wanted to use this trick of only using one of the possible strings (i.e., omitting %1$S) so I could do this in my C++ and pass it to the stringbundle for formatting:

NS_ConvertUTF8toUTF16 utf16Authors(authors);
const PRUnichar *formatStrings[2] = { numNewMsgsText.get(), utf16Authors.get() };

Crash: null pointer error.  At this point I assume my issue is the string conversion (yes, I need another PRUnichar*), so I start debugging that.  But after a while it’s clear that this isn’t the issue.  The stringbundle itself is the culprit.  It seemed as though it was being set to null while I was still using it, and within an if(bundle) block!

After swearing and abandoning my computer for lunch, I came at the problem again, this time asking for help.  Luckily I got the one man who really knows about this code (Pike) to confirm what I was already suspecting:  If you send an array of 2 strings to be merged with a properties string, you have to use both of them in the properties file:

biffNotification_message=%1$S new message from %2$S.

biffNotification_messages=%1$S new messages from %2$S.

Also, it’s not possible to have a trailing space on a property value.  So instead of this for my localized comma separator:

biffNotification_separator=,

(See the trailing space? Don’t worry, neither does Mozilla)…I need to do this:

biffNotification_separator=,\u0020

I’m writing this in order to talk about failure and coping–you shouldn’t copy my code exactly as it is now, because it’s wrong.  While I’ve been writing this I’ve had two bugmails, one rejecting my patch, and another with a crash stack and some interesting information about how I’m not properly handling the German umlaut.  I’ve got the failure down.  So what about coping?  I said at the beginning that failure is really information.  You don’t fail and therefore become a failure.  You fail and in so doing you learn and gain more understanding.

My failure has caused me to go back and read the XPCOM String Guide, re-learn to leverage the knowledge in the community and its willingness to help, given me insight about what does and doesn’t work with regard to localized strings, etc.  Failure is information if you use your mistakes to learn and teach something new.  I don’t want to pretend that I enjoy being wrong, or like showing you that I’ve made mistakes.  I don’t.  At the same time, I’ve come to understand that if I’m not making mistakes it means I’m not trying hard enough, and I’m not pushing myself far enough.

I write this in a moment of failure without the answer in clear view.  This failure is information I take with me as I move forward toward the solution.  I am not broken by my failure, but encouraged to know that I now have a handful of mistakes and some new information that are key to my continued growth.

This entry was posted in CDOT, Idea Factory, Seneca. Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

5 Trackbacks

  1. [...] wrote previously about a personal experience of failure as a way of exploring ideas that I want to elaborate on [...]

  2. [...] get to the end of a project, and the conclusion was, “This can’t be done.”  Failure is data, and good failure will have a long paper trail and evidence of participation and honest [...]

  3. By Living on the Edge « Test Blog on May 31, 2009 at 10:06 pm

    [...] In a web of blogs relating to each other via the topic of failure, I came across this quote from here: “I’ve come to understand that if I’m not making mistakes it means I’m not trying hard [...]

  4. By Machinations on September 18, 2009 at 10:18 pm

    [...] doing the same thing.  I’ll have much to say in the coming weeks about the importance of failure, and also how to succeed.  But both failure and success look the same from the proper [...]

  5. By Two thoughts from class on October 15, 2009 at 10:42 pm

    [...] Very quickly the students had the idea of using the Error Console as a way of logging debug info.  The code where we were working was all C++ instead of JavaScript (all examples on how to use the Error Console from code are in JavaScript), so we had to start by understanding how to use it from native code.  Doing this without the help of MXR makes the work more painful, since you can’t go and look for examples in the code.  However, we worked slowly, and by the end of the class had 80% of the solution done.  All that remained was to figure out some tricky bits of the string API. [...]

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>