public void doLoad(Widget frmModel, JXPathContext jctx) throws BindingException {
    // (There should be a general widget type checker for all the bindings to use,
    // coupled with a general informative exception class to throw if the widget is
    // of the wrong type or null.)
    Repeater repeater = (Repeater) selectWidget(frmModel, this.repeaterId);
    if (repeater == null) {
      String fullId = frmModel.getRequestParameterName();
      if (fullId == null || fullId.length() == 0) {
        fullId = "";
      } else {
        fullId = fullId + ".";
      }
      throw new RuntimeException(
          "TempRepeaterJXPathBinding: Repeater \""
              + fullId
              + this.repeaterId
              + "\" does not exist ("
              + frmModel.getLocation()
              + ")");
    }

    // Start by clearing the repeater, if necessary.
    if (this.clearOnLoad) {
      repeater.clear();
    }

    // Find the location of the repeater data.
    Pointer repeaterPointer = jctx.getPointer(this.repeaterPath);

    // Check if there is data present.
    //
    // (Otherwise, should we check the leniency config option
    // to decide whether to be silent or throw an exception?)
    if (repeaterPointer != null) {

      // Narrow to repeater context.
      JXPathContext repeaterContext = jctx.getRelativeContext(repeaterPointer);

      // Build a jxpath iterator for the repeater row pointers.
      Iterator rowPointers = repeaterContext.iteratePointers(this.rowPath);

      // Iterate through the rows of data.
      int rowNum = 0;
      while (rowPointers.hasNext()) {

        // Get or create a row widget.
        Repeater.RepeaterRow thisRow;
        if (repeater.getSize() > rowNum) {
          thisRow = repeater.getRow(rowNum);
        } else {
          thisRow = repeater.addRow();
        }
        rowNum++;

        // Narrow to the row context.
        Pointer rowPointer = (Pointer) rowPointers.next();
        JXPathContext rowContext = repeaterContext.getRelativeContext(rowPointer);

        // If virtual rows are requested, place a deep clone of the row data
        // into a temporary node, and narrow the context to this virtual row.
        //
        // (A clone of the data is used to prevent modifying the source document.
        // Otherwise, the appendChild method would remove the data from the source
        // document.  Is this protection worth the penalty of a deep clone?)
        //
        // (This implementation of virtual rows currently only supports DOM
        // bindings, but could easily be extended to support other bindings.)

        if (virtualRows) {
          Node repeaterNode = (Node) repeaterPointer.getNode();
          Node virtualNode = repeaterNode.getOwnerDocument().createElementNS(null, "virtual");
          Node node = (Node) rowPointer.getNode();
          Node clone = node.cloneNode(true);
          Node fakeDocElement = node.getOwnerDocument().getDocumentElement().cloneNode(false);
          virtualNode.appendChild(clone);
          fakeDocElement.appendChild(virtualNode);
          rowContext = JXPathContext.newContext(repeaterContext, fakeDocElement);
          rowContext = rowContext.getRelativeContext(rowContext.getPointer("virtual"));
        }

        // Finally, perform the load row binding.
        this.rowBinding.loadFormFromModel(thisRow, rowContext);
      }
    }

    if (getLogger().isDebugEnabled()) getLogger().debug("done loading rows " + this);
  }