public void doSave(Widget frmModel, JXPathContext jctx) throws BindingException { // (See comment in doLoad about type checking and throwing a meaningful exception.) Repeater repeater = (Repeater) selectWidget(frmModel, this.repeaterId); // Perform shortcut binding if the repeater is empty // and the deleteIfEmpty config option is selected. if (repeater.getSize() == 0 && this.deleteIfEmpty) { // Delete all of the old data for this repeater. jctx.removeAll(this.repeaterPath); // Otherwise perform the normal save binding. } else { // Narrow to the repeater context, creating the path if it did not exist. JXPathContext repeaterContext = jctx.getRelativeContext(jctx.createPath(this.repeaterPath)); // Start by deleting all of the old row data. repeaterContext.removeAll(this.rowPath); // Verify that repeater is not empty and has an insert row binding. if (repeater.getSize() > 0) { if (this.insertRowBinding != null) { // register the factory! // this.insertRowBinding.saveFormToModel(repeater, repeaterContext); // Iterate through the repeater rows. for (int i = 0; i < repeater.getSize(); i++) { // Narrow to the repeater row context. Pointer rowPointer = repeaterContext.getPointer(this.rowPathInsert); JXPathContext rowContext = repeaterContext.getRelativeContext(rowPointer); // Variables used for virtual rows. // They are initialized here just to keep the compiler happy. Node rowNode = null; Node virtualNode = null; // If virtual rows are requested, create a temporary node and // narrow the context to this initially empty new virtual row. if (virtualRows) { rowNode = (Node) rowContext.getContextBean(); Document document = rowNode.getOwnerDocument(); virtualNode = document.createElementNS(null, "virtual"); Node fakeDocElement = document.getDocumentElement().cloneNode(false); fakeDocElement.appendChild(virtualNode); rowContext = JXPathContext.newContext(repeaterContext, fakeDocElement); rowContext = rowContext.getRelativeContext(rowContext.getPointer("virtual")); } // Perform the insert row binding this.insertRowBinding.saveFormToModel(repeater, rowContext); // Perform the save row binding. this.rowBinding.saveFormToModel(repeater.getRow(i), rowContext); // If virtual rows are requested, finish by appending the // children of the virtual row to the real context node. if (virtualRows) { NodeList list = virtualNode.getChildNodes(); int count = list.getLength(); for (int j = 0; j < count; j++) { // The list shrinks when a child is appended to the context // node, so we always reference the first child in the list. rowNode.appendChild(list.item(0)); } } getLogger().debug("bound new row"); } } else { getLogger() .warn( "TempRepeaterBinding has detected rows to insert, " + "but misses the <on-insert-row> binding to do it."); } } } }
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); }