001 /**
002 * Copyright 2005-2014 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 }