/*
* gvNIX is an open source tool for rapid application development (RAD).
* Copyright (C) 2010 Generalitat Valenciana
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.gvnix.datatables.tags;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.Tag;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.ObjectUtils;
import com.github.dandelion.datatables.core.configuration.Configuration;
import com.github.dandelion.datatables.jsp.tag.ColumnTag;
import com.github.dandelion.datatables.jsp.tag.TableTag;
/**
* Extends the {@link ColumnTag} to make it compatible with the usual roo tag
* attributes. This way it may replace the roo column tag just by changing the
* xmlns:table reference in the jspx header to the URL of this tag library.
*
* @author <a href="http://www.disid.com">DISID Corporation S.L.</a> made for <a
* href="http://www.dgti.gva.es">General Directorate for Information
* Technologies (DGTI)</a>
*/
public class RooColumnTag extends ColumnTag {
private static final long serialVersionUID = -7713119991577135048L;
// Logger
@SuppressWarnings("unused")
private static final Logger logger = LoggerFactory
.getLogger(RooColumnTag.class);
/** Max displayed text length (default '-1'). Unlimited if negative. */
private Integer maxLength = Integer.valueOf(-1);
/** The column label to be used in the table (optional). */
private String label;
/** Indicate that this field is of type {@link Date}. */
private Boolean date = Boolean.FALSE;
/** Indicate that this field is of type {@link Calendar}. */
private Boolean calendar = Boolean.FALSE;
/** The date / time pattern to use if the field is a date or calendar type. */
private DateFormat dateTimePattern = SimpleDateFormat.getDateTimeInstance();
/**
* Indicate if the contents of this tag and all enclosed tags should be
* rendered (default 'true')
*/
private Boolean render = Boolean.TRUE;
/**
* Used for checking if element has been modified (to recalculate simply
* provide empty string value)
*/
private String z;
/**
* ConversionService bean name in the Spring application context. It is used
* to convert the property value to String. (default
* 'applicationConversionService').
*/
private String conversionServiceId = "applicationConversionService";
private final SpringContextHelper helper = new SpringContextHelper();
private static final String SEPARATOR_FIELDS = ".";
private static final String SEPARATOR_FIELDS_ESCAPED = "_~~_";
private boolean doRender() {
return Boolean.TRUE.equals(render) || render == null;
}
public int doStartTag() throws JspException {
TableTag parent = (TableTag) findAncestorWithClass(this, TableTag.class);
if (!doRender()) {
return SKIP_BODY;
}
doInitialization();
if ("AJAX".equals(parent.getLoadingType())) {
return SKIP_BODY;
}
return super.doStartTag();
}
private void doInitialization() throws JspException {
// Parent tag uses the 'title' attribute as the column title
// Get the label value like the current line in the roo tag files:
// <spring:message
// code="label_${fn:toLowerCase(fn:substringAfter(id,'_'))}" var="label"
// htmlEscape="false" />
String label = getLabel();
if (StringUtils.isEmpty(getTitle())) {
if (StringUtils.isEmpty(label)) {
String id = getId();
String code = "label".concat(id.substring(id.indexOf('_'))
.toLowerCase());
label = helper.resolveMessage(this.pageContext, code);
}
setTitle(label);
}
// Remove configuration of the property "sortInit" if it's empty.
if (StringUtils.isBlank((String) stagingConf
.get(Configuration.COLUMN_SORTINIT))) {
stagingConf.remove(Configuration.COLUMN_SORTINIT);
}
// Remove configuration of the property "sortDirection" if it's empty.
if (StringUtils.isBlank((String) stagingConf
.get(Configuration.COLUMN_SORTDIRECTION))) {
stagingConf.remove(Configuration.COLUMN_SORTDIRECTION);
}
}
@Override
protected String getColumnContent() throws JspException {
// Try to do the same as the roo table.tagx tag to get the value for the
// column
// <c:choose>
// <c:when test="${columnType eq 'date'}">
// <spring:escapeBody>
// <fmt:formatDate value="${item[column]}"
// pattern="${fn:escapeXml(columnDatePattern)}" var="colTxt" />
// </spring:escapeBody>
// </c:when>
// <c:when test="${columnType eq 'calendar'}">
// <spring:escapeBody>
// <fmt:formatDate value="${item[column].time}"
// pattern="${fn:escapeXml(columnDatePattern)}" var="colTxt"/>
// </spring:escapeBody>
// </c:when>
// <c:otherwise>
// <c:set var="colTxt">
// <spring:eval expression="item[column]" htmlEscape="false" />
// </c:set>
// </c:otherwise>
// </c:choose>
// <c:if test="${columnMaxLength ge 0}">
// <c:set value="${fn:substring(colTxt, 0, columnMaxLength)}"
// var="colTxt" />
// </c:if>
// <c:out value="${colTxt}" />
// TODO log problem resolving column content
if (StringUtils.isBlank(this.property)) {
return "";
}
TableTag parent = (TableTag) getParent();
ExpressionParser parser = new SpelExpressionParser();
String unescapedProperty = this.property.replace(
SEPARATOR_FIELDS_ESCAPED, SEPARATOR_FIELDS);
Expression exp = parser.parseExpression(unescapedProperty);
EvaluationContext context = new StandardEvaluationContext(
parent.getCurrentObject());
Object value = exp.getValue(context);
String result = "";
if (value != null) {
if (Date.class.isAssignableFrom(value.getClass())) {
result = dateTimePattern.format(value);
}
else if (value instanceof Calendar) {
result = dateTimePattern.format(((Calendar) value).getTime());
}
else {
ConversionService conversionService = (ConversionService) helper
.getBean(this.pageContext, getConversionServiceId());
if (conversionService != null
&& conversionService.canConvert(value.getClass(),
String.class)) {
result = conversionService.convert(value, String.class);
}
else {
result = ObjectUtils.getDisplayString(value);
}
// result = (isHtmlEscape() ? HtmlUtils.htmlEscape(result) :
// result);
// result = (this.javaScriptEscape ?
// JavaScriptUtils.javaScriptEscape(result) : result);
}
}
else {
result = super.getColumnContent();
}
if (maxLength >= 0 && result.length() > maxLength) {
result = result.substring(0, maxLength);
}
return result;
}
@Override
public int doEndTag() throws JspException {
if (!doRender()) {
return EVAL_PAGE;
}
return super.doEndTag();
}
@Override
public int doAfterBody() throws JspException {
if (!doRender()) {
return SKIP_BODY;
}
return super.doAfterBody();
}
public Integer getMaxLength() {
return maxLength;
}
public void setMaxLength(Integer maxLength) {
this.maxLength = maxLength;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public Boolean getDate() {
return date;
}
public void setDate(Boolean date) {
this.date = date;
}
public Boolean getCalendar() {
return calendar;
}
public void setCalendar(Boolean calendar) {
this.calendar = calendar;
}
public String getDateTimePattern() {
return dateTimePattern.toString();
}
public void setDateTimePattern(String dateTimePattern) {
if (StringUtils.isBlank(dateTimePattern)) {
this.dateTimePattern = SimpleDateFormat.getDateInstance();
return;
}
this.dateTimePattern = new SimpleDateFormat(dateTimePattern,
helper.getRequestLocale(this.pageContext));
}
public Boolean getRender() {
return render;
}
public void setRender(Boolean render) {
this.render = render;
}
public String getZ() {
return z;
}
public void setZ(String z) {
this.z = z;
}
public String getConversionServiceId() {
return conversionServiceId;
}
public void setConversionServiceId(String conversionServiceId) {
this.conversionServiceId = conversionServiceId;
}
/**
* TBC
*
* @param tag
* @param pageContext
* @return
*/
public Tag getParent() {
// If not found so we try to find in page context. Note RooTableTag
// must add the reference to itself in doStartTag() method
Tag parent = (Tag) pageContext.getAttribute(
RooTableTag.TABLE_TAG_VARIABLE, PageContext.REQUEST_SCOPE);
if (parent != null) {
return parent;
}
return super.getParent();
}
}