I really like development on Mozilla platform – here is one of the reasons why: I’m played with Boogie (I wrote about it in this post) and I want to upgrade Boogie’s old text database to the SQLite database and I’ve noticed that new Gecko versions enables building XUL templates also with SQLite datasources and not only with with RDF datasources as before so I’ve decided to try this new technology and I really like that I’ve did so.
Here are few notes about how easy I built Boogie using this:

  • firstly there is no JavaScript to initialize Boogie’s UI – this task is fully served by XUL templates as you can see on image below:Boogie application-flow schema
  • here is the excerpt from db-service.js – this part is initializing database service:
    1. function BoogieDatabaseServicePrototype() {}
    2. BoogieDatabaseServicePrototype.prototype =
    3. {
    4. // Getter for Mozilla storage service
    5. _db_srv : null,
    6. get mDbSrv() {
    7. if(!this._db_srv)
    8. this._db_srv = Components.classes["@mozilla.org/storage/service;1"].
    9. getService(Components.interfaces.mozIStorageService);
    10. return this._db_srv;
    11. },
    12. mDbConnect : null,
    13. /**
    14. * Initialize Boogie database
    15. *
    16. * @returns {boolean}
    17. */
    18. initialize : function()
    19. {
    20. var file = Components.classes["@mozilla.org/file/directory_service;1"].
    21. getService(Components.interfaces.nsIProperties).
    22. get("ProfD", Components.interfaces.nsIFile);
    23. file.append("boogiedb.sqlite");
    24. this.mDbConnect = this.mDbSrv.openDatabase(file);
    25. var counter = 0;
    26. while(!this.mDbConnect.connectionReady)
    27. if(counter++ == 1000000) return false;
    28. var create_tables = false;
    29. try {
    30. var stmt = this.mDbConnect.createStatement(SET_DB_ENCODING);
    31. stmt.execute();
    32. if(!this.createTables())
    33. return false;
    34. } catch(e) {}
    35. return true;
    36. }, // end initialize()
    37. // ...
    38. }; // End of BoogieDatabaseServicePrototype
  • an another excerpt from db-service.js which serves adding/updating/deleting of single items for the single Boogie’s data table):
    1. // ...
    2. /**
    3. * Add note to the database
    4. *
    5. * @param aNote {BoogieNotePrototype}
    6. * @returns {integer}
    7. */
    8. addNote : function(aNote)
    9. {
    10. var stmt = this.mDbConnect.createStatement(INSERT_NOTE);
    11. stmt.bindInt32Parameter(0, aNote.mDeveloperId);
    12. stmt.bindInt32Parameter(1, aNote.mProjectId);
    13. stmt.bindUTF8StringParameter(2, aNote.mSummary);
    14. stmt.bindUTF8StringParameter(3, aNote.mText);
    15. stmt.bindStringParameter(4, aNote.mSince);
    16. stmt.execute();
    17. return this.mDbConnect.lastInsertRowID;
    18. }, // end addNote(aNote)
    19. /**
    20. * Gets single note object with given id from the database
    21. *
    22. * @param aId {integer}
    23. * @returns {BoogieNotePrototype} Or FALSE
    24. */
    25. getNote : function(aId)
    26. {
    27. var stmt = this.mDbConnect.createStatement(SELECT_NOTE);
    28. stmt.bindInt32Parameter(0, aId);
    29. while(stmt.executeStep()) {
    30. return new BoogieNotePrototype(stmt.getInt32(0), // note id
    31. stmt.getInt32(1), // developer id
    32. stmt.getInt32(2), // project id
    33. stmt.getUTF8String(3), // summmary
    34. stmt.getUTF8String(4), // text
    35. stmt.getString(5)); // since
    36. }
    37. return null;
    38. }, // end getNote(aId)
    39. /**
    40. * Returns total count of notes
    41. *
    42. * @returns {integer}
    43. */
    44. getNotesCount : function()
    45. {
    46. var stmt = this.mDbConnect.createStatement(NOTES_COUNT);
    47. while(stmt.executeStep())
    48. return stmt.getInt32(0);
    49. return null;
    50. }, // end getDevelopersCount()
    51. /**
    52. * Returns array with all notes that are stored in the database.
    53. *
    54. * @return {array} Returns instances of BoogieNotePrototype.
    55. */
    56. getNotes : function()
    57. {
    58. var notes = new Array();
    59. var stmt = this.mDbConnect.createStatement(SELECT_NOTES);
    60. while(stmt.executeStep()) {
    61. var note = new BoogieNotePrototype(stmt.getInt32(0), // note id
    62. stmt.getInt32(1), // developer id
    63. stmt.getInt32(2), // project id
    64. stmt.getUTF8String(3), // summmary
    65. stmt.getUTF8String(4), // text
    66. stmt.getString(5)); // since
    67. notes.push(note);
    68. }
    69. return notes;
    70. }, // end getNotes()
    71. /**
    72. * Update note with the given new one
    73. *
    74. * @param aOldNote {BoogieNotePrototype}
    75. * @param aNewNote {BoogieNotePrototype}
    76. * @returns {boolean}
    77. */
    78. updateNote : function(aOldNote, aNewNote)
    79. {
    80. var stmt = this.mDbConnect.createStatement(UPDATE_NOTE);
    81. stmt.bindInt32Parameter(0, aNewNote.mDeveloperId);
    82. stmt.bindInt32Parameter(1, aNewNote.mProjectId);
    83. stmt.bindUTF8StringParameter(2, aNewNote.mSummary);
    84. stmt.bindUTF8StringParameter(3, aNewNote.mText);
    85. stmt.bindStringParameter(4, aNewNote.mSince);
    86. stmt.bindInt32Parameter(5, aOldNote.mId);
    87. stmt.execute();
    88. return true;
    89. }, // end updateNote(aOldNote, aNewNote)
    90. /**
    91. * Remove note with the given identifier from the database
    92. *
    93. * @param aId {integer}
    94. * @returns {BoogieNotePrototype} Or FALSE.
    95. */
    96. removeNote : function(aId)
    97. {
    98. var stmt = this.mDbConnect.createStatement(REMOVE_NOTE);
    99. stmt.bindInt32Parameter(0, aId);
    100. stmt.execute();
    101. return note;
    102. }, // end removeNote()
    103. /**
    104. * Removes all note
    105. *
    106. * @returns {boolean}
    107. */
    108. removeAllNotes : function()
    109. {
    110. try {
    111. this.removeAll("notes");
    112. } catch(e) {
    113. Components.utils.reportError(e);
    114. return false;
    115. }
    116. return true;
    117. }, // end removeAllNotes()
    118. // ...
  • This is the most simplest example of using XUL templates – here we need to render menulist with developers:
    1. <menulist id="developer-menulist"
    2. oncommand="developerMenulistCommand();"
    3. datasources="profile:boogiedb.sqlite"
    4. querytype="storage" ref="*"
    5. persist="value">
    6. <template>
    7. <query>
    8. select idd, surname, firstname from developers
    9. </query>
    10. <action>
    11. <menupopup>
    12. <menuitem uri="?" label="?surname ?firstname" value="?idd"/>
    13. </menupopup>
    14. </action>
    15. </template>
    16. </menulist>
  • And here is a little more complicated example – this XUL code renders tree with notes listed – you can notice that SQL query uses more tables to collect the requested result:
    1. <tree id="boogieNotesTree" flex="1"
    2. hidecolumnpicker="true" selType="single"
    3. datasources="profile:boogiedb.sqlite" ref="*"
    4. querytype="storage" flags="dont-build-content">
    5. <treecols>
    6. <!-- Here are some treecols -->
    7. </treecols>
    8. <template>
    9. <query>
    10. SELECT
    11. a.idn,
    12. a.summary,
    13. c.surname AS developer_surname,
    14. c.firstname AS developer_firstname,
    15. b.name AS project_name,
    16. a.since
    17. FROM notes AS a
    18. LEFT JOIN projects AS b ON b.idp = a.idp
    19. LEFT JOIN developers AS c ON c.idd = a.idd
    20. WHERE 1
    21. </query>
    22. <action>
    23. <treechildren>
    24. <treeitem uri="?">
    25. <treerow value="?idn">
    26. <treecell label="?summary"/>
    27. <treecell label="?developer_surname ?developer_firstname"/>
    28. <treecell label="?project_name"/>
    29. <treecell label="?since"/>
    30. </treerow>
    31. </treeitem>
    32. </treechildren>
    33. </action>
    34. </template>
    35. </tree>
  • the latest example is the most complex – it uses complicated SQL query with parameters:
    1. <tree anonid="bugview-tree" flex="1" enableColumnDrag="true"
    2. seltype="single" datasources="profile:boogiedb.sqlite" ref="*"
    3. querytype="storage" flags="dont-build-content">
    4. <!-- *** treecols *** -->
    5. <template>
    6. <query>
    7. SELECT
    8. a.idb,
    9. b.name AS type,
    10. c.name AS project,
    11. d.name AS component,
    12. e.surname AS developer_surname,
    13. e.firstname AS developer_firstname,
    14. f.name AS status,
    15. g.name AS resolution,
    16. h.name AS severity,
    17. i.name AS priority,
    18. j.name AS version_from,
    19. k.name AS version_to
    20. FROM bugs AS a
    21. LEFT JOIN types AS b ON b.idt = a.idt
    22. LEFT JOIN projects AS c ON c.idp = a.idp
    23. LEFT JOIN components AS d ON d.idc = a.idc
    24. LEFT JOIN developers AS e ON e.idd = a.idd
    25. LEFT JOIN statuses AS f ON f.ids = a.ids
    26. LEFT JOIN resolutions AS g ON g.idr = a.idr
    27. LEFT JOIN severities AS h ON h.idsv = a.idsv
    28. LEFT JOIN priorities AS i ON i.idpr = a.idpr
    29. LEFT JOIN versions AS j ON j.idv = a.idv1
    30. LEFT JOIN versions AS k ON j.idv = a.idv2
    31. WHERE
    32. a.idp = :boogieProjectID
    33. <param id="bugsViewProjectID" name="boogieProjectID">1</param>
    34. </query>
    35. <action>
    36. <!-- *** treechildren *** -->
    37. </action>
    38. </template>
    39. </tree>

Source files mentioned above:

Usefull links: