public void doSave(Widget frmModel, JXPathContext jxpc)
throws BindingException {
// Find the repeater
Repeater repeater = (Repeater) selectWidget(frmModel, this.repeaterId);
// and his context
JXPathContext repeaterContext =
jxpc.getRelativeContext(jxpc.getPointer(this.repeaterPath));
// create set of updatedRowIds
Set updatedRows = new HashSet();
//create list of rows to insert at end
List rowsToInsert = new ArrayList();
// iterate rows in the form model...
int formRowCount = repeater.getSize();
for (int i = 0; i < formRowCount; i++) {
Repeater.RepeaterRow thisRow = repeater.getRow(i);
// Get the identity
List identity = getIdentity(thisRow);
if (hasNonNullElements(identity)) {
// iterate nodes to find match
Iterator rowPointers = repeaterContext.iteratePointers(this.rowPath);
boolean found = false;
while (rowPointers.hasNext()) {
Pointer jxp = (Pointer) rowPointers.next();
JXPathContext rowContext = repeaterContext.getRelativeContext(jxp);
List contextIdentity = getIdentity(rowContext);
if (ListUtils.isEqualList(identity, contextIdentity)) {
// match! --> bind to children
this.rowBinding.saveFormToModel(thisRow, rowContext);
// --> store rowIdValue in list of updatedRowIds
updatedRows.add(identity);
found = true;
break;
}
}
if (!found) {
// this is a new row
rowsToInsert.add(thisRow);
// also add it to the updated row id's so that this row doesn't get deleted
updatedRows.add(identity);
}
} else {
// if there is no value to determine the identity --> this is a new row
rowsToInsert.add(thisRow);
}
}
// Iterate again nodes for deletion
Iterator rowPointers = repeaterContext.iteratePointers(this.rowPath);
List rowsToDelete = new ArrayList();
while (rowPointers.hasNext()) {
Pointer jxp = (Pointer)rowPointers.next();
JXPathContext rowContext = repeaterContext.getRelativeContext((Pointer)jxp.clone());
List contextIdentity = getIdentity(rowContext);
// check if the identity of the rowContext is in the updated rows
// if not --> bind for delete
if (!isIdentityInUpdatedRows(updatedRows, contextIdentity)) {
rowsToDelete.add(rowContext);
}
}
if (rowsToDelete.size() > 0) {
if (this.deleteRowBinding != null) {
// run backwards through the list, so that we don't get into
// trouble by shifting indexes
for (int i = rowsToDelete.size() - 1; i >= 0; i--) {
this.deleteRowBinding.saveFormToModel(frmModel,
rowsToDelete.get(i));
}
} else {
if (getLogger().isWarnEnabled()) {
getLogger().warn(
"RepeaterBinding has detected rows to delete, " +
"but misses the <on-delete-row> binding to do it."
);
}
}
}
// count how many we have now
int indexCount = 1;
rowPointers = repeaterContext.iteratePointers(this.rowPathForInsert);
while (rowPointers.hasNext()) {
rowPointers.next();
indexCount++;
}
// end with rows to insert (to make sure they don't get deleted!)
if (rowsToInsert.size() > 0) {
if (this.insertRowBinding != null) {
Iterator rowIterator = rowsToInsert.iterator();
//register the factory!
while (rowIterator.hasNext()) {
Repeater.RepeaterRow thisRow = (Repeater.RepeaterRow)rowIterator.next();
// Perform the insert row binding.
this.insertRowBinding.saveFormToModel(repeater, repeaterContext);
// --> create the path to let the context be created
Pointer newRowContextPointer = repeaterContext.createPath(
this.rowPathForInsert + "[" + indexCount + "]");
JXPathContext newRowContext =
repeaterContext.getRelativeContext(newRowContextPointer);
if (getLogger().isDebugEnabled()) {
getLogger().debug("inserted row at " + newRowContextPointer.asPath());
}
// + rebind to children for update