1 16 package org.apache.cocoon.woody.binding; 17 18 import java.util.ArrayList ; 19 import java.util.HashSet ; 20 import java.util.Iterator ; 21 import java.util.List ; 22 import java.util.Locale ; 23 import java.util.Set ; 24 25 import org.apache.avalon.framework.logger.Logger; 26 import org.apache.cocoon.woody.datatype.convertor.Convertor; 27 import org.apache.cocoon.woody.formmodel.Widget; 28 import org.apache.cocoon.woody.formmodel.Repeater; 29 import org.apache.commons.collections.ListUtils; 30 import org.apache.commons.jxpath.JXPathContext; 31 import org.apache.commons.jxpath.Pointer; 32 33 40 public class RepeaterJXPathBinding extends JXPathBindingBase { 41 42 private final String repeaterId; 43 private final String repeaterPath; 44 private final String rowPath; 45 private final String rowPathForInsert; 46 private final JXPathBindingBase rowBinding; 47 private final JXPathBindingBase insertRowBinding; 48 private final JXPathBindingBase deleteRowBinding; 49 private final List uniqueRowBinding; 50 51 54 public RepeaterJXPathBinding( 55 JXPathBindingBuilderBase.CommonAttributes commonAtts, 56 String repeaterId, String repeaterPath, String rowPath, 57 String rowPathForInsert, String uniqueRowId, 58 String uniqueRowPath, JXPathBindingBase[] childBindings, 59 JXPathBindingBase insertBinding, 60 JXPathBindingBase[] deleteBindings, JXPathBindingBase[] uniqueBindings) { 61 this(commonAtts, repeaterId, repeaterPath, rowPath, rowPathForInsert, 62 uniqueRowId, uniqueRowPath, null, null, childBindings, 63 insertBinding, deleteBindings, uniqueBindings); 64 } 65 66 69 public RepeaterJXPathBinding( 70 JXPathBindingBuilderBase.CommonAttributes commonAtts, 71 String repeaterId, String repeaterPath, String rowPath, 72 String rowPathForInsert, String uniqueRowId, 73 String uniqueRowPath, Convertor convertor, Locale convertorLocale, 74 JXPathBindingBase[] childBindings, JXPathBindingBase insertBinding, 75 JXPathBindingBase[] deleteBindings, JXPathBindingBase[] uniqueBindings) { 76 super(commonAtts); 77 this.repeaterId = repeaterId; 78 this.repeaterPath = repeaterPath; 79 this.rowPath = rowPath; 80 this.rowPathForInsert = rowPathForInsert; 81 this.rowBinding = new ComposedJXPathBindingBase( 82 JXPathBindingBuilderBase.CommonAttributes.DEFAULT, 83 childBindings); 84 this.rowBinding.setParent(this); 85 this.insertRowBinding = insertBinding; 86 if (this.insertRowBinding != null) { 87 this.insertRowBinding.setParent(this); 88 } 89 90 if (deleteBindings != null) { 91 this.deleteRowBinding = new ComposedJXPathBindingBase( 92 JXPathBindingBuilderBase.CommonAttributes.DEFAULT, 93 deleteBindings); 94 this.deleteRowBinding.setParent(this); 95 } else { 96 this.deleteRowBinding = null; 97 } 98 99 uniqueRowBinding = new ArrayList (); 101 if (uniqueRowId != null && uniqueRowPath != null) { 103 uniqueRowBinding.add(new UniqueFieldJXPathBinding( 104 JXPathBindingBuilderBase.CommonAttributes.DEFAULT, 105 uniqueRowId, uniqueRowPath, convertor, convertorLocale)); 106 } 107 if (uniqueBindings != null) { 108 for (int i=0; i < uniqueBindings.length; i++) { 109 uniqueRowBinding.add(uniqueBindings[i]); 110 } 111 } 112 } 113 114 119 public void doLoad(Widget frmModel, JXPathContext jxpc) 120 throws BindingException { 121 Repeater repeater = (Repeater) frmModel.getWidget(this.repeaterId); 123 repeater.removeRows(); 124 int initialSize = repeater.getSize(); 125 126 JXPathContext repeaterContext = 128 jxpc.getRelativeContext(jxpc.getPointer(this.repeaterPath)); 129 Iterator rowPointers = repeaterContext.iteratePointers(this.rowPath); 130 while (rowPointers.hasNext()) { 132 Repeater.RepeaterRow thisRow; 134 if (initialSize > 0) { 135 thisRow = repeater.getRow(--initialSize); 136 } else { 137 thisRow = repeater.addRow(); 138 } 139 Pointer jxp = (Pointer)rowPointers.next(); 141 JXPathContext rowContext = repeaterContext.getRelativeContext(jxp); 142 Iterator iter = this.uniqueRowBinding.iterator(); 144 while (iter.hasNext()) { 145 ((UniqueFieldJXPathBinding)iter.next()).loadFormFromModel(thisRow, rowContext); 146 } 147 this.rowBinding.loadFormFromModel(thisRow, rowContext); 148 } 149 if (getLogger().isDebugEnabled()) 150 getLogger().debug("done loading rows " + toString()); 151 } 152 153 158 public void doSave(Widget frmModel, JXPathContext jxpc) 159 throws BindingException { 160 Repeater repeater = (Repeater) frmModel.getWidget(this.repeaterId); 162 JXPathContext repeaterContext = 164 jxpc.getRelativeContext(jxpc.getPointer(this.repeaterPath)); 165 166 Set updatedRowIds = new HashSet (); 168 List rowsToInsert = new ArrayList (); 170 171 int formRowCount = repeater.getSize(); 173 for (int i = 0; i < formRowCount; i++) { 174 Repeater.RepeaterRow thisRow = repeater.getRow(i); 175 176 List rowIdValues = getUniqueRowValues(thisRow); 178 179 if (isAnyListElementNotNull(rowIdValues)) { 180 Iterator rowPointers = repeaterContext.iteratePointers(this.rowPath); 182 boolean found = false; 183 while (rowPointers.hasNext()) { 184 Pointer jxp = (Pointer) rowPointers.next(); 185 JXPathContext rowContext = repeaterContext.getRelativeContext(jxp); 186 List matchIds = getMatchIds(rowContext); 187 if (ListUtils.isEqualList(rowIdValues, matchIds)) { 188 this.rowBinding.saveFormToModel(thisRow, rowContext); 190 updatedRowIds.add(rowIdValues); 192 found = true; 193 break; 194 } 195 } 196 if (!found) { 197 rowsToInsert.add(thisRow); 199 updatedRowIds.add(rowIdValues); 201 } 202 } else { 203 rowsToInsert.add(thisRow); 205 } 206 } 207 Iterator rowPointers = repeaterContext.iteratePointers(this.rowPath); 209 List rowsToDelete = new ArrayList (); 210 while (rowPointers.hasNext()) { 211 Pointer jxp = (Pointer)rowPointers.next(); 212 JXPathContext rowContext = repeaterContext.getRelativeContext((Pointer)jxp.clone()); 213 List matchIds = getMatchIds(rowContext); 214 if (!isListInSet(updatedRowIds, matchIds)) { 216 rowsToDelete.add(rowContext); 217 } 218 } 219 if (rowsToDelete.size() > 0) { 220 if (this.deleteRowBinding != null) { 221 for (int i = rowsToDelete.size() - 1; i >= 0; i--) { 224 this.deleteRowBinding.saveFormToModel(frmModel, 225 rowsToDelete.get(i)); 226 } 227 } else { 228 if (getLogger().isWarnEnabled()) { 229 getLogger().warn( 230 "RepeaterBinding has detected rows to delete, " + 231 "but misses the <on-delete-row> binding to do it." 232 ); 233 } 234 } 235 } 236 int indexCount = 1; 238 rowPointers = repeaterContext.iteratePointers(this.rowPathForInsert); 239 while (rowPointers.hasNext()) { 240 rowPointers.next(); 241 indexCount++; 242 } 243 if (rowsToInsert.size() > 0) { 245 if (this.insertRowBinding != null) { 246 Iterator rowIterator = rowsToInsert.iterator(); 247 while (rowIterator.hasNext()) { 249 Repeater.RepeaterRow thisRow = (Repeater.RepeaterRow)rowIterator.next(); 250 this.insertRowBinding.saveFormToModel(repeater, repeaterContext); 252 Pointer newRowContextPointer = repeaterContext.createPath( 254 this.rowPathForInsert + "[" + indexCount + "]"); 255 JXPathContext newRowContext = 256 repeaterContext.getRelativeContext(newRowContextPointer); 257 if (getLogger().isDebugEnabled()) { 258 getLogger().debug("inserted row at " + newRowContextPointer.asPath()); 259 } 260 this.rowBinding.saveFormToModel(thisRow, newRowContext); 262 getLogger().debug("bound new row"); 263 indexCount++; 264 } 265 } else { 266 if (getLogger().isWarnEnabled()) { 267 getLogger().warn("RepeaterBinding has detected rows to insert, but misses " + 268 "the <on-insert-row> binding to do it."); 269 } 270 } 271 } 272 if (getLogger().isDebugEnabled()) { 273 getLogger().debug("done saving rows " + toString()); 274 } 275 } 276 277 283 private boolean isListInSet(Set set, List list) { 284 Iterator iter = set.iterator(); 285 while (iter.hasNext()) { 286 List listFromSet = (List )iter.next(); 287 if (ListUtils.isEqualList(listFromSet, list)) { 288 return true; 289 } 290 } 291 return false; 292 } 293 294 299 private boolean isAnyListElementNotNull(List list) { 300 Iterator iter = list.iterator(); 301 while (iter.hasNext()) { 302 if (iter.next() != null) { 303 return true; 304 } 305 } 306 return false; 307 } 308 309 314 private List getMatchIds(JXPathContext rowContext) { 315 List matchIds = new ArrayList (); 316 Iterator iter = this.uniqueRowBinding.iterator(); 317 while (iter.hasNext()) { 318 UniqueFieldJXPathBinding key = (UniqueFieldJXPathBinding)iter.next(); 319 Object matchId = rowContext.getValue(key.getXpath()); 320 if (matchId != null && key.getConvertor() != null) { 321 if (matchId instanceof String ) { 322 matchId = key.getConvertor().convertFromString( 323 (String )matchId, key.getConvertorLocale(), null); 324 } else { 325 if (getLogger().isWarnEnabled()) { 326 getLogger().warn("Convertor ignored on backend-value " + 327 "which isn't of type String."); 328 } 329 } 330 } 331 matchIds.add(matchId); 332 } 333 return matchIds; 334 } 335 336 341 private List getUniqueRowValues(Repeater.RepeaterRow thisRow) { 342 List values = new ArrayList (); 343 Iterator iter = this.uniqueRowBinding.iterator(); 344 while (iter.hasNext()) { 345 UniqueFieldJXPathBinding key = (UniqueFieldJXPathBinding)iter.next(); 346 Widget rowIdWidget = thisRow.getWidget(key.getFieldId()); 347 Object rowIdValue = rowIdWidget.getValue(); 348 values.add(rowIdValue); 349 } 350 return values; 351 } 352 353 public String toString() { 354 return "RepeaterJXPathBinding [widget=" + this.repeaterId + 355 ", xpath=" + this.repeaterPath + "]"; 356 } 357 358 public void enableLogging(Logger logger) { 359 super.enableLogging(logger); 360 if (this.deleteRowBinding != null) { 361 this.deleteRowBinding.enableLogging(logger); 362 } 363 if (this.insertRowBinding != null) { 364 this.insertRowBinding.enableLogging(logger); 365 } 366 this.rowBinding.enableLogging(logger); 367 Iterator iter = this.uniqueRowBinding.iterator(); 368 while (iter.hasNext()) { 369 ((UniqueFieldJXPathBinding)iter.next()).enableLogging(logger); 370 } 371 } 372 } 373 | Popular Tags |