When Mike's coworker left the company for greener pastures, Mike got stuck with maintaining all of his old projects. This wouldn't have been too bad if it weren't for a application that was developed using the BrillantML (as I'll call it). While Brillant ML is technically a programming language, a more apt description is "a few commands surrounded by an XML wrapper." Yes, it really is as horrid as it sounds.

BrillantML is used to create interfaces on a certain type of consumer hardware, and Mike's first experience with it was when he needed to change some of the menu labels around. The first thing he noticed was his predecessor didn't believe in using loops. Anywhere.

If, for example, the previous programmer needed to set nine text labels on the screen to the first nine entries in the test "database," he would do the following:

<sub name="vmenu_init_update">
  <action value="
    set(@lbl_vmenu_0.text,$$DATA_HomePage[0].menu_item); redraw(@lbl_vmenu_0); 
    set(@lbl_vmenu_1.text,$$DATA_HomePage[1].menu_item); redraw(@lbl_vmenu_1);
    set(@lbl_vmenu_2.text,$$DATA_HomePage[2].menu_item); redraw(@lbl_vmenu_2);
    set(@lbl_vmenu_3.text,$$DATA_HomePage[3].menu_item); redraw(@lbl_vmenu_3);
    set(@lbl_vmenu_4.text,$$DATA_HomePage[4].menu_item); redraw(@lbl_vmenu_4);
    set(@lbl_vmenu_5.text,$$DATA_HomePage[5].menu_item); redraw(@lbl_vmenu_5);
    set(@lbl_vmenu_6.text,$$DATA_HomePage[6].menu_item); redraw(@lbl_vmenu_6);
    set(@lbl_vmenu_7.text,$$DATA_HomePage[7].menu_item); redraw(@lbl_vmenu_7);
    set(@lbl_vmenu_8.text,$$DATA_HomePage[8].menu_item); redraw(@lbl_vmenu_8);
    " />
</sub>

Mike thought it was ugly code and saw it as a great opportunity to refactor.

"The first problem I ran into," Mike wrote, "was that BrillantML doesn’t support arrays of labels. Actually, it doesn’t support arrays at all." Fortunately, there was a work-around: DataTables. All he'd have to do was create a BrillantML-compatable datatable, like this....

<?xml version="1.0" encoding="UTF-8"?>
<data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <hmenu_data>
    <row>
      <hmenu_item>FIRST</hmenu_item>
    </row>
    <row>
      <hmenu_item>SECOND</hmenu_item>
    </row>
    <row>
      <hmenu_item>THIRD</hmenu_item>
    </row>
    <row>
      <hmenu_item>FOURTH</hmenu_item>
    </row>
    <row>
      <hmenu_item>FIFTH</hmenu_item>
    </row>
   </hmenu_data>
 </data>

Of course, he'd also need to create a schema:

 <?xml version="1.0" encoding="UTF-8" ?>
 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"   
     xmlns:bml="http://initrodeglobal.com/xml/ns/brillantml/data-types"
     elementFormDefault="qualified" 
     attributeFormDefault="unqualified">
    
   <xs:element name="data">
     <xs:complexType>
       <xs:sequence>
         <xs:element name="hmenu_data" type="hmenuType"/>
       </xs:sequence>
     </xs:complexType>
   </xs:element>
   
     <xs:complexType name="hmenuType">
     <xs:sequence>
       <xs:element name="row" minOccurs="1" maxOccurs="unbounded">
         <xs:complexType>
           <xs:complexContent>
             <xs:extension base="rowType">
               <xs:sequence>
                 <xs:element name="hmenu_item" type="bml:mixed-string"/>
               </xs:sequence>
             </xs:extension>
           </xs:complexContent>
         </xs:complexType>
       </xs:element>
     </xs:sequence>
   </xs:complexType>
   
   <xs:complexType name="rowType">
     <xs:attribute name="number" type="xs:int" use="optional"/>
   </xs:complexType>
     
</xs:schema>

"The important thing here," Mike explained, "is the type definition of hmenu_item in the schema. You'll see it has a type of 'bml:mixed-string'. This is one of the defined types that BrillantML will recognize. However, one of the types that BrillantML won’t recognize is anything related to a label. So, even though you can have label variables, you cannot make an array of labels." Obviously, that makes it challenging to loop through an array of labels.

Undaunted, Mike created a routine that would check the value of the counter in the loop and then set a variable to the label using a set of if/else pairs. "It was easy to reference the loop counter and the variable," he wrote, "since BrillantML doesn’t use local variables, only global variables."

So, after defining the DataTable, and the schema, Mike's final pair of routines looked like this.

<sub name="set_cur_label">
  <if>
      <test lhs="$i" expr="eq" rhs="0" />
      <action value="set($cur_label,@lbl_vmenu_0)"/>
  </if>
  <elseif>
      <test lhs="$i" expr="eq" rhs="1" />
      <action value="set($cur_label,@lbl_vmenu_1)"/>
  </elseif>
  <elseif>
      <test lhs="$i" expr="eq" rhs="2" />
      <action value="set($cur_label,@lbl_vmenu_2)"/>
  </elseif>

  ... snip ...
  
  <elseif>
      <test lhs="$i" expr="eq" rhs="8" />
      <action value="set($cur_label,@lbl_vmenu_8)"/>
  </elseif>
  <else>
      <action value="set(@lbl_debug.text,'error: attempt to get label');" />
  </else>
</sub>

<sub name="vmenu_init_update">
  <action value="set($i,0)"/>
  <loop>
     <if>
        <test lhs="$i" expr="gt" rhs="8" />
        <break/>
     </if>
     <else>
        <action value="call(set_cur_label)"/>
        <action value="
           set($cur_label.text,$$DATA_HomePage[$i].menu_item); 
           redraw($cur_label);" />
        <action value="add($i,1)"/>
     </else>
  </loop>
</sub>

As you may have noticed, loops in BrillantML are "simplified." Loop tags have no attributes (such as "until" or "while"), instead requiring an if/else statement block. In either block, you have to include a <break/> statement, or risk an infinite loop.

However, when Mike "ran" his "code," all of labels had their text set to the tenth item ($$DATA_HomePage[9].menu_item) in the list. As it turned out, that was a result of another undocumented "feature" of BrillantML: when you set() the value of something, it doesn't actually "set" the "value." It merely "associates" the two. In other words, if you call "set(@lbl_vmenu_2.text,$$DATA_HomePage[$i].menu_item);", lbl_vmenu_2 will always contain the current value of $$DATA_HomePage[$i].menu_item, regardless of whether $i changes or the menu_item changes.

So, after a half day of hacking, Mike undid his check-out and returned the code to the way it was before. He decided to loop no more.

[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!