001 /* 002 Copyright (C) 2001-2003 Laurent Martelli <laurent@aopsys.com> 003 004 This program is free software; you can redistribute it and/or modify 005 it under the terms of the GNU Lesser General Public License as 006 published by the Free Software Foundation; either version 2 of the 007 License, or (at your option) any later version. 008 009 This program is distributed in the hope that it will be useful, 010 but WITHOUT ANY WARRANTY; without even the implied warranty of 011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 012 GNU Lesser General Public License for more details. 013 014 You should have received a copy of the GNU Lesser General Public License 015 along with this program; if not, write to the Free Software 016 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 017 018 package org.objectweb.jac.aspects.gui.web; 019 020 import java.io.PrintWriter; 021 import java.net.URLEncoder; 022 import org.apache.log4j.Logger; 023 import org.objectweb.jac.aspects.gui.*; 024 import org.objectweb.jac.core.NameRepository; 025 import org.objectweb.jac.core.rtti.ClassRepository; 026 import org.objectweb.jac.core.rtti.CollectionItem; 027 import org.objectweb.jac.core.rtti.FieldItem; 028 029 /** 030 * Base class for collection views (list, table, etc) 031 */ 032 public abstract class AbstractCollection extends AbstractView 033 implements CollectionListener, CollectionView, HTMLViewer 034 { 035 static Logger loggerEvents = Logger.getLogger("gui.events"); 036 static Logger loggerEditor = Logger.getLogger("gui.editor"); 037 038 protected CollectionItem collection; 039 protected Object substance; 040 protected CollectionModel model; 041 protected TableSorter sorter; 042 protected TableFilter filter; 043 044 protected boolean autoUpdate = true; 045 protected boolean isEditor = true; 046 047 /* index of selected row (-1 if no row if selection is empty) */ 048 int selected = -1; 049 050 /* number of rows to display on each page */ 051 int rowsPerPage = 10; 052 protected void setRowsPerPage(int rowsPerPage) { 053 this.rowsPerPage = rowsPerPage; 054 split = rowsPerPage>0; 055 } 056 057 /* do we split the collection in multiple pages */ 058 boolean split; 059 060 /* do we show a column with row numbers */ 061 boolean showRowNumbers; 062 063 /* current offset */ 064 int startIndex = 0; 065 066 protected org.objectweb.jac.aspects.gui.CollectionItemView itemView; 067 068 protected boolean viewableItems; // shortcut for itemView.isViewableItems() 069 070 protected ObjectChooser rowsPerPageChooser; 071 072 public AbstractCollection(ViewFactory factory, DisplayContext context, 073 CollectionItem collection, Object substance, 074 CollectionModel model, 075 org.objectweb.jac.aspects.gui.CollectionItemView itemView) 076 { 077 super(factory,context); 078 079 this.collection = collection; 080 this.substance = substance; 081 this.model = model; 082 setRowsPerPage(GuiAC.getNumRowsPerPage(collection)); 083 this.startIndex = GuiAC.getStartIndex(collection); 084 this.showRowNumbers = GuiAC.isShowRowNumbers(collection); 085 this.itemView = itemView; 086 if (itemView!=null) 087 this.viewableItems = itemView.isViewableItems(); 088 init(); 089 sort(); 090 091 int[] availableNumRowsPerPage = GuiAC.getAvailableNumRowsPerPage(collection); 092 if (availableNumRowsPerPage!=null) { 093 ComboBoxModel comboModel = new ComboBoxModel(); 094 for (int i=0; i<availableNumRowsPerPage.length; i++) { 095 int n = availableNumRowsPerPage[i]; 096 comboModel.addObject(new Integer(n), n!=0?(""+n):GuiAC.getLabelAll()); 097 } 098 rowsPerPageChooser = new ObjectChooser(null,null,comboModel,false); 099 rowsPerPageChooser.setLabel(label+"_rowsPerPageChooser"); 100 comboModel.setSelectedItem(""+availableNumRowsPerPage[0]); 101 setRowsPerPage(availableNumRowsPerPage[0]); 102 } 103 } 104 105 public AbstractCollection() { 106 } 107 108 public void setSubstance(Object substance) { 109 this.substance = substance; 110 } 111 112 public Object getSubstance() { 113 return substance; 114 } 115 116 public CollectionModel getCollectionModel() { 117 return model; 118 } 119 120 public boolean isEditor() { 121 return isEditor; 122 } 123 124 public void setEditor(boolean isEditor) { 125 this.isEditor = isEditor; 126 } 127 128 public void setAutoUpdate(boolean autoUpdate) { 129 this.autoUpdate = autoUpdate; 130 } 131 132 /** 133 * Generate HTML code for a collection's event (add, remove, ...) 134 * @param out write HTML to this writer 135 * @param event name of the associated event 136 * @param icon name of icon resource to display 137 * @param label text to display 138 */ 139 void genCollectionEvent(PrintWriter out, 140 String event, 141 String icon, 142 String label) 143 { 144 JacRequest request = WebDisplay.getRequest(); 145 if (request.isIEUserAgent()) { 146 out.println( 147 "<table class=\"method\"><td>"+ 148 iconElement(ResourceManager.getResource(icon),"")+ 149 eventURL(label,event,"").toString()+ 150 "</td></table>"); 151 } else { 152 out.println( 153 eventURL(" "+label,event,"") 154 .add(0,iconElement(ResourceManager.getResource(icon),"").addCssClass("first")) 155 .cssClass("method").toString()); 156 } 157 } 158 159 /** 160 * Sorts the collection with the column index stored in the context 161 * if any. 162 */ 163 public abstract void sort(); 164 165 /** 166 * Initialization to be performed before sort() 167 */ 168 protected void init() {} 169 170 public void onView(Object object) { 171 loggerEvents.debug(toString()+".onView("+object+")"); 172 selected = model.indexOf(object); 173 174 // used for CollectionItemView 175 CollectionPosition extra = new CollectionPosition( 176 this, 177 collection,selected,substance); 178 179 EventHandler.get().onSelection(context,collection,object, 180 null,extra,true); 181 } 182 183 // HTMLViewer interface 184 185 protected void genHeader(PrintWriter out) { 186 genHeader(out, true); 187 } 188 189 /** 190 * Generate HTML for adder, remover, prev/next buttons, ... 191 * @param out print HTML code to this writer 192 */ 193 protected void genHeader(PrintWriter out, boolean div) { 194 if (collection.getSubstance(substance)==null) 195 return; 196 197 boolean addable = GuiAC.isAddable(substance,collection); 198 boolean removable = GuiAC.isRemovable(substance,collection); 199 boolean refreshable = showRefreshButton(); 200 if (!(addable || refreshable || 201 (model.getRowCount()>0 && removable) || 202 (startIndex>0) || 203 (split && startIndex+rowsPerPage<model.getRowCount()))) { 204 return; 205 } 206 if (div) { 207 out.println("<div class=\"methods\">"); 208 } 209 if (addable && isEditor) { 210 if (GuiAC.isAutoCreate(collection)) { 211 genCollectionEvent(out,"onAddToCollection","new_icon",GuiAC.getLabelAdd()); 212 /* 213 genCollectionEvent(out,"onAddToCollection","new_icon","add new"); 214 genCollectionEvent(out,"onAddExistingToCollection","new_icon","add existing"); 215 */ 216 } else { 217 genCollectionEvent(out,"onAddToCollection","new_icon",GuiAC.getLabelAdd()); 218 } 219 } 220 if (startIndex>0) { 221 out.println("<a href=\""+eventURL("onFirst")+"\">"+ 222 iconElement(ResourceManager.getResource("first_icon"),"first")+ 223 "</a>"); 224 out.println("<a href=\""+eventURL("onPrevious")+"\">"+ 225 iconElement(ResourceManager.getResource("previous_icon"),"previous")+ 226 "</a>"); 227 } 228 if (rowsPerPage>0 && model.getRowCount()>0 && 229 (startIndex>0 || startIndex+rowsPerPage<model.getRowCount())) { 230 out.println("["+(startIndex+1)+"-"+ 231 Math.min(startIndex+rowsPerPage,model.getRowCount())+"/"+ 232 model.getRowCount()+"]"); 233 } 234 if (split && startIndex+rowsPerPage<model.getRowCount()) { 235 out.println("<a href=\""+eventURL("onNext")+"\">"+ 236 iconElement(ResourceManager.getResource("next_icon"),"next")+ 237 "</a>"); 238 out.println("<a href=\""+eventURL("onLast")+"\">"+ 239 iconElement(ResourceManager.getResource("last_icon"),"last")+ 240 "</a>"); 241 } 242 if (split && model.getRowCount()>rowsPerPage) { 243 out.println("["+(startIndex/rowsPerPage+1)+"/"+ 244 (int)Math.ceil((float)model.getRowCount()/(float)rowsPerPage)+"]"); 245 } 246 247 if (rowsPerPageChooser!=null) { 248 out.print("Display "); 249 rowsPerPageChooser.genHTML(out); 250 out.print(" rows"); 251 } 252 if (showRefreshButton()) 253 genEventAndActionButton(out,"onRefreshCollection"); 254 255 if (div) { 256 out.println("</div>"); 257 } 258 } 259 260 /** 261 * Tells whether a refesh button must be shown 262 */ 263 protected boolean showRefreshButton() { 264 return rowsPerPageChooser!=null; 265 } 266 267 /** 268 * Gets object at a given index 269 */ 270 protected Object getObject(int index) { 271 return model.getObject(index); 272 } 273 274 // CollectionView interface 275 276 public void setSelected(int index) { 277 selected = index; 278 } 279 280 public void setField(FieldItem field) { 281 collection = (CollectionItem)field; 282 } 283 284 public FieldItem getField() { 285 return collection; 286 } 287 288 public void setValue(Object value) { 289 } 290 291 public void updateModel(Object substance) { 292 } 293 294 // CollectionListener interface 295 296 public void onView(int index) { 297 loggerEvents.debug(toString()+".onView("+index+")"); 298 selected = index; 299 300 // used for CollectionItemView 301 CollectionPosition extra = new CollectionPosition( 302 this, 303 collection,index,substance); 304 305 EventHandler.get().onSelection(context,collection,getObject(index), 306 null,extra,true); 307 loggerEvents.debug(" ending "+toString()+".onView("+index+"), selected="+selected); 308 } 309 310 public void onViewObject(String name) { 311 Object object = NameRepository.get().getObject(name); 312 EventHandler.get().onSelection(context,collection,object, 313 null,null,true); 314 } 315 316 public void onRemove(int index) { 317 loggerEvents.debug(toString()+".onRemove("+index+")"); 318 Object toRemove = getObject(index); 319 320 EventHandler.get() 321 .onRemoveFromCollection( 322 context, 323 new RemoveEvent( 324 this, 325 collection.getSubstance(substance), 326 collection, 327 toRemove), 328 false); 329 } 330 331 public void onTableInvoke(int index,String methodName) { 332 loggerEvents.debug(toString()+".onTableInvoke("+index+")"); 333 selected = index; 334 EventHandler.get().onInvoke( 335 context, 336 new InvokeEvent( 337 this, 338 getObject(index), 339 ClassRepository.get().getClass(getObject(index)).getMethod(methodName))); 340 } 341 342 public void onAddToCollection() { 343 EventHandler.get().onAddToCollection( 344 context, 345 new AddEvent( 346 this, 347 collection.getSubstance(substance), 348 collection, 349 null), 350 false); 351 sort(); 352 } 353 354 public void onAddExistingToCollection() { 355 EventHandler.get().onAddToCollection( 356 context, 357 new AddEvent(this,collection.getSubstance(substance),collection,null), 358 true); 359 sort(); 360 } 361 362 public void onRemoveFromCollection() { 363 EventHandler.get().onRemoveFromCollection( 364 context, 365 new RemoveEvent(this,collection.getSubstance(substance),collection,null), 366 true); 367 sort(); 368 } 369 370 public void onNext() { 371 if (startIndex+rowsPerPage<model.getRowCount()) 372 startIndex += rowsPerPage; 373 context.getDisplay().refresh(); 374 } 375 376 public void onLast() { 377 startIndex = model.getRowCount() - model.getRowCount()%rowsPerPage; 378 if (startIndex==model.getRowCount()) 379 startIndex -= rowsPerPage; 380 context.getDisplay().refresh(); 381 } 382 383 public void onPrevious() { 384 startIndex -= rowsPerPage; 385 if (startIndex<0) 386 startIndex = 0; 387 context.getDisplay().refresh(); 388 } 389 390 public void onFirst() { 391 startIndex = 0; 392 context.getDisplay().refresh(); 393 } 394 395 public void onRefreshCollection() { 396 if (rowsPerPageChooser!=null) { 397 rowsPerPageChooser.readValue( 398 ((WebDisplay)context.getDisplay()) 399 .getRequest().getParameter(rowsPerPageChooser.getLabel())); 400 setRowsPerPage(((Number)rowsPerPageChooser.getValue()).intValue()); 401 } 402 context.getDisplay().refresh(); 403 } 404 405 /** 406 * Ensures that startIndex is not ouf of range 407 */ 408 protected void checkRange() { 409 if (startIndex>=model.getRowCount()) { 410 if (split) { 411 startIndex = model.getRowCount() - model.getRowCount()%rowsPerPage; 412 if (startIndex==model.getRowCount()) 413 startIndex = 0; 414 } else 415 startIndex = 0; 416 } 417 } 418 419 /** 420 * Returns an HTML link to remove the element at a given position. 421 */ 422 protected String removeLink(int position) { 423 // We should use eventURL 424 return "<a href=\""+eventURL("onRemove")+ 425 "&index="+position+"\">"+ 426 iconElement(ResourceManager.getResource("remove_icon"),"remove")+"</a>"; 427 } 428 429 protected String viewLink(int position) { 430 // We should use eventURL 431 return "<a href=\""+eventURL("onView")+ 432 "&index="+position+"\">"+ 433 iconElement(ResourceManager.getResource("view_icon"),"view")+"</a>"; 434 } 435 436 /** 437 * Build an HTML link with an image showing if a column is used to 438 * sort the collection 439 * @param column the index of the column 440 * @param text additional text to put in the link 441 * @return the HTML code of the link 442 */ 443 protected String sortLink(int column,String text) { 444 SortCriteria criteria = sorter.getSortCriteria(column); 445 String iconResource; 446 if (criteria==null) { 447 iconResource = "small_down_icon"; 448 } else { 449 if (criteria.isAscending()) 450 iconResource = "small_down_icon_selected"; 451 else 452 iconResource = "small_up_icon_selected"; 453 } 454 return 455 "<a href=\""+eventURL("onHeaderClick")+"&col="+column+"\"" 456 + " title=\"sort\">" 457 + iconElement(ResourceManager.getResource(iconResource),"sort") 458 + " " + text 459 + "</a>"; 460 } 461 462 // When disabled == true, readValue() does nothing 463 boolean disabled = false; 464 public boolean isEnabled() { 465 return !disabled; 466 } 467 public void setEnabled(boolean enabled) { 468 loggerEditor.debug((enabled?"enable ":"disable ")+this); 469 this.disabled = !enabled; 470 } 471 }