(m_rowCount+10);
// actual row count or timeout
if (m_rowCount > 0 || m_rowCountTimeout)
{
m_loader.setContext(ServerContext.getCurrentInstance());
m_loaderFuture = Adempiere.getThreadPoolExecutor().submit(m_loader);
}
else
m_loader.close();
//
m_changed = false;
m_rowChanged = -1;
m_inserting = false;
return true;
} // open
/**
* Verify whether use of virtual buffer is supported
*/
private void verifyVirtual()
{
if (m_indexKeyColumn == -1)
{
m_virtual = false;
return;
}
GridField[] fields = getFields();
for(int i = 0; i < fields.length; i++)
{
if (fields[i].isKey() && i != m_indexKeyColumn)
{
m_virtual = false;
return;
}
}
}
/**
* Wait until asynchronous loading of Table and Lookup Fields is complete.
*/
public void loadComplete()
{
// Wait for loader
if (m_loaderFuture != null)
{
if (!m_loaderFuture.isDone())
{
try
{
m_loaderFuture.get();
}
catch (Exception ie)
{
log.log(Level.SEVERE, "Interrupted", ie);
}
}
}
// wait for field lookup loaders
for (int i = 0; i < m_fields.size(); i++)
{
GridField field = (GridField)m_fields.get(i);
field.lookupLoadComplete();
}
} // loadComplete
/**
* Is Loading
* @return true if loading is in progress
*/
public boolean isLoading()
{
if (m_loaderFuture != null && !m_loaderFuture.isDone())
return true;
return false;
} // isLoading
/**
* wait for the loading of data
* @param timeout timeout in milisecond. pass 0 or negative value for infinite wait
* @throws InterruptedException
* @throws ExecutionException
* @throws TimeoutException
*/
public void waitLoading(long timeout) throws InterruptedException, ExecutionException, TimeoutException
{
if (m_loaderFuture != null && !m_loaderFuture.isDone()) {
if (timeout > 0)
m_loaderFuture.get(timeout, TimeUnit.MILLISECONDS);
else
m_loaderFuture.get();
}
}
/**
* Is it open?
* @return true if opened
*/
public boolean isOpen()
{
return m_open;
} // isOpen
/**
* Close Resultset
* @param finalCall true for final call and perform clean up
*/
public void close (boolean finalCall)
{
if (!m_open)
return;
if (log.isLoggable(Level.FINE)) log.fine("final=" + finalCall);
// remove listeners
if (finalCall)
{
DataStatusListener evl[] = (DataStatusListener[])listenerList.getListeners(DataStatusListener.class);
for (int i = 0; i < evl.length; i++)
listenerList.remove(DataStatusListener.class, evl[i]);
TableModelListener ev2[] = (TableModelListener[])listenerList.getListeners(TableModelListener.class);
for (int i = 0; i < ev2.length; i++)
listenerList.remove(TableModelListener.class, ev2[i]);
VetoableChangeListener vcl[] = m_vetoableChangeSupport.getVetoableChangeListeners();
for (int i = 0; i < vcl.length; i++)
m_vetoableChangeSupport.removeVetoableChangeListener(vcl[i]);
}
// Stop loader
while (m_loaderFuture != null && !m_loaderFuture.isDone())
{
if (log.isLoggable(Level.FINE))
log.fine("Interrupting Loader ...");
m_loaderFuture.cancel(true);
try
{
Thread.sleep(200); // .2 second
}
catch (InterruptedException ie)
{}
m_loaderFuture = null;
}
if (!m_inserting)
dataSave(false); // not manual
if (m_buffer != null)
{
m_buffer.clear();
}
if (m_sort != null)
{
m_sort.clear();
}
if (m_virtualBuffer != null)
{
m_virtualBuffer.clear();
}
if (finalCall) {
dispose();
m_buffer = null;
m_sort = null;
m_virtualBuffer = null;
}
// Fields are disposed from MTab
if (log.isLoggable(Level.FINE))
log.fine("");
m_open = false;
} // close
/**
* Clean up.
* Called by close-final.
*/
private void dispose()
{
// GridFields
for (int i = 0; i < m_fields.size(); i++)
((GridField)m_fields.get(i)).dispose();
m_fields.clear();
m_fields = null;
//
m_vetoableChangeSupport = null;
//
m_parameterSELECT.clear();
m_parameterSELECT = null;
m_parameterWHERE.clear();
m_parameterWHERE = null;
// clear data arrays
m_buffer = null;
m_virtualBuffer = null;
m_sort = null;
m_rowData = null;
m_oldValue = null;
m_loader = null;
m_loaderFuture = null;
} // dispose
/**
* Get column count
* @return column count
*/
public int getColumnCount()
{
return m_fields.size();
} // getColumnCount
/**
* Get field count
* @return field count
*/
public int getFieldCount()
{
return m_fields.size();
} // getFieldCount
/**
* Return number of rows
* @return Number of rows or 0 if not opened
*/
public int getRowCount()
{
return m_rowCount;
} // getRowCount
/**
* Set the Column to determine the color of the row
* @param columnName column name
*/
public void setColorColumn (String columnName)
{
m_indexColorColumn = findColumn(columnName);
} // setColorColumn
/**
* Get ColorCode for Row.
*
* If numerical value in compare column is
* negative = -1,
* positive = 1,
* otherwise = 0
*
* @see #setColorColumn
* @param row row
* @return color code
*/
public int getColorCode (int row)
{
if (m_indexColorColumn == -1)
return 0;
Object data = getValueAt(row, m_indexColorColumn);
// We need to have a Number
if (data == null || !(data instanceof BigDecimal))
return 0;
BigDecimal bd = (BigDecimal)data;
return bd.signum();
} // getColorCode
/**
* Sort records by Column.
*
* Actually the rows are not sorted, just the access pointer ArrayList
* with the same size as m_buffer ({@link #m_sort}).
* @param col col
* @param ascending ascending
*/
public void sort (int col, boolean ascending)
{
if (log.isLoggable(Level.INFO)) log.info("#" + col + " " + ascending);
if (col < 0) {
return;
}
if (getRowCount() == 0)
return;
GridField field = getField(col);
// Ignoring new record while sorting
if (field.getGridTab().isQuickForm())
dataIgnore();
boolean isSameSortEntries = (col == m_lastSortColumnIndex && ascending == m_lastSortedAscending);
if (!isSameSortEntries)
{
m_lastSortColumnIndex = col;
m_lastSortedAscending = ascending;
}
//cache changed row
MSort changedRow = m_rowChanged >= 0 ? (MSort)m_sort.get(m_rowChanged) : null;
if (m_rowChanged == m_newRow)
changedRow = null;
Object[] changedRowData = changedRow != null ? getDataAtRow(m_rowChanged) : null;
MSort newRow = m_newRow >= 0 ? (MSort)m_sort.get(m_newRow) : null;
MSort currentRow = m_currentRow >= 0 && m_currentRow < m_sort.size() ? (MSort)m_sort.get(m_currentRow) : null;
// RowIDs are not sorted
if (field.getDisplayType() == DisplayType.RowID)
return;
boolean isLookup = DisplayType.isLookup(field.getDisplayType());
boolean isASI = DisplayType.PAttribute == field.getDisplayType();
// fill MSort entities with data entity
for (int i = 0; i < m_sort.size(); i++)
{
MSort sort = (MSort)m_sort.get(i);
Object[] rowData = getDataAtRow(i);
if (rowData[col] == null)
sort.data = null;
else if (isLookup || isASI)
sort.data = field.getLookup().getDisplay(rowData[col]); // lookup
else
sort.data = rowData[col]; // data
}
if (log.isLoggable(Level.INFO)) log.info(field.toString() + " #" + m_sort.size());
// sort it
MSort sort = new MSort(0, null);
sort.setSortAsc(ascending);
Collections.sort(m_sort, sort);
if (m_virtual)
{
Object[] newRowData = newRow != null ? m_virtualBuffer.get(NEW_ROW_ID) : null;
m_virtualBuffer.clear();
if (newRow != null)
m_virtualBuffer.put(NEW_ROW_ID, newRowData);
if (changedRow != null)
{
for(int i = 0; i < m_sort.size(); i++)
{
if (m_sort.get(i) == changedRow)
{
m_rowChanged = i;
m_virtualBuffer.put(changedRow.index, changedRowData);
break;
}
}
}
//release sort memory
for (int i = 0; i < m_sort.size(); i++)
{
m_sort.get(i).data = null;
if (newRow != null && m_sort.get(i) == newRow)
{
if (m_rowChanged == m_newRow)
m_rowChanged = i;
m_newRow = i;
}
if (currentRow != null && m_sort.get(i) == currentRow)
m_currentRow = i;
}
}
else
{
for (int i = 0; i < m_sort.size(); i++)
{
if (newRow != null && m_sort.get(i) == newRow)
{
if (m_rowChanged == m_newRow)
m_rowChanged = i;
m_newRow = i;
}
if (currentRow != null && m_sort.get(i) == currentRow)
m_currentRow = i;
}
}
if (!isSameSortEntries)
{
// Info detected by MTab.dataStatusChanged and current row set to 0
fireDataStatusIEvent(SORTED_DSE_EVENT, "#" + m_sort.size());
// update UI
fireTableDataChanged();
}
} // sort
/**
* Get Key ID or -1 of none
* @param row row index
* @return ID or -1
*/
public int getKeyID (int row)
{
if (m_indexKeyColumn != -1)
{
try
{
Integer ii = (Integer)getValueAt(row, m_indexKeyColumn);
if (ii == null)
return -1;
return ii.intValue();
}
catch (Exception e) // Alpha Key
{
return -1;
}
}
return -1;
} // getKeyID
/**
* Get Key UUID or null of none
* @param row row index
* @return UUID or null
*/
public String getKeyUUID (int row)
{
if (m_indexUUIDColumn != -1)
{
try
{
String ii = (String)getValueAt(row, m_indexUUIDColumn);
if (ii == null)
return null;
return ii;
}
catch (Exception e)
{
return null;
}
}
return null;
} // getKeyUUID
/**
* Get UUID or null of none
* @param row row index
* @return UUID or null
*/
public UUID getUUID (int row)
{
String keyUUID = getKeyUUID(row);
if (keyUUID != null)
return UUID.fromString(keyUUID);
return null;
} // getUUID
/**
* Get Key ColumnName
* @return key column name
*/
public String getKeyColumnName()
{
if (m_indexKeyColumn != -1)
return getColumnName(m_indexKeyColumn);
return "";
} // getKeyColumnName
/**
* Get Value at row and column
* @param row row
* @param col col
* @return Value at row/column
*/
public Object getValueAt (int row, int col)
{
if (!m_open || row < 0 || col < 0 || row >= m_rowCount)
{
return null;
}
waitLoadingForRow(row);
// empty buffer
if (row >= m_sort.size())
{
return null;
}
// return Data item
Object[] rowData = getDataAtRow(row);
// out of bounds
if (rowData == null || col > rowData.length)
{
return null;
}
return rowData[col];
} // getValueAt
/**
* wait for loading of row
* @param row
*/
public void waitLoadingForRow(int row) {
// need to wait for data read into buffer
int loops = 0;
//wait for [timeout] seconds
int timeout = MSysConfig.getIntValue(MSysConfig.GRIDTABLE_LOAD_TIMEOUT_IN_SECONDS, DEFAULT_GRIDTABLE_LOAD_TIMEOUT_IN_SECONDS, Env.getAD_Client_ID(Env.getCtx()));
while (row >= m_sort.size() && m_loaderFuture != null && !m_loaderFuture.isDone() && loops < timeout)
{
if (log.isLoggable(Level.FINE)) log.fine("Waiting for loader row=" + row + ", size=" + m_sort.size());
try
{
m_loaderFuture.get(1000, TimeUnit.MILLISECONDS);
}
catch (Exception ie)
{}
loops++;
}
if (m_sort.size() == 0) {
// check if there is a DB error saved to show
Exception savedEx = CLogger.retrieveException();
if (savedEx != null)
throw new IllegalStateException(savedEx);
}
// zero rows found without load timeout
if (row == 0 && m_sort.size() == 0 && m_rowCountTimeout && !m_rowLoadTimeout)
throw new AdempiereException(Msg.getMsg(Env.getCtx(), "FindZeroRecords"));
if (row >= m_sort.size()) {
log.warning("Reached " + timeout + " seconds timeout loading row " + (row+1) + " for SQL=" + m_SQL);
//adjust row count
m_rowCount = m_sort.size();
throw new AdempiereException(Msg.getMsg(Env.getCtx(), LOAD_TIMEOUT_ERROR_MESSAGE));
}
}
/**
* @param row row index
* @return data at row index
*/
private Object[] getDataAtRow(int row)
{
return getDataAtRow(row, true);
}
/**
* @param row row index
* @param fetchIfNotFound
* @return data at row index
*/
private Object[] getDataAtRow(int row, boolean fetchIfNotFound)
{
waitLoadingForRow(row);
MSort sort = (MSort)m_sort.get(row);
Object[] rowData = null;
if (m_virtual)
{
if (sort.index != NEW_ROW_ID && !(m_virtualBuffer.containsKey(sort.index)) && fetchIfNotFound)
{
fillBuffer(row, DEFAULT_FETCH_SIZE);
}
rowData = (Object[])m_virtualBuffer.get(sort.index);
}
else
{
rowData = (Object[])m_buffer.get(sort.index);
}
return rowData;
}
/**
* @param row row index
* @param rowData
*/
private void setDataAtRow(int row, Object[] rowData) {
MSort sort = m_sort.get(row);
if (m_virtual)
{
if (sort.index != NEW_ROW_ID && !(m_virtualBuffer.containsKey(sort.index)))
{
fillBuffer(row, DEFAULT_FETCH_SIZE);
}
m_virtualBuffer.put(sort.index, rowData);
}
else
{
m_buffer.set(sort.index, rowData);
}
}
/**
* Fill virtual buffer ({@link #m_virtualBuffer}.
* @param start
* @param fetchSize
*/
private void fillBuffer(int start, int fetchSize)
{
//adjust start if needed
if (start > 0)
{
if (start + fetchSize >= m_sort.size())
{
start = start - (fetchSize - ( m_sort.size() - start ));
if (start < 0)
start = 0;
}
}
StringBuilder sql = new StringBuilder();
sql.append(m_SQL_Select)
.append(" WHERE ")
.append(getKeyColumnName())
.append(" IN (");
Maprowmap = new LinkedHashMap(DEFAULT_FETCH_SIZE);
int count = 0;
for(int i = start; i < start+fetchSize && i < m_sort.size(); i++)
{
if (m_sort.get(i).index == NEW_ROW_ID)
continue;
if(count > 0)
sql.append(",");
sql.append(m_sort.get(i).index);
rowmap.put(m_sort.get(i).index, i);
count++;
}
sql.append(")");
Object[] newRow = m_virtualBuffer.get(NEW_ROW_ID);
//cache changed row
Object[] changedRow = m_rowChanged >= 0 ? getDataAtRow(m_rowChanged, false) : null;
m_virtualBuffer = new HashMap(210);
if (newRow != null && newRow.length > 0)
m_virtualBuffer.put(NEW_ROW_ID, newRow);
PreparedStatement stmt = null;
ResultSet rs = null;
m_rowLoadTimeout = false;
try
{
stmt = DB.prepareStatement(sql.toString(), null);
int timeout = MSysConfig.getIntValue(MSysConfig.GRIDTABLE_LOAD_TIMEOUT_IN_SECONDS, DEFAULT_GRIDTABLE_LOAD_TIMEOUT_IN_SECONDS, Env.getAD_Client_ID(Env.getCtx()));
if (timeout > 0)
stmt.setQueryTimeout(timeout);
rs = stmt.executeQuery();
while(rs.next())
{
Object[] data = readData(rs);
rowmap.remove(data[m_indexKeyColumn]);
m_virtualBuffer.put((Integer)data[m_indexKeyColumn], data);
}
if (!rowmap.isEmpty())
{
List toremove = new ArrayList();
for(Map.Entry entry : rowmap.entrySet())
{
toremove.add(entry.getValue());
}
Collections.reverse(toremove);
for(Integer row : toremove)
{
m_sort.remove(row.intValue());
}
}
if (changedRow != null && changedRow.length > 0)
{
if (changedRow[m_indexKeyColumn] != null && (Integer)changedRow[m_indexKeyColumn] > 0)
{
m_virtualBuffer.put((Integer)changedRow[m_indexKeyColumn], changedRow);
}
}
}
catch (SQLException e)
{
if (DB.getDatabase().isQueryTimeout(e))
m_rowLoadTimeout = true;
log.log(Level.SEVERE, e.getLocalizedMessage(), e);
}
finally
{
DB.close(rs, stmt);
}
}
/**
* Indicate that there will be a change
* @param changed changed
*/
public void setChanged (boolean changed)
{
// Can we edit?
if (!m_open || m_readOnly)
return;
// Indicate Change
m_changed = changed;
if (!changed)
m_rowChanged = -1;
} // setChanged
/**
* Set value at row and column
*
* @param value value to assign to cell
* @param row row index of cell
* @param col column index of cell
*/
public final void setValueAt (Object value, int row, int col)
{
setValueAt (value, row, col, false, false);
} // setValueAt
/**
* call {@link #setValueAt(Object, int, int, boolean, boolean)} with isInitEdit = false
*
* @param value value to assign to cell
* @param row row index of cell
* @param col column index of cell
* @param force force setting new value
*/
public final void setValueAt (Object value, int row, int col, boolean force)
{
setValueAt (value, row, col, force, false);
} // setValueAt
/**
* Set value in row data and update GridField.
*
* @param value value to assign to cell
* @param row row index of cell
* @param col column index of cell
* @param force force setting new value
* @param isInitEdit indicate event rise by start edit a field. just want change status to edit, don't change anything else
*/
public final void setValueAt (Object value, int row, int col, boolean force, boolean isInitEdit)
{
// Can we edit?
if (!m_open || m_readOnly // not accessible
|| row < 0 || col < 0 // invalid index
|| m_rowCount == 0 // no rows
|| row >= m_rowCount ) //invalid row
{
if (log.isLoggable(Level.FINEST)) log.finest("r=" + row + " c=" + col + " - R/O=" + m_readOnly + ", Rows=" + m_rowCount + " - Ignored");
return;
}
dataSave(row, false);
// Has anything changed?
Object oldValue = getValueAt(row, col);
if (!force && !isInitEdit && !isValueChanged(oldValue, value) )
{
if (log.isLoggable(Level.FINEST)) log.finest("r=" + row + " c=" + col + " - New=" + value + "==Old=" + oldValue + " - Ignored");
return;
}
if (log.isLoggable(Level.FINE)) log.fine("r=" + row + " c=" + col + " = " + value + " (" + oldValue + ")");
// Save old value
m_oldValue = new Object[3];
m_oldValue[0] = Integer.valueOf(row);
m_oldValue[1] = Integer.valueOf(col);
m_oldValue[2] = oldValue;
// Set Data item
Object[] rowData = getDataAtRow(row);
m_rowChanged = row;
// save original value - shallow copy
if (m_rowData == null)
{
int size = m_fields.size();
m_rowData = new Object[size];
for (int i = 0; i < size; i++)
m_rowData[i] = rowData[i];
}
// save & update
rowData[col] = value;
setDataAtRow(row, rowData);
// update Table
fireTableCellUpdated(row, col);
// update GridField
GridField field = getField(col);
field.setValue(value, m_inserting);
// inform
DataStatusEvent evt = createDSE();
evt.setIsInitEdit(isInitEdit);
evt.setChangedColumn(col, field.getColumnName());
fireDataStatusChanged(evt);
} // setValueAt
/**
* Get Old Value
* @param row row
* @param col col
* @return old value
*/
public Object getOldValue (int row, int col)
{
if (m_oldValue == null)
return null;
if (((Integer)m_oldValue[0]).intValue() == row
&& ((Integer)m_oldValue[1]).intValue() == col)
return m_oldValue[2];
return null;
} // getOldValue
/**
* Check if {@link #m_rowChanged} needs to be saved.
* @param onlyRealChange if true the value of a field was actually changed
* (e.g. for new records, which have not been changed) - default false
* @return true if needs to be saved
*/
public boolean needSave(boolean onlyRealChange)
{
return needSave(m_rowChanged, onlyRealChange);
} // needSave
/**
* Check if {@link #m_rowChanged} needs to be saved.
* @return true if needs to be saved
*/
public boolean needSave()
{
return needSave(m_rowChanged, false);
} // needSave
/**
* Check if newRow needs to be saved.
* @param newRow to check
* @return true if needs to be saved
*/
public boolean needSave(int newRow)
{
return needSave(newRow, false);
} // needSave
/**
* Check if the row needs to be saved.
* - only when row changed
* - only if nothing was changed
* @param newRow to check
* @param onlyRealChange if true, only if the value of a field was actually changed
* (e.g. for new record with default value, which have not been changed) - default false
* @return true it needs to be saved
*/
public boolean needSave(int newRow, boolean onlyRealChange)
{
if (log.isLoggable(Level.FINE))
log.fine("Row=" + newRow +
", Changed=" + m_rowChanged + "/" + m_changed); // m_rowChanged set in setValueAt
// nothing done
if (!m_changed && m_rowChanged == -1)
return false;
// E.g. New unchanged records
if (m_changed && m_rowChanged == -1 && onlyRealChange)
return false;
// same row
if (newRow == m_rowChanged)
return false;
return true;
} // needSave
/*************************************************************************/
/** Save OK - O */
public static final char SAVE_OK = 'O'; // the only OK condition
/** Save Error - E */
public static final char SAVE_ERROR = 'E';
/** Save Access Error - A */
public static final char SAVE_ACCESS = 'A';
/** Save Mandatory Error - M */
public static final char SAVE_MANDATORY = 'M';
/** Save Abort Error - U */
public static final char SAVE_ABORT = 'U';
/**
* Check if it needs to be saved and save it.
* @param newRow row index
* @param manualCmd true if initiated from user action
* @return true if not needed to be saved or successful saved
*/
public boolean dataSave (int newRow, boolean manualCmd)
{
if (log.isLoggable(Level.FINE)) log.fine("Row=" + newRow +
", Changed=" + m_rowChanged + "/" + m_changed); // m_rowChanged set in setValueAt
// nothing done
if (!m_changed && m_rowChanged == -1)
return true;
// same row, don't save yet
if (newRow == m_rowChanged)
return true;
return (dataSave(manualCmd) == SAVE_OK);
} // dataSave
/**
* Save changes.
* @param manualCmd if true (i.e initiated from user action), no vetoable PropertyChange will be fired for save confirmation
* @return OK Or Error condition
* Error info (Access*, FillMandatory, SaveErrorNotUnique,
* SaveErrorRowNotFound, SaveErrorDataChanged) is saved in the log
*/
public char dataSave (boolean manualCmd)
{
// cannot save
if (!m_open)
{
log.warning ("Error - Open=" + m_open);
return SAVE_ERROR;
}
// no need - not changed - row not positioned - no Value changed
if (m_rowChanged == -1)
{
if (log.isLoggable(Level.CONFIG)) log.config("NoNeed - Changed=" + m_changed + ", Row=" + m_rowChanged);
if (!manualCmd)
return SAVE_OK;
}
// Value not changed
if (m_rowData == null)
{
//reset out of sync variable
m_rowChanged = -1;
if (log.isLoggable(Level.FINE))
log.fine("No Changes");
return SAVE_ERROR;
}
if (m_readOnly)
// If Processed - not editable (Find always editable) -> ok for changing payment terms, etc.
{
if (log.isLoggable(Level.WARNING))
log.warning("IsReadOnly - ignored");
dataIgnore();
return SAVE_ACCESS;
}
// row not positioned - no Value changed
if (m_rowChanged == -1)
{
if (m_newRow != -1) // new row and nothing changed - might be OK
m_rowChanged = m_newRow;
else
{
fireDataStatusEEvent("SaveErrorNoChange", "", true);
return SAVE_ERROR;
}
}
// Can we change?
int[] co = getClientOrg(m_rowChanged);
int AD_Client_ID = co[0];
int AD_Org_ID = co[1];
if (!MRole.getDefault(m_ctx, false).canUpdate(AD_Client_ID, AD_Org_ID, m_AD_Table_ID, 0, true))
{
fireDataStatusEEvent(CLogger.retrieveError());
dataIgnore();
return SAVE_ACCESS;
}
if (log.isLoggable(Level.INFO)) log.info("Row=" + m_rowChanged);
// inform about data save action, if not manually initiated
try
{
if (!manualCmd)
m_vetoableChangeSupport.fireVetoableChange(PROPERTY, -1, m_rowChanged);
}
catch (PropertyVetoException pve)
{
log.warning(pve.getMessage());
return SAVE_ABORT;
}
// get updated row data
Object[] rowData = getDataAtRow(m_rowChanged);
// Check Mandatory
String missingColumns = getMandatory(rowData);
if (missingColumns.length() != 0)
{
fireDataStatusEEvent("FillMandatory", missingColumns + "\n", true);
return SAVE_MANDATORY;
}
/**
* Update row *****
*/
int Record_ID = 0;
if (!m_inserting)
Record_ID = getKeyID(m_rowChanged);
try
{
return dataSavePO (Record_ID);
}
catch (Throwable e)
{
if (e instanceof ClassNotFoundException)
log.warning(m_tableName + " - " + e.getLocalizedMessage());
else
{
log.log(Level.SEVERE, "Persistency Issue - "
+ m_tableName + ": " + e.getLocalizedMessage(), e);
log.saveError("Error", e.getLocalizedMessage());
}
}
return SAVE_ERROR;
} // dataSave
/**
* Save via PO
* @param Record_ID
* @return SAVE_ERROR or SAVE_OK
* @throws Exception
*/
private char dataSavePO (int Record_ID) throws Exception
{
if (log.isLoggable(Level.FINE)) log.fine("ID=" + Record_ID);
//
Object[] rowData = getDataAtRow(m_rowChanged);
//
MTable table = MTable.get (m_ctx, m_AD_Table_ID);
PO po = null;
if (! m_importing) // Just use trx when importing
m_trxName = null;
if (Record_ID != -1)
{
if (Record_ID == 0 && !m_inserting && MTable.isZeroIDTable(table.getTableName())) {
String uuidFromZeroID = table.getUUIDFromZeroID();
po = table.getPOByUU(uuidFromZeroID, m_trxName);
} else {
po = table.getPO(Record_ID, m_trxName);
}
}
else // Multi - Key
po = table.getPO(getWhereClause(rowData), m_trxName);
// No Persistent Object
if (po == null)
throw new ClassNotFoundException ("No Persistent Object");
if (!po.is_new())
{
if (hasChanged(po))
{
// return error stating that current record has changed and it cannot be saved
String adMessage = "CurrentRecordModified";
String msg = Msg.getMsg(Env.getCtx(), adMessage);
fireDataStatusEEvent(adMessage, msg, true);
return SAVE_ERROR;
}
}
int size = m_fields.size();
for (int col = 0; col < size; col++)
{
GridField field = (GridField)m_fields.get (col);
if (field.isVirtualColumn())
continue;
String columnName = field.getColumnName ();
Object value = rowData[col];
Object oldValue = m_rowData[col];
// RowID
if (field.getDisplayType() == DisplayType.RowID)
; // ignore
// Nothing changed & null
else if (oldValue == null && value == null)
; // ignore
// *** Data changed ***
else if (m_inserting || isValueChanged(oldValue, value) )
{
// Check existence
int poIndex = po.get_ColumnIndex(columnName);
if (poIndex < 0)
{
// Custom Fields not in PO
po.set_CustomColumn(columnName, value);
// log.log(Level.SEVERE, "Column not found: " + columnName);
continue;
}
Object dbValue = po.get_Value(poIndex);
if (m_inserting
|| !m_compareDB
// Original == DB
|| (oldValue == null && dbValue == null)
|| (oldValue != null && oldValue.equals (dbValue))
// Target == DB (changed by trigger to new value already)
|| (value == null && dbValue == null)
|| (value != null && value.equals (dbValue))
|| ((oldValue != null && dbValue != null && oldValue.getClass().equals(byte[].class) && dbValue.getClass().equals(byte[].class)) && Arrays.equals((byte[])oldValue, (byte[])dbValue))
|| ((value != null && dbValue != null && value.getClass().equals(byte[].class) && dbValue.getClass().equals(byte[].class)) && Arrays.equals((byte[])oldValue, (byte[])dbValue))
)
{
if (!po.set_ValueNoCheck (columnName, value))
{
ValueNamePair lastError = CLogger.retrieveError();
if (lastError != null) {
String adMessage = lastError.getValue();
String adMessageArgument = lastError.getName().trim();
StringBuilder info = new StringBuilder(adMessageArgument);
if (!adMessageArgument.endsWith(";")) info.append(";");
info.append(field.getHeader());
fireDataStatusEEvent(adMessage, info.toString(), true);
} else {
fireDataStatusEEvent("Set value failed", field.getHeader(), true);
}
return SAVE_ERROR;
}
}
// Original != DB
else
{
String msg = columnName
+ "= " + oldValue
+ (oldValue==null ? "" : "(" + oldValue.getClass().getName() + ")")
+ " != DB: " + dbValue
+ (dbValue==null ? "" : "(" + dbValue.getClass().getName() + ")")
+ " -> New: " + value
+ (value==null ? "" : "(" + value.getClass().getName() + ")");
dataRefresh(m_rowChanged);
fireDataStatusEEvent("SaveErrorDataChanged", msg, true);
return SAVE_ERROR;
}
} // Data changed
} // for every column
if (!po.save())
{
String msg = "SaveError";
String info = "";
ValueNamePair ppE = CLogger.retrieveError();
if (ppE != null)
{
msg = ppE.getValue();
info = ppE.getName();
if ("DBExecuteError".equals(msg))
info = "DBExecuteError:" + info;
}
fireDataStatusEEvent(msg, info, true);
return SAVE_ERROR;
}
else if (m_virtual && po.get_ID() > 0)
{
//update ID
MSort sort = m_sort.get(m_rowChanged);
int oldid = sort.index;
if (oldid != po.get_ID())
{
sort.index = po.get_ID();
Object[] data = m_virtualBuffer.remove(oldid);
data[m_indexKeyColumn] = sort.index;
m_virtualBuffer.put(sort.index, data);
}
}
// Refresh - update buffer
String whereClause = po.get_WhereClause(true);
if (log.isLoggable(Level.FINE)) log.fine("Reading ... " + whereClause);
StringBuilder refreshSQL = new StringBuilder(m_SQL_Select)
.append(" WHERE ").append(whereClause);
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
pstmt = DB.prepareStatement(refreshSQL.toString(), get_TrxName());
rs = pstmt.executeQuery();
if (rs.next())
{
Object[] rowDataDB = readData(rs);
// update buffer
setDataAtRow(m_rowChanged, rowDataDB);
fireTableRowsUpdated(m_rowChanged, m_rowChanged);
}
}
catch (SQLException e)
{
String msg = "SaveError";
log.log(Level.SEVERE, refreshSQL.toString(), e);
fireDataStatusEEvent(msg, e.getLocalizedMessage(), true);
return SAVE_ERROR;
}
finally
{
DB.close(rs, pstmt);
rs = null;
pstmt = null;
}
// everything ok
m_rowData = null;
m_changed = false;
m_compareDB = true;
m_rowChanged = -1;
m_newRow = -1;
m_inserting = false;
//
ValueNamePair pp = CLogger.retrieveWarning();
if (pp != null)
{
String msg = pp.getValue();
String info = pp.getName();
fireDataStatusEEvent(msg, info, false);
}
else
{
pp = CLogger.retrieveInfo();
String msg = "Saved";
String info = "";
if (pp != null)
{
msg = pp.getValue();
info = pp.getName();
}
fireDataStatusIEvent(msg, info);
}
//
log.config("fini");
return SAVE_OK;
} // dataSavePO
/**
* Get Record Where Clause from data (single key or multi-key)
* @param rowData data
* @return where clause or null
*/
private String getWhereClause (Object[] rowData)
{
int size = m_fields.size();
StringBuilder singleRowWHERE = null;
StringBuilder singleRowUUWHERE = null;
StringBuilder multiRowWHERE = null;
String tableName = getTableName();
int uidColumn = -1;
for (int col = 0; col < size; col++)
{
GridField field = (GridField)m_fields.get (col);
String columnName = field.getColumnName();
if (field.isKey())
{
Object value = rowData[col];
if (value == null)
{
log.log(Level.WARNING, "PK data is null - " + columnName);
return null;
}
if (columnName.endsWith ("_ID"))
singleRowWHERE = new StringBuilder(tableName).append(".").append(columnName)
.append ("=").append (value);
else
singleRowWHERE = new StringBuilder(tableName).append(".").append(columnName)
.append ("=").append (DB.TO_STRING(value.toString()));
break;
}
else if (field.isParentColumn())
{
Object value = rowData[col];
if (value == null)
{
if (log.isLoggable(Level.INFO))log.log(Level.INFO, "FK data is null - " + columnName);
continue;
}
if (multiRowWHERE == null)
multiRowWHERE = new StringBuilder();
else
multiRowWHERE.append(" AND ");
if (columnName.endsWith ("_ID"))
multiRowWHERE.append (tableName).append(".").append(columnName)
.append ("=").append (value);
else if (value instanceof Timestamp) {
multiRowWHERE.append (tableName).append(".").append(columnName)
.append ("=").append (DB.TO_DATE((Timestamp)value, false));
}else
multiRowWHERE.append (tableName).append(".").append(columnName)
.append ("=").append (DB.TO_STRING(value.toString()));
}
else if (columnName.equals(PO.getUUIDColumnName(tableName)))
{
uidColumn = col;
}
} // for all columns
if (singleRowWHERE != null)
return singleRowWHERE.toString();
if (multiRowWHERE != null)
return multiRowWHERE.toString();
if (uidColumn >= 0)
{
Object value = rowData[uidColumn];
if (value == null && multiRowWHERE == null)
{
log.log(Level.WARNING, "UUID data is null - " + uidColumn);
return null;
}
else
{
singleRowUUWHERE = new StringBuilder(tableName).append(".").append(PO.getUUIDColumnName(tableName))
.append ("=").append (DB.TO_STRING(value.toString()));
}
}
if (singleRowUUWHERE != null)
return singleRowUUWHERE.toString();
log.log(Level.WARNING, "No key Found");
return null;
} // getWhereClause
/*************************************************************************/
/**
* Get Mandatory empty columns
* @param rowData row data
* @return Mandatory columns that's empty (separated by comma)
*/
private String getMandatory(Object[] rowData)
{
// see also => ProcessParameter.saveParameter
StringBuilder sb = new StringBuilder();
// Check all columns
int size = m_fields.size();
for (int i = 0; i < size; i++)
{
GridField field = (GridField)m_fields.get(i);
if (field.isMandatory(true)) // check context
{
if (rowData[i] == null || rowData[i].toString().length() == 0)
{
field.setInserting (true); // set editable otherwise deadlock
field.setError(true);
if (sb.length() > 0)
sb.append(", ");
sb.append(field.getHeader());
}
else
field.setError(false);
}
}
if (sb.length() == 0)
return "";
return sb.toString();
} // getMandatory
/**
* @return true if need save and all mandatory field has value
*/
public boolean isNeedSaveAndMandatoryFill() {
if (!m_open)
{
return false;
}
// no need - not changed - row not positioned - no Value changed
if (m_rowChanged == -1)
{
return false;
}
// Value not changed
if (m_rowData == null)
{
return false;
}
if (m_readOnly)
{
return false;
}
// row not positioned - no Value changed
if (m_rowChanged == -1)
{
if (m_newRow != -1) // new row and nothing changed - might be OK
m_rowChanged = m_newRow;
else
{
return false;
}
}
// get updated row data
Object[] rowData = getDataAtRow(m_rowChanged);
// Check Mandatory
String missingColumns = getMandatory(rowData);
if (missingColumns.length() != 0) {
return false;
}
return true;
}
/*************************************************************************/
// IDEMPIERE-454 Easy import
private boolean m_importing = false;
private String m_trxName = null;
private int m_currentRow = -1;
/**
* Append new row after current row
* @param currentRow row
* @param copyCurrent true to copy value from current row
* @return true if success -
* Error info (Access*, AccessCannotInsert) is saved in the log
*/
public boolean dataNew (int currentRow, boolean copyCurrent)
{
if (log.isLoggable(Level.INFO)) log.info("Current=" + currentRow + ", Copy=" + copyCurrent);
// Read only
if (m_readOnly)
{
fireDataStatusEEvent("AccessCannotInsert", "", true);
return false;
}
// see if we need to save
dataSave(-2, false);
m_inserting = true;
// Setup the buffer first so that event will be handle properly
// Create default data
int size = m_fields.size();
m_rowData = new Object[size]; // "original" data
Object[] rowData = new Object[size];
m_changed = true;
m_compareDB = true;
m_newRow = currentRow + 1;
// if there is no record, the current row could be 0 (and not -1)
if (m_sort.size() < m_newRow)
m_newRow = m_sort.size();
// add Data at end of buffer
MSort newSort = m_virtual
? new MSort(NEW_ROW_ID, null)
: new MSort(m_sort.size(), null); // index
if (m_virtual)
{
m_virtualBuffer.put(NEW_ROW_ID, rowData);
}
else
{
m_buffer.add(rowData);
}
// add Sort pointer
m_sort.add(m_newRow, newSort);
m_rowCount++;
// fill data
if (copyCurrent)
{
boolean hasDocTypeTargetField = (getField("C_DocTypeTarget_ID") != null);
Object[] origData = getDataAtRow(currentRow);
for (int i = 0; i < size; i++)
{
GridField field = (GridField)m_fields.get(i);
MColumn column = null;
if (field.getAD_Column_ID() > 0)
column = MColumn.get(m_ctx, field.getAD_Column_ID());
if (field.isVirtualColumn())
;
else if ( field.isKey() // KeyColumn
|| (column != null && column.isUUIDColumn()) // IDEMPIERE-67
|| (column != null && column.isStandardColumn() && !column.getColumnName().equals("AD_Org_ID")) // AD_Org_ID can be copied
// Bug [ 1807947 ]
|| (hasDocTypeTargetField && field.getColumnName().equals("C_DocType_ID"))
|| ! field.isAllowCopy())
{
Object value = field.getDefault();
field.setValue(value, m_inserting);
field.validateValueNoDirect();
rowData[i] = field.getValue();
}
else {
Object value = origData[i];
field.setValue(value, m_inserting);
field.validateValueNoDirect();
rowData[i] = field.getValue();
}
}
}
else // new
{
for (int i = 0; i < size; i++)
{
GridField field = (GridField)m_fields.get(i);
if (field.getGridTab() != null) {
//avoid getting default from previous row
String key = field.getVO().WindowNo+"|"+field.getVO().TabNo+"|"+field.getVO().ColumnName;
field.getVO().ctx.remove(key);
}
Object value = field.getDefault();
field.setValue(value, m_inserting);
field.validateValueNoDirect();
rowData[i] = field.getValue();
}
}
m_rowChanged = -1; // only changed in setValueAt
// inform
if (log.isLoggable(Level.FINE)) log.fine("Current=" + currentRow + ", New=" + m_newRow);
fireTableRowsInserted(m_newRow, m_newRow);
fireDataStatusIEvent(copyCurrent ? DATA_UPDATE_COPIED_MESSAGE : DATA_INSERTED_MESSAGE, "");
if (log.isLoggable(Level.FINE)) log.fine("Current=" + currentRow + ", New=" + m_newRow + " - complete");
return true;
} // dataNew
/**
* Delete data at row index
* @param row row index
* @return true if success -
* Error info (Access*, AccessNotDeleteable, DeleteErrorDependent,
* DeleteError) is saved in the log
*/
public boolean dataDelete (int row)
{
if (log.isLoggable(Level.INFO)) log.info("Row=" + row);
if (row < 0)
return false;
// Tab R/O
if (m_readOnly)
{
fireDataStatusEEvent("AccessCannotDelete", "", true); // privileges
return false;
}
// Is this record deletable?
if (!m_deleteable)
{
fireDataStatusEEvent("AccessNotDeleteable", "", true); // audit
return false;
}
// Processed Column and not an Import Table
if (m_indexProcessedColumn > 0 && !m_tableName.startsWith("I_"))
{
Boolean processed = (Boolean)getValueAt(row, m_indexProcessedColumn);
if (processed != null && processed.booleanValue())
{
fireDataStatusEEvent("CannotDeleteTrx", "", true);
return false;
}
}
// Carlos Ruiz - globalqss - IDEMPIERE-111
// Check if the role has access to this client
// Can we change?
int[] co = getClientOrg(row);
int AD_Client_ID = co[0];
int AD_Org_ID = co[1];
if (!MRole.getDefault(m_ctx, false).canUpdate(AD_Client_ID, AD_Org_ID, m_AD_Table_ID, 0, true))
{
fireDataStatusEEvent("AccessCannotDelete", "", true);
return false;
}
MSort sort = (MSort)m_sort.get(row);
Object[] rowData = getDataAtRow(row);
//
PO po = getPO(row);
// Delete via PO
if (po != null)
{
boolean ok = false;
try
{
ok = po.delete(false);
}
catch (Throwable t)
{
log.log(Level.SEVERE, "Delete", t);
}
if (!ok)
{
ValueNamePair vp = CLogger.retrieveError();
if (vp != null && !(Util.isEmpty(vp.getValue()) || Util.isEmpty(vp.getName())))
fireDataStatusEEvent(vp);
else
fireDataStatusEEvent("DeleteError", "", true);
return false;
}
}
else // Delete via SQL
{
StringBuilder sql = new StringBuilder("DELETE FROM ");
sql.append(m_tableName).append(" WHERE ").append(getWhereClause(rowData));
int no = 0;
PreparedStatement pstmt = null;
try
{
pstmt = DB.prepareStatement (sql.toString(),
ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE, null);
no = pstmt.executeUpdate();
}
catch (SQLException e)
{
log.log(Level.SEVERE, sql.toString(), e);
String msg = "DeleteError";
String dbMsg = DBException.getDefaultDBExceptionMessage(e);
if (!Util.isEmpty(dbMsg))
msg = dbMsg;
fireDataStatusEEvent(msg, e.getLocalizedMessage(), true);
return false;
}
finally
{
DB.close(pstmt);
pstmt = null;
}
// Check Result
if (no != 1)
{
log.log(Level.SEVERE, "Number of deleted rows = " + no);
return false;
}
}
// Get Sort
if (m_virtual)
{
m_virtualBuffer.remove(sort.index);
}
else
{
// Delete row in Buffer and shifts all below up
m_buffer.remove(sort.index);
}
m_rowCount--;
// Delete row in Sort
m_sort.remove(row);
if (!m_virtual)
{
// Correct pointer in Sort
for (int i = 0; i < m_sort.size(); i++)
{
MSort ptr = (MSort)m_sort.get(i);
if (ptr.index > sort.index)
ptr.index--; // move up
}
}
// inform
m_changed = false;
m_rowChanged = -1;
fireTableRowsDeleted(row, row);
fireDataStatusIEvent("Deleted", "");
if (log.isLoggable(Level.FINE)) log.fine("Row=" + row + " complete");
return true;
} // dataDelete
/**
* Ignore/Undo changes
*/
public void dataIgnore()
{
if (!m_inserting && !m_changed && m_rowChanged < 0)
{
if (log.isLoggable(Level.FINE))
log.fine("Nothing to ignore");
m_newRow = -1;
return;
}
if (log.isLoggable(Level.INFO)) log.info("Inserting=" + m_inserting);
// Inserting - delete new row
if (m_inserting)
{
// Get Sort
MSort sort = (MSort)m_sort.get(m_newRow);
if (m_virtual)
{
m_virtualBuffer.remove(NEW_ROW_ID);
}
else
{
// Delete row in Buffer and shifts all below up
m_buffer.remove(sort.index);
}
m_rowCount--;
// Delete row in Sort
m_sort.remove(m_newRow); // pintint to the last column, so no adjustment
//
m_changed = false;
m_rowData = null;
m_rowChanged = -1;
m_inserting = false;
// inform
fireTableRowsDeleted(m_newRow, m_newRow);
}
else
{
// update buffer
if (m_rowData != null)
{
setDataAtRow(m_rowChanged, m_rowData);
}
m_changed = false;
m_rowData = null;
m_rowChanged = -1;
m_inserting = false;
// inform
// fireTableRowsUpdated(m_rowChanged, m_rowChanged); >> messes up display?? (clearSelection)
}
m_newRow = -1;
fireDataStatusIEvent(DATA_IGNORED_MESSAGE, "");
} // dataIgnore
/**
* Refresh Row - ignore changes
* @param row row
*/
public void dataRefresh (int row)
{
dataRefresh(row, true);
}
/**
* get where clause for row
* @param row
* @return where clause
*/
public String getWhereClause(int row)
{
if (row < 0 || m_sort.size() == 0 || m_inserting)
return null;
Object[] rowData = getDataAtRow(row);
if (rowData == null)
return null;
String where = getWhereClause(rowData);
return where;
}
/**
* Refresh Row - ignore changes
* @param row row
* @param fireStatusEvent
*/
public void dataRefresh (int row, boolean fireStatusEvent)
{
if (log.isLoggable(Level.INFO)) log.info("Row=" + row);
if (row < 0 || m_sort.size() == 0 || m_inserting)
return;
Object[] rowData = getDataAtRow(row);
// ignore
dataIgnore();
// Create SQL
String where = getWhereClause(rowData);
if (where == null || where.length() == 0)
where = "1=2";
String sql = m_SQL_Select + " WHERE " + where;
Object[] rowDataDB = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
pstmt = DB.prepareStatement(sql,get_TrxName());
rs = pstmt.executeQuery();
// only one row
if (rs.next())
rowDataDB = readData(rs);
}
catch (SQLException e)
{
log.log(Level.SEVERE, sql, e);
fireTableRowsUpdated(row, row);
fireDataStatusEEvent("RefreshError", sql, true);
return;
}
finally
{
DB.close(rs, pstmt);
rs = null;
pstmt = null;
}
// update buffer
if (rowDataDB!=null)
setDataAtRow(row, rowDataDB);
// info
m_rowData = null;
m_changed = false;
m_rowChanged = -1;
m_inserting = false;
fireTableRowsUpdated(row, row);
if (fireStatusEvent)
fireDataStatusIEvent(DATA_REFRESH_MESSAGE, "");
} // dataRefresh
/**
* Refresh all Rows - ignore changes
*/
public void dataRefreshAll()
{
dataRefreshAll(true);
}
/**
* Refresh all Rows - ignore changes
* @param fireStatusEvent
*/
public void dataRefreshAll(boolean fireStatusEvent)
{
dataRefreshAll(fireStatusEvent, -1);
}
/**
* Refresh all Rows - ignore changes
* @param fireStatusEvent
* @param rowToRetained
*/
public void dataRefreshAll(boolean fireStatusEvent, int rowToRetained)
{
m_inserting = false; // should not happen
dataIgnore();
String retainedWhere = null;
if (rowToRetained >= 0)
{
retainedWhere = getWhereClause(rowToRetained);
}
close(false);
if (retainedWhere != null)
{
if (m_whereClause != null && m_whereClause.trim().length() > 0)
{
StringBuilder orRetainedWhere = new StringBuilder(") OR (").append(retainedWhere).append(")) ");
if (! m_whereClause.contains(orRetainedWhere.toString()))
m_whereClause = "((" + m_whereClause + orRetainedWhere.toString();
}
open(m_maxRows);
}
else
{
open(m_maxRows);
}
// Info
m_rowData = null;
m_changed = false;
m_rowChanged = -1;
m_inserting = false;
if (m_lastSortColumnIndex >= 0)
{
loadComplete();
sort(m_lastSortColumnIndex, m_lastSortedAscending);
}
fireTableDataChanged();
if (fireStatusEvent)
fireDataStatusIEvent(DATA_REFRESH_MESSAGE, "");
} // dataRefreshAll
/**
* Re-query with new whereClause
* @param whereClause sql where clause
* @param onlyCurrentRows only current rows
* @param onlyCurrentDays how many days back
* @param fireEvents if tabledatachanged and datastatusievent must be fired
* @return true if success
*/
public boolean dataRequery (String whereClause, boolean onlyCurrentRows, int onlyCurrentDays, boolean fireEvents)
{
if (log.isLoggable(Level.INFO)) log.info(whereClause + "; OnlyCurrent=" + onlyCurrentRows);
close(false);
m_onlyCurrentDays = onlyCurrentDays;
setSelectWhereClause(whereClause, onlyCurrentRows, m_onlyCurrentDays);
open(m_maxRows);
// Info
m_rowData = null;
m_changed = false;
m_rowChanged = -1;
m_inserting = false;
if (m_lastSortColumnIndex >= 0)
{
loadComplete();
sort(m_lastSortColumnIndex, m_lastSortedAscending);
}
if (fireEvents) {
fireTableDataChanged();
fireDataStatusIEvent(DATA_REFRESH_MESSAGE, "");
}
return true;
} // dataRequery
/**
* Delegate to {@link #dataRequery(String, boolean, int, boolean)} with fireEvents=true
* @param whereClause
* @param onlyCurrentRows
* @param onlyCurrentDays
* @return true if success
*/
public boolean dataRequery (String whereClause, boolean onlyCurrentRows, int onlyCurrentDays)
{
return dataRequery (whereClause, onlyCurrentRows, onlyCurrentDays, true);
} // dataRequery
/**
* Is Cell Editable.
* @param row row index
* @param col column index
* @return true, if editable
*/
public boolean isCellEditable (int row, int col)
{
// Entire Table not editable
if (m_readOnly)
return false;
// Key not editable
if (col == m_indexKeyColumn)
return false;
/** @todo check link columns */
// Check column range
if (col < 0 && col >= m_fields.size())
return false;
// IsActive Column always editable if no processed exists
if (col == m_indexActiveColumn && m_indexProcessedColumn == -1)
return true;
// Row
if (!isRowEditable(row))
return false;
// Column
return ((GridField)m_fields.get(col)).isEditable(false);
} // IsCellEditable
/**
* Is row editable
* @param row row index
* @return true if editable
*/
public boolean isRowEditable (int row)
{
// Entire Table not editable or no row
if (m_readOnly || row < 0)
return false;
// If not Active - not editable
if (m_indexActiveColumn > 0) // && m_TabNo != Find.s_TabNo)
{
Object value = getValueAt(row, m_indexActiveColumn);
if (value instanceof Boolean)
{
if (!((Boolean)value).booleanValue())
return false;
}
else if ("N".equals(value))
return false;
}
// If Processed - not editable (Find always editable)
if (m_indexProcessedColumn > 0) // && m_TabNo != Find.s_TabNo)
{
Object processed = getValueAt(row, m_indexProcessedColumn);
if (processed instanceof Boolean)
{
if (((Boolean)processed).booleanValue())
return false;
}
else if ("Y".equals(processed))
return false;
}
//
int[] co = getClientOrg(row);
int AD_Client_ID = co[0];
int AD_Org_ID = co[1];
int Record_ID = getKeyID(row);
return MRole.getDefault(m_ctx, false).canUpdate
(AD_Client_ID, AD_Org_ID, m_AD_Table_ID, Record_ID, false);
} // isRowEditable
/**
* Get Client Org for row
* @param row row index
* @return array [0] = Client [1] = Org - a value of -1 is not defined/found
*/
private int[] getClientOrg (int row)
{
int AD_Client_ID = -1;
if (m_indexClientColumn != -1)
{
Integer ii = (Integer)getValueAt(row, m_indexClientColumn);
if (ii != null)
AD_Client_ID = ii.intValue();
}
int AD_Org_ID = 0;
if (m_indexOrgColumn != -1)
{
Integer ii = (Integer)getValueAt(row, m_indexOrgColumn);
if (ii != null)
AD_Org_ID = ii.intValue();
}
return new int[] {AD_Client_ID, AD_Org_ID};
} // getClientOrg
/**
* Set entire table as read only
* @param value new read only value
*/
public void setReadOnly (boolean value)
{
if (log.isLoggable(Level.FINE)) log.fine("ReadOnly=" + value);
m_readOnly = value;
} // setReadOnly
/**
* Is entire Table Read/Only
* @return true if read only
*/
public boolean isReadOnly()
{
return m_readOnly;
} // isReadOnly
/**
* Is inserting
* @return true if inserting
*/
public boolean isInserting()
{
return m_inserting;
} // isInserting
/**
* Set Compare DB.
* If Set to false, save overwrites the record, regardless of DB changes.
* (When a payment is changed in Sales Order, the payment reversal clears the payment id)
* @param compareDB compare DB - false forces overwrite
* @deprecated
*/
@Deprecated
public void setCompareDB (boolean compareDB)
{
m_compareDB = compareDB;
} // setCompareDB
/**
* Get Compare DB.
* @return false if save overwrites the record, regardless of DB changes
* (false forces overwrite).
* @deprecated
*/
@Deprecated
public boolean getCompareDB ()
{
return m_compareDB;
} // getCompareDB
/**
* Can Table rows be deleted
* @param value new deleteable value
*/
public void setDeleteable (boolean value)
{
if (log.isLoggable(Level.FINE)) log.fine("Deleteable=" + value);
m_deleteable = value;
} // setDeleteable
/**
* Read data from result set
* @param rs result set
* @return Data Array
*/
private Object[] readData (ResultSet rs)
{
int size = m_fields.size();
Object[] rowData = new Object[size];
String columnName = null;
int displayType = 0;
// Types see also GridField.createDefault
try
{
// get row data
for (int j = 0; j < size; j++)
{
// Column Info
GridField field = (GridField)m_fields.get(j);
columnName = field.getColumnName();
displayType = field.getDisplayType();
// Integer, ID, Lookup
if (displayType == DisplayType.Integer || (DisplayType.isID(displayType) && !(columnName.equals("EntityType") || columnName.equals("AD_Language"))))
{
rowData[j] = Integer.valueOf(rs.getInt(j+1)); // Integer
if (rs.wasNull())
rowData[j] = null;
}
// Number
else if (DisplayType.isNumeric(displayType))
rowData[j] = rs.getBigDecimal(j+1); // BigDecimal
// Date
else if (DisplayType.isDate(displayType))
rowData[j] = rs.getTimestamp(j+1); // Timestamp
// RowID or Key (and Selection)
else if (displayType == DisplayType.RowID)
rowData[j] = null;
// YesNo
else if (displayType == DisplayType.YesNo)
{
String str = rs.getString(j+1);
if (field.isEncryptedColumn())
str = (String)decrypt(str, getAD_Client_ID(rs));
rowData[j] = Boolean.valueOf("Y".equals(str)); // Boolean
}
// LOB
else if (DisplayType.isLOB(displayType))
{
Object value = rs.getObject(j+1);
if (rs.wasNull())
rowData[j] = null;
else if (value instanceof Clob)
{
Clob lob = (Clob)value;
long length = lob.length();
rowData[j] = lob.getSubString(1, (int)length);
}
else if (value instanceof Blob)
{
Blob lob = (Blob)value;
long length = lob.length();
rowData[j] = lob.getBytes(1, (int)length);
}
else if (value instanceof String)
rowData[j] = value;
else if (value instanceof byte[])
rowData[j] = value;
}
// String
else
rowData[j] = rs.getString(j+1); // String
// Encrypted
if (field.isEncryptedColumn() && displayType != DisplayType.YesNo)
rowData[j] = decrypt(rowData[j], getAD_Client_ID(rs));
}
}
catch (SQLException e)
{
log.log(Level.SEVERE, columnName + ", DT=" + displayType, e);
}
return rowData;
} // readData
/**
* Decrypt
* @param yy encrypted data
* @param AD_Client_ID
* @return clear data
*/
private Object decrypt (Object yy, int AD_Client_ID)
{
if (yy == null)
return null;
return SecureEngine.decrypt(yy, AD_Client_ID);
} // decrypt
/**
* @param rs
* @return AD_Client_ID or -1
*/
private int getAD_Client_ID(ResultSet rs) {
int AD_Client_ID = -1;
try {
AD_Client_ID = rs.getInt("AD_Client_ID");
if (rs.wasNull())
AD_Client_ID = -1;
} catch (SQLException e) {
AD_Client_ID = -1;
}
if (AD_Client_ID == -1)
AD_Client_ID = getAD_Client_ID();
return AD_Client_ID;
}
/**
* @return AD_Client_ID
*/
private int getAD_Client_ID()
{
int AD_Client_ID = Env.getAD_Client_ID(Env.getCtx());
GridField field = getField("AD_Client_ID");
if (field != null && field.getValue() != null) {
AD_Client_ID = ((Number)field.getValue()).intValue();
}
return AD_Client_ID;
}
/**
* Remove Data Status Listener
* @param l listener
*/
public synchronized void removeDataStatusListener(DataStatusListener l)
{
listenerList.remove(DataStatusListener.class, l);
} // removeDataStatusListener
/**
* Add Data Status Listener
* @param l listener
*/
public synchronized void addDataStatusListener(DataStatusListener l)
{
listenerList.add(DataStatusListener.class, l);
} // addDataStatusListener
/**
* Fire data status changed event
* @param e event
*/
private void fireDataStatusChanged (DataStatusEvent e)
{
DataStatusListener[] listeners = listenerList.getListeners(DataStatusListener.class);
for (int i = 0; i < listeners.length; i++)
listeners[i].dataStatusChanged(e);
} // fireDataStatusChanged
/**
* Create new Data Status Event instance
* @return new data status event instance
*/
private DataStatusEvent createDSE()
{
boolean changed = m_changed;
if (m_rowChanged != -1)
changed = true;
DataStatusEvent dse = new DataStatusEvent(this, m_rowCount, changed,
Env.isAutoCommit(m_ctx, m_WindowNo), m_inserting);
dse.AD_Table_ID = m_AD_Table_ID;
dse.Record_ID = null;
return dse;
} // createDSE
/**
* Create and fire Data Status Info Event
* @param AD_Message message
* @param info additional info
*/
protected void fireDataStatusIEvent (String AD_Message, String info)
{
DataStatusEvent e = createDSE();
e.setInfo(AD_Message, info, false,false);
if (SORTED_DSE_EVENT.equals(AD_Message) && m_currentRow >= 0)
e.setCurrentRow(m_currentRow);
fireDataStatusChanged (e);
} // fireDataStatusIEvent
/**
* Create and fire Data Status Error Event
* @param AD_Message message
* @param info info
* @param isError error
*/
protected void fireDataStatusEEvent (String AD_Message, String info, boolean isError)
{
//
DataStatusEvent e = createDSE();
if (info != null && info.startsWith("DBExecuteError:")) {
String firstline;
int nl = info.indexOf("\n");
if (nl > 0)
firstline = info.substring(0, nl);
else
firstline = info;
String newinfo = Msg.getMsg(m_ctx, firstline);
if (firstline.equals(newinfo))
newinfo = info.substring(15); // size of "DBExecuteError:"
e.setInfo(AD_Message, newinfo, isError, !isError);
} else {
e.setInfo(AD_Message, info, isError, !isError);
}
if (isError)
log.saveWarning(AD_Message, info);
fireDataStatusChanged (e);
} // fireDataStatusEEvent
/**
* Create and fire Data Status Error Event (from Error Log)
* @param errorLog error log info
*/
protected void fireDataStatusEEvent (ValueNamePair errorLog)
{
if (errorLog != null)
fireDataStatusEEvent (errorLog.getValue(), errorLog.getName(), true);
} // fireDataStatusEEvent
/**
* Remove Vetoable change listener for row changes
* @param l listener
*/
public synchronized void removeVetoableChangeListener(VetoableChangeListener l)
{
m_vetoableChangeSupport.removeVetoableChangeListener(l);
} // removeVetoableChangeListener
/**
* Add Vetoable change listener for row changes
* @param l listener
*/
public synchronized void addVetoableChangeListener(VetoableChangeListener l)
{
m_vetoableChangeSupport.addVetoableChangeListener(l);
} // addVetoableChangeListener
/**
* Fire Vetoable change listener for row changes
* @param e event
* @throws PropertyVetoException
*/
protected void fireVetoableChange(PropertyChangeEvent e) throws java.beans.PropertyVetoException
{
m_vetoableChangeSupport.fireVetoableChange(e);
} // fireVetoableChange
/**
* toString
* @return String representation
*/
public String toString()
{
return new StringBuilder("MTable[").append(m_tableName)
.append(",WindowNo=").append(m_WindowNo)
.append(",Tab=").append(m_TabNo).append("]").toString();
} // toString
/**
*
* @return new row added
*/
public int getNewRow()
{
return m_newRow;
}
/**
* Asynchronous Loader
*/
protected class Loader implements Serializable, Runnable
{
/**
* generated serial id
*/
private static final long serialVersionUID = -6866671239509705988L;
/**
* Construct Loader
*/
public Loader()
{
super();
} // Loader
private PreparedStatement m_pstmt = null;
private ResultSet m_rs = null;
private Trx trx = null;
private Properties m_context = null;
private int maxRows;
private int rows;
public void setContext(Properties context)
{
m_context = context;
}
/**
* Open ResultSet
* @param maxRows maximum number of rows or 0 for all
* @return number of records
*/
protected int open (int maxRows)
{
this.maxRows = maxRows;
// Get Number of Rows
rows = 0;
PreparedStatement pstmt = null;
ResultSet rs = null;
m_rowCountTimeout = false;
try
{
pstmt = DB.prepareStatement(m_SQL_Count, get_TrxName());
setParameter (pstmt, true);
int timeout = MSysConfig.getIntValue(MSysConfig.GRIDTABLE_INITIAL_COUNT_TIMEOUT_IN_SECONDS,
DEFAULT_GRIDTABLE_COUNT_TIMEOUT_IN_SECONDS, Env.getAD_Client_ID(Env.getCtx()));
if (timeout > 0)
pstmt.setQueryTimeout(timeout);
rs = pstmt.executeQuery();
if (rs.next())
rows = rs.getInt(1);
}
catch (SQLException e0)
{
if (DB.getDatabase().isQueryTimeout(e0))
{
m_rowCountTimeout = true;
return 0;
}
else
throw new DBException(e0);
}
finally
{
DB.close(rs, pstmt);
}
StringBuilder info = new StringBuilder("Rows=");
info.append(rows);
if (rows == 0)
info.append(" - ").append(m_SQL_Count);
if (maxRows > 0 && rows > maxRows)
{
info.append(" - MaxRows=").append(maxRows);
rows = maxRows;
}
if (log.isLoggable(Level.FINE)) log.fine(info.toString());
return rows;
} // open
private void openResultSet() {
String trxName = get_TrxName();
//postgresql need trx to use cursor based resultset
//https://jdbc.postgresql.org/documentation/head/query.html#query-with-cursor
if (trxName == null) {
trxName = m_virtual ? Trx.createTrxName("Loader") : null;
trx = trxName != null ? Trx.get(trxName, true) : null;
if (trx != null)
trx.setDisplayName(getClass().getName()+"_openResultSet");
}
// open Statement (closed by Loader.close)
try
{
m_pstmt = DB.prepareStatement(m_SQL, trxName);
//ensure not all rows are fetch into memory for virtual table
if (m_virtual)
m_pstmt.setFetchSize(100);
setParameter (m_pstmt, false);
int timeout = MSysConfig.getIntValue(MSysConfig.GRIDTABLE_LOAD_TIMEOUT_IN_SECONDS, DEFAULT_GRIDTABLE_LOAD_TIMEOUT_IN_SECONDS, Env.getAD_Client_ID(Env.getCtx()));
if (timeout > 0)
m_pstmt.setQueryTimeout(timeout);
m_rs = m_pstmt.executeQuery();
}
catch (SQLException e)
{
if (DB.getDatabase().isQueryTimeout(e)) {
m_rowLoadTimeout = true;
throw new AdempiereException(Msg.getMsg(Env.getCtx(), LOAD_TIMEOUT_ERROR_MESSAGE), e);
} else {
log.saveError(e.getLocalizedMessage(), e);
throw new DBException(e);
}
}
}
/**
* Close RS and Statement
*/
private void close()
{
DB.close(m_rs, m_pstmt);
m_rs = null;
m_pstmt = null;
if (trx != null)
{
trx.close();
trx = null;
}
} // close
/**
* Fill Buffer to include Row
*/
public void run()
{
try {
if (m_context != null)
ServerContext.setCurrentInstance(m_context);
doRun();
} finally {
if (m_context != null)
ServerContext.dispose();
}
} // run
/**
* Fill buffer from result set
*/
private void doRun() {
boolean isFindOverMax = false;
try
{
openResultSet();
if (m_rs == null)
return;
while (m_rs.next())
{
if (maxRows > 0 && m_sort.size() == maxRows) {
isFindOverMax = true;
break;
}
if (Thread.interrupted())
{
if (log.isLoggable(Level.FINE)) log.fine("Interrupted");
close();
return;
}
// Get Data
int recordId = 0;
Object[] rowData = null;
if (m_virtual)
recordId = m_rs.getInt(getKeyColumnName());
else
rowData = readData(m_rs);
// add Data
MSort sort = m_virtual
? new MSort(recordId, null)
: new MSort(m_buffer.size(), null); // index
if (!m_virtual)
{
m_buffer.add(rowData);
}
m_sort.add(sort);
// Start with rowCount=0, inform loading of first row
if (m_rowCountTimeout)
{
m_rowCount++;
if (m_rowCount == 1)
{
DataStatusEvent evt = createDSE();
evt.setLoading(m_sort.size());
evt.setInfo("CountQueryTimeoutLoadBackground", null, false, true);
fireDataStatusChanged(evt);
}
}
// Statement all 1000 rows & sleep
if (m_sort.size() % 1000 == 0)
{
DataStatusEvent evt = createDSE();
evt.setLoading(m_sort.size());
fireDataStatusChanged(evt);
// give the other processes a chance
try
{
Thread.yield();
Thread.sleep(10); // .01 second
}
catch (InterruptedException ie)
{
if (log.isLoggable(Level.FINE)) log.fine("Interrupted while sleeping");
close();
return;
}
}
} // while(rs.next())
}
catch (Exception e)
{
log.log(Level.SEVERE, "run", e);
}
finally
{
close();
}
// Background loading without initial rowCount, inform final loaded rows
if (m_rowCountTimeout && m_sort.size() > 0)
{
DataStatusEvent evt = createDSE();
evt.setLoading(m_sort.size());
if (isFindOverMax)
evt.setInfo("FindOverMax", " > " + m_sort.size(), false, true);
fireDataStatusChanged(evt);
}
fireDataStatusIEvent("", "");
}
/**
* Set Parameter for Query.
* elements must be Integer, BigDecimal, String (default)
* @param pstmt prepared statement
* @param countSQL count
*/
private void setParameter (PreparedStatement pstmt, boolean countSQL)
{
if (m_parameterSELECT.size() == 0 && m_parameterWHERE.size() == 0)
return;
try
{
int pos = 1; // position in Statement
// Select Clause Parameters
for (int i = 0; !countSQL && i < m_parameterSELECT.size(); i++)
{
Object para = m_parameterSELECT.get(i);
if (para != null)
if (log.isLoggable(Level.FINE)) log.fine("Select " + i + "=" + para);
//
if (para == null)
;
else if (para instanceof Integer)
{
Integer ii = (Integer)para;
pstmt.setInt (pos++, ii.intValue());
}
else if (para instanceof BigDecimal)
pstmt.setBigDecimal (pos++, (BigDecimal)para);
else
pstmt.setString(pos++, para.toString());
}
// Where Clause Parameters
for (int i = 0; i < m_parameterWHERE.size(); i++)
{
Object para = m_parameterWHERE.get(i);
if (para != null)
if (log.isLoggable(Level.FINE)) log.fine("Where " + i + "=" + para);
//
if (para == null)
;
else if (para instanceof Integer)
{
Integer ii = (Integer)para;
pstmt.setInt (pos++, ii.intValue());
}
else if (para instanceof BigDecimal)
pstmt.setBigDecimal (pos++, (BigDecimal)para);
else
pstmt.setString(pos++, para.toString());
}
}
catch (SQLException e)
{
log.log(Level.SEVERE, "parameter", e);
}
} // setParameter
} // Loader
/**
* Feature Request [1707462]
* Enable runtime change of VFormat
* @param identifier column name
* @param strNewFormat new input mask
* author fer_luck
*/
protected void setFieldVFormat (String identifier, String strNewFormat)
{
int cols = m_fields.size();
for (int i = 0; i < cols; i++)
{
GridField field = (GridField)m_fields.get(i);
if (identifier.equalsIgnoreCase(field.getColumnName())){
field.setVFormat(strNewFormat);
m_fields.set(i, field);
break;
}
}
} // setFieldVFormat
/**
* Verify if the record at row has been changed at DB (by other user or process)
* @param row row index
* @return true if record at row has been changed at DB
*/
public boolean hasChanged(int row) {
// not so aggressive (it can has still concurrency problems)
// compare Updated, IsProcessed
if (getKeyID(row) > 0) {
int colUpdated = findColumn("Updated");
int colProcessed = findColumn("Processed");
boolean hasUpdated = (colUpdated >= 0);
boolean hasProcessed = (colProcessed >= 0);
String columns = null;
if (hasUpdated && hasProcessed) {
columns = new String("Updated, Processed");
} else if (hasUpdated) {
columns = new String("Updated");
} else if (hasProcessed) {
columns = new String("Processed");
} else {
// no columns updated or processed to compare
return false;
}
// todo: temporary fix for carlos assumption that all windows have _id column
if ( findColumn(m_tableName + "_ID") == -1)
return false;
Timestamp dbUpdated = null;
String dbProcessedS = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
String sql = "SELECT " + columns + " FROM " + m_tableName + " WHERE " + m_tableName + "_ID=?";
try
{
pstmt = DB.prepareStatement(sql, get_TrxName());
pstmt.setInt(1, getKeyID(row));
rs = pstmt.executeQuery();
if (rs.next()) {
int idx = 1;
if (hasUpdated)
dbUpdated = rs.getTimestamp(idx++);
if (hasProcessed)
dbProcessedS = rs.getString(idx++);
}
else
if (log.isLoggable(Level.INFO)) log.info("No Value " + sql);
}
catch (SQLException e)
{
throw new DBException(e, sql);
}
finally
{
DB.close(rs, pstmt);
rs = null; pstmt = null;
}
if (hasUpdated) {
Timestamp memUpdated = null;
memUpdated = (Timestamp) getOldValue(row, colUpdated);
if (memUpdated == null)
memUpdated = (Timestamp) getValueAt(row, colUpdated);
if (memUpdated != null && ! memUpdated.equals(dbUpdated))
return true;
}
if (hasProcessed) {
Boolean memProcessed = null;
memProcessed = (Boolean) getOldValue(row, colProcessed);
if (memProcessed == null){
if(getValueAt(row, colProcessed) instanceof Boolean )
memProcessed = (Boolean) getValueAt(row, colProcessed);
else if (getValueAt(row, colProcessed) instanceof String )
memProcessed = Boolean.valueOf((String)getValueAt(row, colProcessed));
}
Boolean dbProcessed = Boolean.TRUE;
if (! dbProcessedS.equals("Y"))
dbProcessed = Boolean.FALSE;
if (memProcessed != null && ! memProcessed.equals(dbProcessed))
return true;
}
}
// @TODO: configurable aggressive - compare each column with the DB
return false;
}
/**
* Verify if po has been changed at DB (by other user or process)
* @param po
* @return true if po has been changed at DB
*/
private boolean hasChanged(PO po) {
if (m_rowChanged < 0)
return false;
// not so aggressive (it can has still concurrency problems)
// compare Updated, IsProcessed
int colUpdated = findColumn("Updated");
int colProcessed = findColumn("Processed");
boolean hasUpdated = colUpdated >= 0;
boolean hasProcessed = colProcessed >= 0;
if (!hasUpdated && !hasProcessed) {
// no columns updated or processed to compare
return false;
}
Timestamp dbUpdated = (Timestamp) po.get_Value("Updated");
if (hasUpdated) {
Timestamp memUpdated = null;
memUpdated = (Timestamp) getOldValue(m_rowChanged, colUpdated);
if (memUpdated == null)
memUpdated = (Timestamp) getValueAt(m_rowChanged, colUpdated);
if (memUpdated != null && ! memUpdated.equals(dbUpdated))
return true;
}
if (hasProcessed) {
Boolean memProcessed = null;
memProcessed = (Boolean) getOldValue(m_rowChanged, colProcessed);
if (memProcessed == null){
if(getValueAt(m_rowChanged, colProcessed) instanceof Boolean )
memProcessed = (Boolean) getValueAt(m_rowChanged, colProcessed);
else if (getValueAt(m_rowChanged, colProcessed) instanceof String )
memProcessed = Boolean.valueOf((String)getValueAt(m_rowChanged, colProcessed));
}
Boolean dbProcessed = po.get_ValueAsBoolean("Processed");
if (memProcessed != null && ! memProcessed.equals(dbProcessed))
return true;
}
return false;
}
/**
* get Parent Tab No
* @return Parent Tab No
*/
private int getParentTabNo()
{
int tabNo = m_TabNo;
int currentLevel = Env.getContextAsInt(m_ctx, m_WindowNo, tabNo, GridTab.CTX_TabLevel);
int parentLevel = currentLevel-1;
if (parentLevel < 0)
return tabNo;
while (parentLevel != currentLevel)
{
tabNo--;
currentLevel = Env.getContextAsInt(m_ctx, m_WindowNo, tabNo, GridTab.CTX_TabLevel);
if (tabNo == 0)
break;
}
return tabNo;
}
/**
* get Tab No
* @return Tab No
*/
public int getTabNo()
{
return m_TabNo;
}
/**
* @param value
* @return true if value is not null and is empty string
*/
private boolean isNotNullAndIsEmpty (Object value) {
if (value != null
&& (value instanceof String)
&& value.toString().equals("")
)
{
return true;
} else {
return false;
}
}
/**
* @param oldValue
* @param value
* @return true if oldValue and value is different
*/
@SuppressWarnings("unchecked")
private boolean isValueChanged(Object oldValue, Object value)
{
if ( isNotNullAndIsEmpty(oldValue) ) {
oldValue = null;
}
if ( isNotNullAndIsEmpty(value) ) {
value = null;
}
boolean bChanged = (oldValue == null && value != null)
|| (oldValue != null && value == null);
if (!bChanged && oldValue != null)
{
if (oldValue.getClass().equals(value.getClass()))
{
if (oldValue instanceof Comparable>)
{
bChanged = (((Comparable)oldValue).compareTo(value) != 0);
}
else
{
bChanged = !oldValue.equals(value);
}
}
else if(value != null)
{
bChanged = !(oldValue.toString().equals(value.toString()));
}
}
return bChanged;
}
/**
* Load PO for row
* @param row row index
* @return PO
*/
public PO getPO(int row) {
MTable table = MTable.get (m_ctx, m_AD_Table_ID);
PO po = null;
int Record_ID = getKeyID(row);
if (Record_ID != -1)
{
if (Record_ID == 0 && MTable.isZeroIDTable(table.getTableName())) {
String uuidFromZeroID = table.getUUIDFromZeroID();
po = table.getPOByUU(uuidFromZeroID, m_trxName);
} else {
po = table.getPO(Record_ID, m_trxName);
}
}
else // Multi - Key
po = table.getPO(getWhereClause(getDataAtRow(row)), m_trxName);
return po;
}
/**
* @param importing import mode
* @param trxName optional trx name
*/
public void setImportingMode(boolean importing, String trxName) {
m_importing = importing;
m_trxName = trxName;
}
/**
* @return true if it is in import mode
*/
public boolean isImporting() {
return m_importing;
}
/**
* @return trx name
*/
public String get_TrxName() {
return m_trxName;
}
/**
* reset the cache sort state ( sort column and sort ascending )
*/
public void resetCacheSortState() {
m_lastSortColumnIndex = -1;
m_lastSortedAscending = true;
}
/**
* @return index of primary key column
*/
public int getKeyColumnIndex() {
return m_indexKeyColumn;
}
/**
* Index of change row
*/
public int getRowChanged()
{
return m_rowChanged;
}
/**
* reset to empty
*/
public void reset()
{
if (m_buffer != null)
m_buffer.clear();
m_changed = false;
m_rowChanged = -1;
if (m_sort != null)
m_sort.clear();
if (m_virtualBuffer != null)
m_virtualBuffer.clear();
m_rowCount = 0;
m_rowData = null;
m_oldValue = null;
m_inserting = false;
m_lastSortColumnIndex = -1;
m_lastSortedAscending = false;
}
/**
* set current row of gridtable container (gridtab). use in sort to create dse event with new current row (after sort) data
* @param m_currentRow
*/
protected void setCurrentRow(int m_currentRow) {
this.m_currentRow = m_currentRow;
}
}