/** * OLAT - Online Learning and Training<br> * http://www.olat.org * <p> * Licensed under the Apache License, Version 2.0 (the "License"); <br> * you may not use this file except in compliance with the License.<br> * You may obtain a copy of the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing,<br> * software distributed under the License is distributed on an "AS IS" BASIS, <br> * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> * See the License for the specific language governing permissions and <br> * limitations under the License. * <p> * Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> * University of Zurich, Switzerland. * <hr> * <a href="http://www.openolat.org"> * OpenOLAT - Online Learning and Training</a><br> * This file has been modified by the OpenOLAT community. Changes are licensed * under the Apache 2.0 license as the original file. * <p> */ package org.olat.core.gui.components.table; import java.sql.Timestamp; import java.text.Collator; import java.util.Date; import java.util.Locale; import org.olat.core.gui.components.EscapeMode; import org.olat.core.gui.render.Renderer; import org.olat.core.gui.render.StringOutput; import org.olat.core.util.Formatter; import org.olat.core.util.StringHelper; import org.olat.core.util.filter.impl.OWASPAntiSamyXSSFilter; /** * Description:<br> * * @author Felix Jost */ public class DefaultColumnDescriptor implements ColumnDescriptor { public final static String DEFAULT_POPUP_ATTRIBUTES = "height=600, width=600, location=no, menubar=no, resizable=yes, status=no, scrollbars=yes, toolbar=no"; private Formatter formatter; private String action; private String headerKey; private int alignment; private boolean popUpWindowAction; private String popUpWindowAttributes; //protected to allow overriding of compare method protected Locale locale; protected Collator collator; protected Table table; protected int dataColumn; private EscapeMode escapeHtml = EscapeMode.html; private boolean translateHeaderKey = true; /** * Constructor for table default column descriptor * @param headerKey translation key for column header * @param dataColumn position of column * @param action name of event that should be fired when rows column is clicken. null allowed for no action * @param locale the users locale */ public DefaultColumnDescriptor(final String headerKey, final int dataColumn, final String action, final Locale locale) { this(headerKey, dataColumn, action, locale, ColumnDescriptor.ALIGNMENT_LEFT); } /** * * @param headerKey * @param dataColumn * @param action * @param locale used ONLY for method getRenderValue in case the Object is of type Date to provide locale-sensitive Date formatting * @param alignment left, middle or right; constants in ColumnDescriptor */ public DefaultColumnDescriptor(final String headerKey, final int dataColumn, final String action, final Locale locale, final int alignment) { this.dataColumn = dataColumn; this.headerKey = headerKey; this.action = action; this.locale = locale; this.alignment = alignment; if (locale != null) { formatter = Formatter.getInstance(locale); collator = Collator.getInstance(locale); } } /** * @see org.olat.core.gui.components.table.ColumnDescriptor#getHeaderKey() */ public String getHeaderKey() { return headerKey; } public boolean translateHeaderKey() { return translateHeaderKey; } public void setTranslateHeaderKey(final boolean translateHeaderKey) { this.translateHeaderKey = translateHeaderKey; } public void setEscapeHtml(EscapeMode escape) { this.escapeHtml = escape; } /** * * @param row the row in the table * @return the object to be rendered given the row */ protected Object getModelData(final int row) { return table.getTableDataModel().getValueAt(table.getSortedRow(row),dataColumn); } /** * @see org.olat.core.gui.components.table.ColumnDescriptor#renderValue(org.olat.core.gui.render.StringOutput, int, org.olat.core.gui.render.Renderer) */ public void renderValue(final StringOutput sb, final int row, final Renderer renderer) { Object val = getModelData(row); if (val == null) { return; } if (val instanceof Date) { String res = formatter.formatDateAndTime((Date)val); sb.append(res); } else if(val instanceof String) { renderString(sb, (String)val); } else { renderString(sb, val.toString()); } } private void renderString(StringOutput sb, String val) { switch(escapeHtml) { case none: sb.append(val); break; case html: StringHelper.escapeHtml(sb, val); break; case antisamy: sb.append(new OWASPAntiSamyXSSFilter().filter(val)); break; default : StringHelper.escapeHtml(sb, val); } } /** * @see org.olat.core.gui.components.table.ColumnDescriptor#getAlignment() */ public int getAlignment() { return alignment; } /** * is called repeatedly caused by Collections.sort(...); * @see org.olat.core.gui.components.table.ColumnDescriptor#compareTo(int, int) */ @Override public int compareTo(final int rowa, final int rowb) { Object a = table.getTableDataModel().getValueAt(rowa,dataColumn); Object b = table.getTableDataModel().getValueAt(rowb,dataColumn); // depending on the class of the Objects, we compare if (a == null || b == null) { return compareNullObjects(a, b); } if (a instanceof String && b instanceof String) { return collator.compare(a, b); } if(a instanceof Date && b instanceof Date) { return compareDateAndTimestamps((Date)a, (Date)b); } if (a instanceof Boolean && b instanceof Boolean) { // faster than string compare, boolean are comparable return compareBooleans((Boolean)a, (Boolean)b); } if (a instanceof Comparable && a.getClass().equals(b.getClass())) { //compare the same things return ((Comparable)a).compareTo(b); } return a.toString().compareTo(b.toString()); } protected int compareString(final String a, final String b) { if (a == null || b == null) { return compareNullObjects(a, b); } return collator == null ? a.compareTo(b) : collator.compare(a, b); } protected int compareBooleans(final Boolean a, final Boolean b) { if (a == null || b == null) { return compareNullObjects(a, b); } boolean ba = a.booleanValue(); boolean bb = b.booleanValue(); return ba? (bb? 0: -1):(bb? 1: 0); } protected int compareDateAndTimestamps(Date a, Date b) { if (a == null || b == null) { return compareNullObjects(a, b); } if (a instanceof Timestamp) { // a timestamp (a) cannot compare a date (b), but vice versa is ok. if(b instanceof Timestamp) { return ((Timestamp)a).compareTo((Timestamp)b); } else { Timestamp ta = (Timestamp)a; Date aAsDate = new Date(ta.getTime()); return aAsDate.compareTo(b); } } else if (b instanceof Timestamp) { Timestamp tb = (Timestamp)b; Date bAsDate = new Date(tb.getTime()); return a.compareTo(bAsDate); } return a.compareTo(b); } protected int compareNullObjects(final Object a, final Object b) { boolean ba = (a == null); boolean bb = (b == null); return ba? (bb? 0: -1):(bb? 1: 0); } /** * @see org.olat.core.gui.components.table.ColumnDescriptor#setTable(org.olat.core.gui.components.table.Table) */ public void setTable(final Table table) { this.table = table; } /** * @see org.olat.core.gui.components.table.ColumnDescriptor#getAction(int) */ public String getAction(final int row) { return action; } /** * Sets the alignment. * @param alignment The alignment to set */ public void setAlignment(final int alignment) { this.alignment= alignment; } /** * @return Table */ protected Table getTable() { return table; } /** * @see org.olat.core.gui.components.table.ColumnDescriptor#modelChanged() */ public void modelChanged() { // empty } /** * @see org.olat.core.gui.components.table.ColumnDescriptor#sortingAboutToStart() */ public void sortingAboutToStart() { // empty } /** * @return int */ @Override public int getDataColumn() { return dataColumn; } /** * @see org.olat.core.gui.components.table.ColumnDescriptor#otherColumnDescriptorSorted() */ public void otherColumnDescriptorSorted() { // empty } /** * @see org.olat.core.gui.components.table.ColumnDescriptor#isSortingAllowed() */ public boolean isSortingAllowed() { return true; } /** * @return Locale */ public Locale getLocale() { return locale; } /** * @see org.olat.core.gui.components.table.ColumnDescriptor#isPopUpWindowAction() */ public boolean isPopUpWindowAction() { return popUpWindowAction; } /** * @see org.olat.core.gui.components.table.ColumnDescriptor#getPopUpWindowAttributes() */ public String getPopUpWindowAttributes() { return popUpWindowAttributes; } /** * TODO:fj:b replace with PopupObject which is easily configurable * * Optional action link configuration * @param popUpWindowAction true: action link will open in new window, false: action opens in current window * @param popUpWindowAttributes javascript window.open attributes or null if default values are used * e.g. something like this: "height=600, width=600, location=no, menubar=no, resizable=yes, status=no, scrollbars=yes, toolbar=no" */ public void setIsPopUpWindowAction(final boolean popUpWindowAction, final String popUpWindowAttributes) { this.popUpWindowAction = popUpWindowAction; this.popUpWindowAttributes = popUpWindowAttributes; } }