001 /** 002 * Copyright 2005-2012 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.rice.krad.uif.widget; 017 018 import org.apache.commons.lang.ClassUtils; 019 import org.apache.commons.lang.StringUtils; 020 import org.kuali.rice.core.api.util.type.KualiDecimal; 021 import org.kuali.rice.core.api.util.type.KualiInteger; 022 import org.kuali.rice.core.api.util.type.KualiPercent; 023 import org.kuali.rice.krad.uif.UifConstants; 024 import org.kuali.rice.krad.uif.container.CollectionGroup; 025 import org.kuali.rice.krad.uif.control.Control; 026 import org.kuali.rice.krad.uif.field.DataField; 027 import org.kuali.rice.krad.uif.field.InputField; 028 import org.kuali.rice.krad.uif.view.View; 029 import org.kuali.rice.krad.uif.control.CheckboxControl; 030 import org.kuali.rice.krad.uif.control.CheckboxGroupControl; 031 import org.kuali.rice.krad.uif.control.RadioGroupControl; 032 import org.kuali.rice.krad.uif.control.SelectControl; 033 import org.kuali.rice.krad.uif.component.Component; 034 import org.kuali.rice.krad.uif.field.FieldGroup; 035 import org.kuali.rice.krad.uif.layout.LayoutManager; 036 import org.kuali.rice.krad.uif.layout.TableLayoutManager; 037 import org.kuali.rice.krad.uif.util.ObjectPropertyUtils; 038 import org.kuali.rice.krad.web.form.UifFormBase; 039 040 import java.sql.Timestamp; 041 import java.util.Set; 042 043 /** 044 * Decorates a HTML Table client side with various tools 045 * 046 * <p> 047 * Decorations implemented depend on widget implementation. Examples are 048 * sorting, paging and skinning. 049 * </p> 050 * 051 * @author Kuali Rice Team (rice.collab@kuali.org) 052 */ 053 public class RichTable extends WidgetBase { 054 private static final long serialVersionUID = 4671589690877390070L; 055 056 private String emptyTableMessage; 057 private boolean disableTableSort; 058 /** since columns are visible by default, this set holds propertyNames for the ones meant to be hidden*/ 059 private Set<String> hiddenColumns; 060 /**holds the propertyNames for columns that are to be sorted*/ 061 private Set<String> sortableColumns; 062 063 064 private boolean showSearchAndExportOptions = true; 065 066 public RichTable() { 067 super(); 068 } 069 070 /** 071 * The following initialization is performed: 072 * 073 * <ul> 074 * <li>Initializes component options for empty table message</li> 075 * </ul> 076 */ 077 @Override 078 public void performFinalize(View view, Object model, Component component) { 079 super.performFinalize(view, model, component); 080 081 UifFormBase formBase = (UifFormBase) model; 082 083 if (isRender()) { 084 if (StringUtils.isNotBlank(getEmptyTableMessage())) { 085 getComponentOptions().put(UifConstants.TableToolsKeys.LANGUAGE, 086 "{\"" + UifConstants.TableToolsKeys.EMPTY_TABLE + "\" : \"" + getEmptyTableMessage() + "\"}"); 087 } 088 089 if (!isShowSearchAndExportOptions()) { 090 Object domOption = getComponentOptions().get(UifConstants.TableToolsKeys.SDOM); 091 if (domOption instanceof String) { 092 String sDomOption = (String) domOption; 093 if (StringUtils.isNotBlank(sDomOption)) { 094 sDomOption = StringUtils.remove(sDomOption, "T"); //Removes Export option 095 sDomOption = StringUtils.remove(sDomOption, "f"); //Removes search option 096 getComponentOptions().put(UifConstants.TableToolsKeys.SDOM, sDomOption); 097 } 098 } 099 100 } 101 102 // for add events, disable initial sorting 103 if (UifConstants.ActionEvents.ADD_LINE.equals(formBase.getActionEvent())) { 104 getComponentOptions().put(UifConstants.TableToolsKeys.AASORTING, "[]"); 105 } 106 107 if (component instanceof CollectionGroup) { 108 buildTableOptions((CollectionGroup) component); 109 } 110 111 if (isDisableTableSort()) { 112 getComponentOptions().put(UifConstants.TableToolsKeys.TABLE_SORT, "false"); 113 } 114 } 115 } 116 117 /** 118 * Builds column options for sorting 119 * 120 * @param collectionGroup 121 */ 122 protected void buildTableOptions(CollectionGroup collectionGroup) { 123 LayoutManager layoutManager = collectionGroup.getLayoutManager(); 124 125 // if sub collection exists, don't allow the table sortable 126 if (!collectionGroup.getSubCollections().isEmpty()) { 127 setDisableTableSort(true); 128 } 129 130 if (!isDisableTableSort()) { 131 // if rendering add line, skip that row from col sorting 132 if (collectionGroup.isRenderAddLine() 133 && !collectionGroup.isReadOnly() 134 && !((layoutManager instanceof TableLayoutManager) && ((TableLayoutManager) layoutManager) 135 .isSeparateAddLine())) { 136 getComponentOptions().put(UifConstants.TableToolsKeys.SORT_SKIP_ROWS, 137 "[" + UifConstants.TableToolsValues.ADD_ROW_DEFAULT_INDEX + "]"); 138 } 139 140 StringBuffer tableToolsColumnOptions = new StringBuffer("["); 141 142 if (layoutManager instanceof TableLayoutManager && ((TableLayoutManager) layoutManager) 143 .isRenderSequenceField()) { 144 tableToolsColumnOptions.append(" null ,"); 145 } 146 147 // skip select field if enabled 148 if (collectionGroup.isRenderSelectField()) { 149 String colOptions = constructTableColumnOptions(false, null, null); 150 tableToolsColumnOptions.append(colOptions + " , "); 151 } 152 153 // if data dictionary defines aoColumns, copy here and skip default sorting/visibility behaviour 154 if (!StringUtils.isEmpty(getComponentOptions().get(UifConstants.TableToolsKeys.AO_COLUMNS))) { 155 // get the contents of the JS array string 156 String jsArray = getComponentOptions().get(UifConstants.TableToolsKeys.AO_COLUMNS); 157 int startBrace = StringUtils.indexOf(jsArray,"["); 158 int endBrace = StringUtils.lastIndexOf(jsArray, "]"); 159 tableToolsColumnOptions.append(StringUtils.substring(jsArray, startBrace + 1, endBrace) + " , "); 160 } else { 161 // use layout manager sortableColumns and hiddenColumns if set 162 Set<String> currentSortableColumns = getSortableColumns(); 163 Set<String> currentHiddenColumns = getHiddenColumns(); 164 if (layoutManager instanceof TableLayoutManager) { 165 TableLayoutManager tableLayoutMgr = (TableLayoutManager) layoutManager; 166 if (tableLayoutMgr.getSortableColumns() != null && !tableLayoutMgr.getSortableColumns().isEmpty()) { 167 currentSortableColumns = tableLayoutMgr.getSortableColumns(); 168 } 169 if (tableLayoutMgr.getHiddenColumns() != null && !tableLayoutMgr.getHiddenColumns().isEmpty()) { 170 currentHiddenColumns = tableLayoutMgr.getHiddenColumns(); 171 } 172 } 173 // TODO: does this handle multiple rows correctly? 174 for (Component component : collectionGroup.getItems()) { 175 // for FieldGroup, get the first field from that group 176 if (component instanceof FieldGroup) { 177 component = ((FieldGroup) component).getItems().get(0); 178 } 179 180 if (component instanceof DataField) { 181 DataField field = (DataField) component; 182 // if a field is marked as invisible in hiddenColumns, append options and skip sorting 183 if (currentHiddenColumns != null && currentHiddenColumns.contains(field.getPropertyName())) { 184 tableToolsColumnOptions.append("{" + UifConstants.TableToolsKeys.VISIBLE + ": " + UifConstants.TableToolsValues.FALSE + "}, "); 185 // if sortableColumns is present and a field is marked as sortable or unspecified 186 } else if (currentSortableColumns != null && !currentSortableColumns.isEmpty()) { 187 if (currentSortableColumns.contains(field.getPropertyName())) { 188 tableToolsColumnOptions.append(getDataFieldColumnOptions(collectionGroup, field) + ", "); 189 } else { 190 tableToolsColumnOptions.append("{'" + UifConstants.TableToolsKeys.SORTABLE + "': " + UifConstants.TableToolsValues.FALSE + "}, "); 191 } 192 } else {// sortable columns not defined 193 String colOptions = getDataFieldColumnOptions(collectionGroup, field); 194 tableToolsColumnOptions.append(colOptions + " , "); 195 } 196 } else { 197 String colOptions = constructTableColumnOptions(false, null, null); 198 tableToolsColumnOptions.append(colOptions + " , "); 199 } 200 } 201 } 202 203 if (collectionGroup.isRenderLineActions() && !collectionGroup.isReadOnly()) { 204 String colOptions = constructTableColumnOptions(false, null, null); 205 tableToolsColumnOptions.append(colOptions); 206 } else { 207 tableToolsColumnOptions = new StringBuffer(StringUtils.removeEnd(tableToolsColumnOptions.toString(), 208 ", ")); 209 } 210 211 tableToolsColumnOptions.append("]"); 212 213 getComponentOptions().put(UifConstants.TableToolsKeys.AO_COLUMNS, tableToolsColumnOptions.toString()); 214 } 215 } 216 217 /** 218 * construct the column options for a data field 219 * 220 * @param collectionGroup - the collectionGroup in which the data field is defined 221 * @param field - the field to construction options for 222 * @return - options as valid for datatable 223 */ 224 private String getDataFieldColumnOptions(CollectionGroup collectionGroup, DataField field) { 225 String sortType = null; 226 if (!collectionGroup.isReadOnly() && (field instanceof InputField) 227 && ((InputField) field).getControl() != null) { 228 Control control = ((InputField) field).getControl(); 229 if (control instanceof SelectControl) { 230 sortType = UifConstants.TableToolsValues.DOM_SELECT; 231 } else if (control instanceof CheckboxControl || control instanceof CheckboxGroupControl) { 232 sortType = UifConstants.TableToolsValues.DOM_CHECK; 233 } else if (control instanceof RadioGroupControl) { 234 sortType = UifConstants.TableToolsValues.DOM_RADIO; 235 } else { 236 sortType = UifConstants.TableToolsValues.DOM_TEXT; 237 } 238 } else { 239 sortType = UifConstants.TableToolsValues.DOM_TEXT; 240 } 241 242 Class dataTypeClass = ObjectPropertyUtils.getPropertyType(collectionGroup.getCollectionObjectClass(), 243 field.getPropertyName()); 244 return constructTableColumnOptions(true, dataTypeClass, sortType); 245 } 246 247 /** 248 * Constructs the sort data type for each datatable columns. 249 */ 250 protected String constructTableColumnOptions(boolean isSortable, Class dataTypeClass, String sortDataType) { 251 String colOptions = "null"; 252 253 String sortType = ""; 254 if (!isSortable || dataTypeClass == null || sortType == null) { 255 colOptions = "\"" + UifConstants.TableToolsKeys.SORTABLE + "\" : false, \"sType\" : \"string\""; 256 } else { 257 if (ClassUtils.isAssignable(dataTypeClass, KualiPercent.class)) { 258 sortType = UifConstants.TableToolsValues.PERCENT; 259 } else if (ClassUtils.isAssignable(dataTypeClass, KualiInteger.class) || ClassUtils.isAssignable( 260 dataTypeClass, KualiDecimal.class)) { 261 sortType = UifConstants.TableToolsValues.CURRENCY; 262 } else if (ClassUtils.isAssignable(dataTypeClass, Timestamp.class)) { 263 sortType = "date"; 264 } else if (ClassUtils.isAssignable(dataTypeClass, java.sql.Date.class) || ClassUtils.isAssignable( 265 dataTypeClass, java.util.Date.class)) { 266 sortType = UifConstants.TableToolsValues.DATE; 267 } else if (ClassUtils.isAssignable(dataTypeClass, Number.class)) { 268 sortType = UifConstants.TableToolsValues.NUMERIC; 269 } 270 else { 271 sortType = UifConstants.TableToolsValues.STRING; 272 } 273 274 colOptions = "\"" + UifConstants.TableToolsKeys.SORT_DATA_TYPE + "\" : \"" + sortDataType + "\""; 275 colOptions += " , \"" + UifConstants.TableToolsKeys.SORT_TYPE + "\" : \"" + sortType + "\""; 276 } 277 278 colOptions = "{" + colOptions + "}"; 279 280 return colOptions; 281 } 282 283 /** 284 * Returns the text which is used to display text when the table is empty 285 * 286 * @return empty table message 287 */ 288 public String getEmptyTableMessage() { 289 return emptyTableMessage; 290 } 291 292 /** 293 * Setter for a text to be displayed when the table is empty 294 * 295 * @param emptyTableMessage 296 */ 297 public void setEmptyTableMessage(String emptyTableMessage) { 298 this.emptyTableMessage = emptyTableMessage; 299 } 300 301 /** 302 * Returns true if sorting is disabled 303 * 304 * @return the disableTableSort 305 */ 306 public boolean isDisableTableSort() { 307 return this.disableTableSort; 308 } 309 310 /** 311 * Enables/disables the table sorting 312 * 313 * @param disableTableSort the disableTableSort to set 314 */ 315 public void setDisableTableSort(boolean disableTableSort) { 316 this.disableTableSort = disableTableSort; 317 } 318 319 /** 320 * Returns true if search and export options are enabled 321 * 322 * @return the showSearchAndExportOptions 323 */ 324 public boolean isShowSearchAndExportOptions() { 325 return this.showSearchAndExportOptions; 326 } 327 328 /** 329 * Show/Hide the search and export options in tabletools 330 * 331 * @param showSearchAndExportOptions the showSearchAndExportOptions to set 332 */ 333 public void setShowSearchAndExportOptions(boolean showSearchAndExportOptions) { 334 this.showSearchAndExportOptions = showSearchAndExportOptions; 335 } 336 337 public Set<String> getHiddenColumns() { 338 return hiddenColumns; 339 } 340 341 public void setHiddenColumns(Set<String> hiddenColumns) { 342 this.hiddenColumns = hiddenColumns; 343 } 344 345 public Set<String> getSortableColumns() { 346 return sortableColumns; 347 } 348 349 public void setSortableColumns(Set<String> sortableColumns) { 350 this.sortableColumns = sortableColumns; 351 } 352 }