/*
* RHQ Management Platform
* Copyright (C) 2005-2011 Red Hat, Inc.
* All rights reserved.
*
* 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 version 2 of the License.
*
* 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.rhq.coregui.client.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.gwt.core.client.JavaScriptObject;
import com.smartgwt.client.data.Criteria;
import com.smartgwt.client.data.DSRequest;
import com.smartgwt.client.data.DSResponse;
import com.smartgwt.client.data.DataSource;
import com.smartgwt.client.data.DataSourceField;
import com.smartgwt.client.data.Record;
import com.smartgwt.client.data.SortSpecifier;
import com.smartgwt.client.data.fields.DataSourceIntegerField;
import com.smartgwt.client.data.fields.DataSourceTextField;
import com.smartgwt.client.rpc.RPCResponse;
import com.smartgwt.client.types.DSDataFormat;
import com.smartgwt.client.types.DSProtocol;
import com.smartgwt.client.types.SortDirection;
import com.smartgwt.client.util.JSOHelper;
import com.smartgwt.client.widgets.form.validator.IntegerRangeValidator;
import com.smartgwt.client.widgets.form.validator.LengthRangeValidator;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import org.rhq.core.domain.alert.AlertFilter;
import org.rhq.core.domain.alert.AlertPriority;
import org.rhq.core.domain.cloud.PartitionEvent.ExecutionStatus;
import org.rhq.core.domain.cloud.PartitionEventType;
import org.rhq.core.domain.cloud.Server;
import org.rhq.core.domain.cloud.StorageNode;
import org.rhq.core.domain.criteria.BaseCriteria;
import org.rhq.core.domain.drift.DriftCategory;
import org.rhq.core.domain.event.EventSeverity;
import org.rhq.core.domain.operation.OperationRequestStatus;
import org.rhq.core.domain.resource.ResourceCategory;
import org.rhq.core.domain.util.PageControl;
import org.rhq.core.domain.util.PageList;
import org.rhq.core.domain.util.PageOrdering;
import org.rhq.coregui.client.CoreGUI;
import org.rhq.coregui.client.Messages;
import org.rhq.coregui.client.components.table.Table;
import org.rhq.coregui.client.util.effects.ColoringUtility;
import org.rhq.coregui.client.util.enhanced.EnhancedUtility;
import org.rhq.coregui.client.util.message.Message;
import org.rhq.coregui.client.util.rpc.DataSourceResponseStatistics;
/**
* Base GWT-RPC oriented DataSource class.
*
* The <T> type is the entity POJO type that represents a record retrieved by the data source
* The <C> type is the criteria type that is used to fetch data from the data source
*
* @author Greg Hinkle
* @author Ian Springer
*/
public abstract class RPCDataSource<T, C extends BaseCriteria> extends DataSource {
protected static final Messages MSG = CoreGUI.getMessages();
private List<String> hightlightingFieldNames = new ArrayList<String>();
private Criteria previousCriteria;
private Integer dataPageSize;
public RPCDataSource() {
this(null);
}
public RPCDataSource(String name) {
if (name != null) {
Log.info("Trying to build DataSource: " + name);
setID(EnhancedUtility.getSafeId(name));
}
// TODO until http://code.google.com/p/smartgwt/issues/detail?id=490 is fixed always go to the server for data
setClientOnly(false);
setAutoCacheAllData(false);
setCacheAllData(false);
setDataProtocol(DSProtocol.CLIENTCUSTOM);
setDataFormat(DSDataFormat.CUSTOM);
setDataPageSize(50);
}
/**
* A pattern that can be used for Datasource subclassing. Each subclass can add its own fields prior to
* all of the fields being added to the datasource.
*/
protected List<DataSourceField> addDataSourceFields() {
return new ArrayList<DataSourceField>();
}
@Override
protected Object transformRequest(DSRequest request) {
try {
DSResponse response = new DSResponse();
response.setAttribute("clientContext", request.getAttributeAsObject("clientContext"));
// Assume success as the default.
response.setStatus(0);
switch (request.getOperationType()) {
case FETCH:
C criteria = getFetchCriteria(request);
if (criteria != null) {
// we are always going to supply a PageControl due to https://bugzilla.redhat.com/show_bug.cgi?id=682304.
// This can cause some subtle issues with Criteria handling, it's important that code overriding
// getFetchCriteria() take a close look at the jdoc.
if (criteria.getPageControlOverrides() == null) {
criteria.setPageControl(getPageControl(request));
}
if (Log.isDebugEnabled()) {
Log.debug(getClass().getName() + " using [" + criteria.getPageControlOverrides()
+ "] for fetch request.");
}
} else {
Log.warn(getClass().getName()
+ ".getFetchCriteria() returned null - no paging of results will be done.");
}
executeFetch(request, response, criteria);
break;
case ADD:
ListGridRecord newRecord = getDataObject(request);
executeAdd(newRecord, request, response);
break;
case UPDATE:
Record oldRecord = request.getOldValues(); // original values before the update
Record updatedRecord = getUpdatedRecord(request, oldRecord);
executeUpdate(updatedRecord, oldRecord, request, response);
break;
case REMOVE:
ListGridRecord deletedRecord = getDataObject(request);
executeRemove(deletedRecord, request, response);
break;
default:
super.transformRequest(request);
break;
}
} catch (Throwable t) {
CoreGUI.getErrorHandler().handleError(
MSG.dataSource_rpc_error_transformRequestFailure(request.getOperationType().name()), t);
return null;
}
return request.getData();
}
/**
* Returns the data page size that should be used for fetch requests, or null if results should not be paged.
* Default value is 50.
*
* @return the data page size that should be used for fetch requests, or null if results should not be paged
*/
public Integer getDataPageSize() {
return dataPageSize;
}
/**
* Sets the data page size that should be used for fetch requests, or null if results should not be paged.
* Default value is 50. Subclasses that wish to use a different value should call this method in their constructors.
*
* @param dataPageSize the data page size that should be used for fetch requests, or null if results should not be paged
*/
public void setDataPageSize(Integer dataPageSize) {
if (dataPageSize <= 0) {
throw new IllegalArgumentException("Page size must be greater than 0.");
}
this.dataPageSize = dataPageSize;
}
private Record getUpdatedRecord(DSRequest request, Record oldRecord) {
// Get changed values.
JavaScriptObject data = request.getData();
// Apply changes.
JSOHelper.apply(data, oldRecord.getJsObj());
return new ListGridRecord(data);
}
private static ListGridRecord getDataObject(DSRequest request) {
JavaScriptObject data = request.getData();
ListGridRecord newRecord = new ListGridRecord(data);
return newRecord;
}
/**
* Returns a prepopulated PageControl based on the provided DSRequest. This will set sort fields,
* pagination, but *not* filter fields.
*
* @param request the request to turn into a page control
* @return the page control for passing to criteria and other queries
*/
protected PageControl getPageControl(DSRequest request) {
if (previousCriteria != null && !CriteriaUtility.equals(request.getCriteria(), this.previousCriteria)) {
// The criteria has changed since the last fetch request - reset paging.
Log.debug("Resetting paging on " + getClass().getName() + "...");
request.setStartRow(0);
request.setEndRow(getDataPageSize());
}
this.previousCriteria = (request.getCriteria() != null) ? request.getCriteria() : new Criteria();
// Create PageControl and initialize paging.
PageControl pageControl;
if (request.getEndRow() == null) {
// A null endRow means no paging. However, there is a bug in the RHQ criteria API, where when an unlimited
// PageControl is used in combination with one or more join fetches, the results contain duplicates.
// So until that bug is fixed, always use paging.
//Log.debug("WARNING: " + getClass().getName() + " is not using paging for fetch request.");
//pageControl = PageControl.getUnlimitedInstance();
pageControl = PageControl.getExplicitPageControl(0, getDataPageSize());
} else {
int startRow = (request.getStartRow() != null) ? request.getStartRow() : 0;
int endRow = request.getEndRow();
pageControl = PageControl.getExplicitPageControl(startRow, (endRow - startRow));
}
// Initialize sorting.
initializeSorting(pageControl, request);
return pageControl;
}
private void initializeSorting(PageControl pageControl, DSRequest request) {
SortSpecifier[] sortSpecifiers = request.getSortBy();
if (sortSpecifiers != null) {
for (SortSpecifier sortSpecifier : sortSpecifiers) {
PageOrdering ordering = (sortSpecifier.getSortDirection() == SortDirection.ASCENDING) ? PageOrdering.ASC
: PageOrdering.DESC;
String columnName = sortSpecifier.getField();
String sortField = getSortFieldForColumn(columnName);
if (sortField != null) {
pageControl.addDefaultOrderingField(sortField, ordering);
} else {
Log.warn("Field [" + columnName + "] in [" + getClass().getName()
+ "] could not be mapped to a PageControl ordering field.");
}
}
}
}
/**
* By default, for a sortable column, the field name (usually a ListGridField name) is used as the
* OrderingField in the PageControl passed to the server. If for some reason the field name does not
* properly map to a sortable entity field, this method can be overridden to provide the proper mapping.
* Note that certain columns may also leverage sort fields in the underlying (rhq) criteria class itself, but
* not every sortable column may be appropriate as a sort field in the criteria.
*
* @param columnName The column field name
* @return The entity field for the desired sort. By default just returns back the columnName. Can be null if
* there is no valid mapping.
*/
protected String getSortFieldForColumn(String columnName) {
return columnName;
}
@Override
public void processResponse(String requestId, DSResponse responseProperties) {
super.processResponse(requestId, responseProperties);
DataSourceResponseStatistics.record(requestId, responseProperties);
}
protected void sendSuccessResponse(DSRequest request, DSResponse response, T dataObject) {
sendSuccessResponse(request, response, dataObject, null);
}
protected void sendSuccessResponse(DSRequest request, DSResponse response, Record record) {
sendSuccessResponse(request, response, record, null);
}
protected void sendSuccessResponse(DSRequest request, DSResponse response, T dataObject, Message message) {
sendSuccessResponse(request, response, dataObject, message, null);
}
protected void sendSuccessResponse(DSRequest request, DSResponse response, Record record, Message message) {
sendSuccessResponse(request, response, record, message, null);
}
protected void sendSuccessResponse(DSRequest request, DSResponse response, T dataObject, Message message,
String viewPath) {
Record record = copyValues(dataObject);
sendSuccessResponse(request, response, record, message, viewPath);
}
protected void sendSuccessResponse(DSRequest request, DSResponse response, Record record, Message message,
String viewPath) {
response.setStatus(RPCResponse.STATUS_SUCCESS);
response.setData(new Record[] { record });
processResponse(request.getRequestId(), response);
if (viewPath != null) {
CoreGUI.goToView(viewPath, message);
} else if (message != null) {
CoreGUI.getMessageCenter().notify(message);
}
}
protected void sendSuccessResponse(DSRequest request, DSResponse response, PageList<T> dataObjects) {
Record[] records = buildRecords(dataObjects);
PageList<Record> recordsPageList = new PageList<Record>(dataObjects.getPageControl());
recordsPageList.setTotalSize(dataObjects.getTotalSize());
recordsPageList.setUnbounded(dataObjects.isUnbounded());
recordsPageList.addAll(Arrays.asList(records));
sendSuccessResponseRecords(request, response, recordsPageList);
}
protected void sendSuccessResponseRecords(DSRequest request, DSResponse response, PageList<Record> records) {
response.setStatus(RPCResponse.STATUS_SUCCESS);
Record[] recordsArray = records.toArray(new Record[records.size()]);
response.setData(recordsArray);
setPagingInfo(response, records);
processResponse(request.getRequestId(), response);
}
protected void setPagingInfo(DSResponse response, PageList<?> pageList) {
// For paging to work, we have to specify size of full result set.
int totalRows = (pageList.isUnbounded()) ? pageList.size() : pageList.getTotalSize();
response.setTotalRows(totalRows);
// Also set start row and end row in case for some reason we're not returning the same page the ListGrid
// requested.
//PageControl pageControl = pageList.getPageControl();
//response.setStartRow(pageControl.getStartRow());
//response.setEndRow(pageControl.getStartRow() + pageControl.getPageSize());
}
protected void sendFailureResponse(DSRequest request, DSResponse response, String message, Throwable caught) {
CoreGUI.getErrorHandler().handleError(message, caught);
response.setStatus(RPCResponse.STATUS_FAILURE);
processResponse(request.getRequestId(), response);
}
protected void sendValidationErrorResponse(DSRequest request, DSResponse response, Map<String, String> errorMessages) {
response.setStatus(RPCResponse.STATUS_VALIDATION_ERROR);
response.setErrors(errorMessages);
processResponse(request.getRequestId(), response);
}
/**
* @deprecated use {@link #sendSuccessResponseRecords(DSRequest, DSResponse, PageList)} instead
*/
@Deprecated
protected void populateSuccessResponse(PageList<T> dataObjects, DSResponse response) {
response.setStatus(RPCResponse.STATUS_SUCCESS);
Record[] records = buildRecords(dataObjects);
response.setData(records);
setPagingInfo(response, dataObjects);
}
public ListGridRecord[] buildRecords(Collection<T> dataObjects) {
return buildRecords(dataObjects, true);
}
public ListGridRecord[] buildRecords(Collection<T> dataObjects, boolean cascade) {
if (dataObjects == null) {
return null;
}
ListGridRecord[] records = new ListGridRecord[dataObjects.size()];
int i = 0;
for (T item : dataObjects) {
records[i++] = copyValues(item, cascade);
}
return records;
}
public Set<T> buildDataObjects(Record[] records) {
if (records == null) {
return null;
}
Set<T> results = new LinkedHashSet<T>(records.length);
for (Record record : records) {
results.add(copyValues(record));
}
return results;
}
@Override
public void addField(DataSourceField field) throws IllegalStateException {
super.addField(field);
if ((field instanceof HighlightingDatasourceTextField) == false) {
return;
}
field.setHidden(true);
hightlightingFieldNames.add(field.getName());
String name = field.getName() + "-highlight";
String title = field.getTitle();
DataSourceTextField fieldToDisplayHighlighting = new DataSourceTextField(name, title);
super.addField(fieldToDisplayHighlighting);
}
@SuppressWarnings("unchecked")
protected void highlightFilterMatches(final DSRequest request, final Record[] records) {
Map<String, Object> criteriaMap = request.getCriteria().getValues();
for (String filterName : hightlightingFieldNames) {
String filterValue = (String) criteriaMap.get(filterName);
for (Record nextRecord : records) {
String originalData = nextRecord.getAttribute(filterName);
String decoratedData = (filterValue != null) ? ColoringUtility.highlight(originalData, filterValue)
: originalData;
nextRecord.setAttribute(filterName + "-highlight", decoratedData);
}
}
}
/**
* Given a request, this returns a criteria object that should be used to fetch data that the request
* is asking for. If a particular data source subclass does not use criteria, this can return <code>null</code>.
* <br/><br/>
* IMPORTANT! If the criteria returned does not include a PageControl it will be assigned the PageControl
* of the DSRequest. (See https://bugzilla.redhat.com/show_bug.cgi?id=682304 for more on why we do this.) This
* is important because it means that this criteria object will completely ignore calls made to
* setPaging(pageNumber, pageSize) as well as addSortField(fieldName), see
* {@link org.rhq.core.domain.criteria.Criteria#setPageControl(PageControl)}. So, when overriding this
* method, the preferred way to set specific paging or sorting is to manipulate the provided
* PageControl in the DSRequest. The DSRequest can be implicitly manipulated by using InitialSortSpecifiers in
* {@link Table} construction, or it can be manipulated manually. It is not necessary to call
* {@link org.rhq.core.domain.criteria.Criteria#setPageControl(PageControl)} since it will be applied
* automatically at fetch-time (by this class). To completely override the request's PageControl
* you can create a new one and set it on the Criteria, but remember that you may lose paging/sorting state.
*
* @param request the request being made for data
* @return a criteria object that is to be used when fetching for the requested data, or <code>null</code> if not used
*/
protected abstract C getFetchCriteria(final DSRequest request);
/**
* Extensions should implement this method to retrieve data. Paging solutions should use
* {@link #getPageControl(com.smartgwt.client.data.DSRequest)}. All implementations should call processResponse()
* whether they fail or succeed. Data should be set on the request via setData. Implementations can use
* buildRecords() to get the list of records.
*
* @param request
* @param response
* @param criteria can be used by the method to perform queries in order to fetch the required data
*/
protected abstract void executeFetch(final DSRequest request, final DSResponse response, final C criteria);
public abstract T copyValues(Record from);
/**
*
* @param from
* @return
*/
// TODO (ips): This really should return Records, rather than ListGridRecords, so the DataSource is not specific to
// ListGrids, but that will require a lot of refactoring at this point...
public abstract ListGridRecord copyValues(T from);
public ListGridRecord copyValues(T from, boolean cascade) {
return copyValues(from);
}
/**
* Executed on <code>REMOVE</code> operation. <code>processResponse (requestId, response)</code>
* should be called when operation completes (either successful or failure).
*
* @param recordToRemove
* @param request <code>DSRequest</code> being processed. <code>request.getData ()</code>
* contains record should be removed.
* @param response <code>DSResponse</code>. <code>setData (list)</code> should be called on
* successful execution of this method. Array should contain single element representing
*/
protected void executeRemove(Record recordToRemove, final DSRequest request, final DSResponse response) {
throw new UnsupportedOperationException("This dataSource does not support removals.");
}
/**
* TODO
*
* @param recordToAdd
* @param request
* @param response
*/
protected void executeAdd(Record recordToAdd, final DSRequest request, final DSResponse response) {
throw new UnsupportedOperationException("This dataSource does not support additions.");
}
/**
* TODO
*
* @param editedRecord
* @param oldRecord
* @param request
* @param response
*/
protected void executeUpdate(Record editedRecord, Record oldRecord, final DSRequest request,
final DSResponse response) {
throw new UnsupportedOperationException("This dataSource does not support updates.");
}
/**
* Add the specified fields to this data source. When the data source is associated with a
* {@link com.smartgwt.client.widgets.grid.ListGrid}, the fields will be displayed in the order they are specified
* here.
*
* @param fields the fields to be added
*/
public void addFields(List<DataSourceField> fields) {
for (DataSourceField field : fields) {
addField(field);
}
}
public void addFields(DataSourceField... fields) {
addFields(Arrays.asList(fields));
}
public static <S> S[] getArrayFilter(DSRequest request, String paramName, Class<S> type) {
return getArrayFilter(request.getCriteria(), paramName, type);
}
@SuppressWarnings("unchecked")
public static <S> S[] getArrayFilter(Criteria criteria, String paramName, Class<S> type) {
Map<String, Object> criteriaMap = criteria.getValues();
S[] resultArray;
Object value = criteriaMap.get(paramName);
if (value == null) {
resultArray = null;
} else if (type == Integer.class) {
int[] intermediates;
if (isArray(value)) {
intermediates = criteria.getAttributeAsIntArray(paramName);
} else { // want array return, but only single instance of the type in the request
intermediates = new int[] { criteria.getAttributeAsInt(paramName) };
}
resultArray = (S[]) new Integer[intermediates.length];
int index = 0;
for (int next : intermediates) {
resultArray[index++] = (S) Integer.valueOf(next);
}
} else if (type == String.class) {
String[] intermediates;
if (isArray(value)) {
intermediates = criteria.getAttributeAsStringArray(paramName);
} else { // want array return, but only single instance of the type in the request
intermediates = new String[] { criteria.getAttributeAsString(paramName) };
}
resultArray = (S[]) new String[intermediates.length];
int index = 0;
for (String next : intermediates) {
resultArray[index++] = (S) next;
}
} else if (type.isEnum()) {
String[] intermediates;
if (isArray(value)) {
intermediates = criteria.getAttributeAsStringArray(paramName);
} else { // want array return, but only single instance of the type in the request
intermediates = new String[] { criteria.getAttributeAsString(paramName) };
}
List<S> buffer = new ArrayList<S>();
for (String next : intermediates) {
buffer.add((S) Enum.valueOf((Class<? extends Enum>)type, next));
}
resultArray = buffer.toArray(getEnumArray(type, buffer.size()));
} else {
throw new IllegalArgumentException(MSG.dataSource_rpc_error_unsupportedArrayFilterType(type.getName()));
}
if (Log.isDebugEnabled() && resultArray != null) {
Log.debug("Array filter: " + paramName + "=[" + Arrays.toString(resultArray) + "]");
}
return resultArray;
}
private static boolean isArray(Object value) {
return value.getClass().isArray() || value.getClass().equals(ArrayList.class);
}
@SuppressWarnings("unchecked")
private static <S> S[] getEnumArray(Class<S> genericEnumType, int size) {
// workaround until GWT implements reflection APIs, so we can do:
// array=(S[])Array.newInstance(Class<S>,capacity);
if (genericEnumType == AlertPriority.class) {
return (S[]) new AlertPriority[size];
} else if (genericEnumType == EventSeverity.class) {
return (S[]) new EventSeverity[size];
} else if (genericEnumType == OperationRequestStatus.class) {
return (S[]) new OperationRequestStatus[size];
} else if (genericEnumType == ResourceCategory.class) {
return (S[]) new ResourceCategory[size];
} else if (genericEnumType == DriftCategory.class) {
return (S[]) new DriftCategory[size];
} else if (genericEnumType == ExecutionStatus.class) {
return (S[]) new ExecutionStatus[size];
} else if (genericEnumType == PartitionEventType.class) {
return (S[]) new PartitionEventType[size];
} else if (genericEnumType == Server.OperationMode.class) {
return (S[]) new Server.OperationMode[size];
} else if (genericEnumType == StorageNode.OperationMode.class) {
return (S[]) new StorageNode.OperationMode[size];
} else if (genericEnumType == AlertFilter.class) {
return (S[]) new AlertFilter[size];
} else {
throw new IllegalArgumentException(MSG.dataSource_rpc_error_unsupportedEnumType(genericEnumType.getName()));
}
}
@SuppressWarnings("unchecked")
public static void printRequestCriteria(DSRequest request) {
Criteria criteria = request.getCriteria();
Map<String, Object> criteriaMap = criteria.getValues();
for (Map.Entry<String, Object> nextEntry : criteriaMap.entrySet()) {
Log.debug("Request Criteria: " + nextEntry.getKey() + ":" + nextEntry.getValue());
}
}
public static <S> S getFilter(DSRequest request, String paramName, Class<S> type) {
return getFilter(request.getCriteria(), paramName, type);
}
@SuppressWarnings("unchecked")
public static <S> S getFilter(Criteria criteria, String paramName, Class<S> type) {
Map<String, Object> criteriaMap = (criteria != null) ? criteria.getValues() : Collections
.<String, Object> emptyMap();
S result;
Object value = criteriaMap.get(paramName);
if (value == null || value.toString().equals("")) {
result = null;
} else {
String strValue = value.toString();
if (type == String.class) {
result = (S) strValue;
} else if (type == Integer.class) {
result = (S) Integer.valueOf(strValue);
} else if (type == Long.class) {
result = (S) Long.valueOf(strValue);
} else if (type.isEnum()) {
result = (S) Enum.valueOf((Class<? extends Enum>) type, strValue);
} else {
result = (S) value; // otherwise presume the object is already that type, and just cast it
}
}
//if (Log.isDebugEnabled() && result != null) {
Log.debug("Filter: " + paramName + "=[" + result + "]");
//}
return result;
}
protected static DataSourceTextField createTextField(String name, String title, Integer minLength,
Integer maxLength, Boolean required) {
DataSourceTextField textField = new DataSourceTextField(name, title);
textField.setLength(maxLength);
textField.setRequired(required);
if (minLength != null || maxLength != null) {
LengthRangeValidator lengthRangeValidator = new LengthRangeValidator();
lengthRangeValidator.setMin(minLength);
lengthRangeValidator.setMax(maxLength);
textField.setValidators(lengthRangeValidator);
}
return textField;
}
protected static DataSourceTextField createBooleanField(String name, String title, Boolean required) {
DataSourceTextField textField = new DataSourceTextField(name, title);
textField.setLength(Boolean.FALSE.toString().length());
textField.setRequired(required);
LinkedHashMap<String, String> valueMap = new LinkedHashMap<String, String>();
valueMap.put(Boolean.TRUE.toString(), MSG.common_val_yes_lower());
valueMap.put(Boolean.FALSE.toString(), MSG.common_val_no_lower());
textField.setValueMap(valueMap);
return textField;
}
protected static DataSourceIntegerField createIntegerField(String name, String title, Integer minValue,
Integer maxValue, Boolean required) {
DataSourceIntegerField textField = new DataSourceIntegerField(name, title);
textField.setRequired(required);
if (minValue != null || maxValue != null) {
IntegerRangeValidator integerRangeValidator = new IntegerRangeValidator();
if (minValue != null) {
integerRangeValidator.setMin(minValue);
} else {
integerRangeValidator.setMin(Integer.MIN_VALUE);
}
if (maxValue != null) {
integerRangeValidator.setMax(maxValue);
} else {
integerRangeValidator.setMax(Integer.MAX_VALUE);
}
textField.setValidators(integerRangeValidator);
}
return textField;
}
protected static Date convertTimestampToDate(Long timestamp) {
if (timestamp == null) {
return null;
}
// Assume 0 means null, not Jan 1, 1970.
return (timestamp != 0) ? new Date(timestamp) : null;
}
}