diff --git a/base/src/org/compiere/model/GridTab.java b/base/src/org/compiere/model/GridTab.java index a7492a863e..998db567ba 100644 --- a/base/src/org/compiere/model/GridTab.java +++ b/base/src/org/compiere/model/GridTab.java @@ -99,10 +99,11 @@ import org.compiere.util.ValueNamePair; */ public class GridTab implements DataStatusListener, Evaluatee, Serializable { + /** * */ - private static final long serialVersionUID = -5882167959482156252L; + private static final long serialVersionUID = 7198494041906579986L; public static final String DEFAULT_STATUS_MESSAGE = "NavigateOrUpdate"; @@ -112,13 +113,28 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable * MTab provides a property listener for changed rows and a * DataStatusListener for communicating changes of the underlying data * @param vo Value Object + * @param w */ public GridTab(GridTabVO vo, GridWindow w) + { + this(vo, w, false); + } + + /** + * Create Tab (Model) from Value Object. + *

+ * MTab provides a property listener for changed rows and a + * DataStatusListener for communicating changes of the underlying data + * @param vo Value Object + * @param w + * @param virtual + */ + public GridTab(GridTabVO vo, GridWindow w, boolean virtual) { m_window = w; m_vo = vo; // Create MTable - m_mTable = new GridTable (m_vo.ctx, m_vo.AD_Table_ID, m_vo.TableName, m_vo.WindowNo, m_vo.TabNo, true); + m_mTable = new GridTable (m_vo.ctx, m_vo.AD_Table_ID, m_vo.TableName, m_vo.WindowNo, m_vo.TabNo, true, virtual); m_mTable.setReadOnly(m_vo.IsReadOnly || m_vo.IsView); m_mTable.setDeleteable(m_vo.IsDeleteable); // Load Tab @@ -637,7 +653,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable value = Env.getContext(m_vo.ctx, m_vo.WindowNo, getParentTabNo(), lc, true); if (value == null || value.length() == 0) value = Env.getContext(m_vo.ctx, m_vo.WindowNo, lc, true); // back compatibility - } + } // Same link value? if (refresh) @@ -3055,11 +3071,11 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable int parentLevel = currentLevel-1; if (parentLevel < 0) return tabNo; - while (parentLevel != currentLevel) - { - tabNo--; - currentLevel = Env.getContextAsInt(m_vo.ctx, m_vo.WindowNo, tabNo, GridTab.CTX_TabLevel); - } + while (parentLevel != currentLevel) + { + tabNo--; + currentLevel = Env.getContextAsInt(m_vo.ctx, m_vo.WindowNo, tabNo, GridTab.CTX_TabLevel); + } return tabNo; } } // GridTab diff --git a/base/src/org/compiere/model/GridTable.java b/base/src/org/compiere/model/GridTable.java index a49ff799a9..85f2e5f6a0 100644 --- a/base/src/org/compiere/model/GridTable.java +++ b/base/src/org/compiere/model/GridTable.java @@ -30,6 +30,9 @@ import java.sql.SQLException; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; import java.util.Properties; import java.util.logging.Level; @@ -85,7 +88,7 @@ public class GridTable extends AbstractTableModel /** * */ - private static final long serialVersionUID = 4273775926021737068L; + private static final long serialVersionUID = 6148998589248950598L; /** * JDBC Based Buffered Table @@ -99,6 +102,23 @@ public class GridTable extends AbstractTableModel */ public GridTable(Properties ctx, int AD_Table_ID, String TableName, int WindowNo, int TabNo, boolean withAccessControl) + { + this(ctx, AD_Table_ID, TableName, WindowNo, TabNo, withAccessControl, false); + } + + /** + * JDBC Based Buffered Table + * + * @param ctx Properties + * @param AD_Table_ID table id + * @param TableName table name + * @param WindowNo window no + * @param TabNo tab no + * @param withAccessControl if true adds AD_Client/Org restrictuins + * @param virtual use virtual table mode if table is mark as high volume + */ + public GridTable(Properties ctx, int AD_Table_ID, String TableName, int WindowNo, int TabNo, + boolean withAccessControl, boolean virtual) { super(); log.info(TableName); @@ -108,6 +128,7 @@ public class GridTable extends AbstractTableModel m_WindowNo = WindowNo; m_TabNo = TabNo; m_withAccessControl = withAccessControl; + m_virtual = virtual && MTable.get(ctx, AD_Table_ID).isHighVolume(); } // MTable private static CLogger log = CLogger.getCLogger(GridTable.class.getName()); @@ -120,6 +141,11 @@ public class GridTable extends AbstractTableModel private boolean m_withAccessControl; private boolean m_readOnly = true; private boolean m_deleteable = true; + //virtual table state variables + private boolean m_virtual; + private int m_cacheStart; + private int m_cacheEnd; + public static final String CTX_KeyColumnName = "KeyColumnName"; // /** Rowcount */ @@ -296,7 +322,7 @@ public class GridTable extends AbstractTableModel select.append(" FROM ").append(m_tableName); m_SQL_Select = select.toString(); m_SQL_Count = "SELECT COUNT(*) FROM " + m_tableName; - //BF [ 2910358 ] + //BF [ 2910358 ] //Restore the Original Value for Key Column Name based in Tab Context Value int parentTabNo = getParentTabNo(); String parentKey = Env.getContext(m_ctx, m_WindowNo, parentTabNo, GridTab.CTX_KeyColumnName, true); @@ -304,8 +330,8 @@ public class GridTable extends AbstractTableModel String currKey = Env.getContext(m_ctx, m_WindowNo, parentKey); if (valueKey != null && valueKey.length() > 0 && parentKey != null && parentKey.length() > 0 && ! currKey.equals(valueKey)) { - Env.setContext(m_ctx, m_WindowNo, parentKey, valueKey); - } + Env.setContext(m_ctx, m_WindowNo, parentKey, valueKey); + } StringBuffer where = new StringBuffer(""); // WHERE @@ -356,7 +382,9 @@ public class GridTable extends AbstractTableModel // ORDER BY if (!m_orderClause.equals("")) + { m_SQL += " ORDER BY " + m_orderClause; + } // log.fine(m_SQL_Count); Env.setContext(m_ctx, m_WindowNo, m_TabNo, GridTab.CTX_SQL, m_SQL); @@ -538,6 +566,11 @@ public class GridTable extends AbstractTableModel return true; } + if (m_virtual) + { + verifyVirtual(); + } + // create m_SQL and m_countSQL createSelectSql(); if (m_SQL == null || m_SQL.equals("")) @@ -549,10 +582,19 @@ public class GridTable extends AbstractTableModel // Start Loading m_loader = new Loader(); m_rowCount = m_loader.open(maxRows); - m_buffer = new ArrayList(m_rowCount+10); + if (m_virtual) + m_buffer = new ArrayList(210); + else + m_buffer = new ArrayList(m_rowCount+10); m_sort = new ArrayList(m_rowCount+10); + m_cacheStart = m_cacheEnd = -1; if (m_rowCount > 0) - m_loader.start(); + { + if (m_rowCount < 1000) + m_loader.run(); + else + m_loader.start(); + } else m_loader.close(); m_open = true; @@ -563,6 +605,24 @@ public class GridTable extends AbstractTableModel return true; } // open + 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 async loader of Table and Lookup Fields is complete * Used for performance tests @@ -659,6 +719,8 @@ public class GridTable extends AbstractTableModel m_sort.clear(); m_sort = null; + m_cacheStart = m_cacheEnd = -1; + if (finalCall) dispose(); @@ -778,7 +840,7 @@ public class GridTable extends AbstractTableModel for (int i = 0; i < m_sort.size(); i++) { MSort sort = (MSort)m_sort.get(i); - Object[] rowData = (Object[])m_buffer.get(sort.index); + Object[] rowData = getDataAtRow(i); if (rowData[col] == null) sort.data = null; else if (isLookup || isASI) @@ -792,6 +854,17 @@ public class GridTable extends AbstractTableModel MSort sort = new MSort(0, null); sort.setSortAsc(ascending); Collections.sort(m_sort, sort); + if (m_virtual) + { + m_buffer.clear(); + m_cacheStart = m_cacheEnd = -1; + + //release sort memory + for (int i = 0; i < m_sort.size(); i++) + { + m_sort.get(i).data = null; + } + } // update UI fireTableDataChanged(); // Info detected by MTab.dataStatusChanged and current row set to 0 @@ -852,9 +925,9 @@ public class GridTable extends AbstractTableModel // need to wait for data read into buffer int loops = 0; - while (row >= m_buffer.size() && m_loader.isAlive() && loops < 15) + while (row >= m_sort.size() && m_loader.isAlive() && loops < 15) { - log.fine("Waiting for loader row=" + row + ", size=" + m_buffer.size()); + log.fine("Waiting for loader row=" + row + ", size=" + m_sort.size()); try { Thread.sleep(500); // 1/2 second @@ -865,15 +938,14 @@ public class GridTable extends AbstractTableModel } // empty buffer - if (row >= m_buffer.size()) + if (row >= m_sort.size()) { // log.fine( "Empty buffer"); return null; } // return Data item - MSort sort = (MSort)m_sort.get(row); - Object[] rowData = (Object[])m_buffer.get(sort.index); + Object[] rowData = getDataAtRow(row); // out of bounds if (rowData == null || col > rowData.length) { @@ -883,6 +955,116 @@ public class GridTable extends AbstractTableModel return rowData[col]; } // getValueAt +private Object[] getDataAtRow(int row) + { + MSort sort = (MSort)m_sort.get(row); + Object[] rowData = null; + if (m_virtual) + { + int bufferrow = -1; + if (sort.index != -1 && (row < m_cacheStart || row > m_cacheEnd)) + { + fillBuffer(row); + } + bufferrow = row - m_cacheStart; + + rowData = (Object[])m_buffer.get(bufferrow); + } + else + { + rowData = (Object[])m_buffer.get(sort.index); + } + return rowData; + } + + private void setDataAtRow(int row, Object[] rowData) { + MSort sort = m_sort.get(row); + if (m_virtual) + { + int bufferrow = -1; + if (sort.index != -1 && (row < m_cacheStart || row > m_cacheEnd)) + { + fillBuffer(row); + } + bufferrow = row - m_cacheStart; + m_buffer.set(bufferrow, rowData); + } + else + { + m_buffer.set(sort.index, rowData); + } + + } + + private void fillBuffer(int start) + { + //adjust start if needed + if (start > 0) + { + if (start + 200 >= m_sort.size()) + { + start = start - (200 - ( m_sort.size() - start )); + if (start < 0) + start = 0; + } + } + StringBuffer sql = new StringBuffer(); + sql.append(m_SQL_Select) + .append(" WHERE ") + .append(getKeyColumnName()) + .append(" IN ("); + m_cacheStart = start; + m_cacheEnd = m_cacheStart - 1; + Maprowmap = new LinkedHashMap(200); + for(int i = start; i < start+200 && i < m_sort.size(); i++) + { + if(i > start) + sql.append(","); + sql.append(m_sort.get(i).index); + rowmap.put(m_sort.get(i).index, i); + } + sql.append(")"); + m_buffer = new ArrayList(210); + for(int i = 0; i < 200; i++) + m_buffer.add(null); + PreparedStatement stmt = null; + ResultSet rs = null; + try + { + stmt = DB.prepareStatement(sql.toString(), null); + rs = stmt.executeQuery(); + while(rs.next()) + { + Object[] data = readData(rs); + int row = rowmap.remove(data[m_indexKeyColumn]); + m_buffer.set(row - m_cacheStart, data); + m_cacheEnd++; + } + 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); + m_buffer.remove(row - m_cacheStart); + } + } + } + catch (SQLException e) + { + log.log(Level.SEVERE, e.getLocalizedMessage(), e); + } + finally + { + DB.close(rs, stmt); + } + } + /** * Indicate that there will be a change * @param changed changed @@ -958,8 +1140,8 @@ public class GridTable extends AbstractTableModel m_oldValue[2] = oldValue; // Set Data item - MSort sort = (MSort)m_sort.get(row); - Object[] rowData = (Object[])m_buffer.get(sort.index); + + Object[] rowData = getDataAtRow(row); m_rowChanged = row; /** Selection @@ -981,7 +1163,7 @@ public class GridTable extends AbstractTableModel // save & update rowData[col] = value; - m_buffer.set(sort.index, rowData); + setDataAtRow(row, rowData); // update Table fireTableCellUpdated(row, col); // update MField @@ -1127,6 +1309,8 @@ public class GridTable extends AbstractTableModel // Value not changed if (m_rowData == null) { + //reset out of sync variable + m_rowChanged = -1; log.fine("No Changes"); return SAVE_ERROR; } @@ -1180,8 +1364,7 @@ public class GridTable extends AbstractTableModel } // get updated row data - MSort sort = (MSort)m_sort.get(m_rowChanged); - Object[] rowData = (Object[])m_buffer.get(sort.index); + Object[] rowData = getDataAtRow(m_rowChanged); // Check Mandatory String missingColumns = getMandatory(rowData); @@ -1714,7 +1897,12 @@ public class GridTable extends AbstractTableModel { rowDataDB = readData(rs); // update buffer - m_buffer.set(sort.index, rowDataDB); + setDataAtRow(m_rowChanged, rowDataDB); + if (m_virtual) + { + MSort sort = m_sort.get(m_rowChanged); + sort.index = getKeyID(m_rowChanged); + } fireTableRowsUpdated(m_rowChanged, m_rowChanged); } else @@ -1764,8 +1952,7 @@ public class GridTable extends AbstractTableModel { log.fine("ID=" + Record_ID); // - MSort sort = (MSort)m_sort.get(m_rowChanged); - Object[] rowData = (Object[])m_buffer.get(sort.index); + Object[] rowData = getDataAtRow(m_rowChanged); // MTable table = MTable.get (m_ctx, m_AD_Table_ID); PO po = null; @@ -1874,7 +2061,12 @@ public class GridTable extends AbstractTableModel { Object[] rowDataDB = readData(rs); // update buffer - m_buffer.set(sort.index, rowDataDB); + setDataAtRow(m_rowChanged, rowDataDB); + if (m_virtual) + { + MSort sort = m_sort.get(m_rowChanged); + sort.index = getKeyID(m_rowChanged); + } fireTableRowsUpdated(m_rowChanged, m_rowChanged); } } @@ -2167,12 +2359,21 @@ public class GridTable extends AbstractTableModel m_compareDB = true; m_newRow = currentRow + 1; // if there is no record, the current row could be 0 (and not -1) - if (m_buffer.size() < m_newRow) - m_newRow = m_buffer.size(); + if (m_sort.size() < m_newRow) + m_newRow = m_sort.size(); // add Data at end of buffer - MSort newSort = new MSort(m_buffer.size(), null); // index - m_buffer.add(rowData); + MSort newSort = m_virtual + ? new MSort(-1, null) + : new MSort(m_sort.size(), null); // index + if (m_virtual) + { + m_buffer.add(m_newRow, rowData); + } + else + { + m_buffer.add(rowData); + } // add Sort pointer m_sort.add(m_newRow, newSort); m_rowCount++; @@ -2181,8 +2382,7 @@ public class GridTable extends AbstractTableModel if (copyCurrent) { boolean hasDocTypeTargetField = (getField("C_DocTypeTarget_ID") != null); - MSort sort = (MSort) m_sort.get(currentRow); - Object[] origData = (Object[])m_buffer.get(sort.index); + Object[] origData = getDataAtRow(currentRow); for (int i = 0; i < size; i++) { GridField field = (GridField)m_fields.get(i); @@ -2280,7 +2480,7 @@ public class GridTable extends AbstractTableModel // fireDataStatusEvent(Log.retrieveError()); MSort sort = (MSort)m_sort.get(row); - Object[] rowData = (Object[])m_buffer.get(sort.index); + Object[] rowData = getDataAtRow(row); // MTable table = MTable.get (m_ctx, m_AD_Table_ID); PO po = null; @@ -2347,19 +2547,31 @@ public class GridTable extends AbstractTableModel } // Get Sort - int bufferRow = sort.index; + int bufferRow = m_virtual + ? row - m_cacheStart + : sort.index; // Delete row in Buffer and shifts all below up m_buffer.remove(bufferRow); m_rowCount--; // Delete row in Sort m_sort.remove(row); - // Correct pointer in Sort - for (int i = 0; i < m_sort.size(); i++) + if (!m_virtual) { - MSort ptr = (MSort)m_sort.get(i); - if (ptr.index > bufferRow) - ptr.index--; // move up + // Correct pointer in Sort + for (int i = 0; i < m_sort.size(); i++) + { + MSort ptr = (MSort)m_sort.get(i); + if (ptr.index > bufferRow) + ptr.index--; // move up + } + } + else + { + if (m_cacheStart == row) + m_cacheStart++; + else + m_cacheEnd--; } // inform @@ -2389,7 +2601,9 @@ public class GridTable extends AbstractTableModel { // Get Sort MSort sort = (MSort)m_sort.get(m_newRow); - int bufferRow = sort.index; + int bufferRow = m_virtual + ? m_buffer.size() - 1 + : sort.index; // Delete row in Buffer and shifts all below up m_buffer.remove(bufferRow); m_rowCount--; @@ -2408,8 +2622,7 @@ public class GridTable extends AbstractTableModel // update buffer if (m_rowData != null) { - MSort sort = (MSort)m_sort.get(m_rowChanged); - m_buffer.set(sort.index, m_rowData); + setDataAtRow(m_rowChanged, m_rowData); } m_changed = false; m_rowData = null; @@ -2435,7 +2648,7 @@ public class GridTable extends AbstractTableModel return; MSort sort = (MSort)m_sort.get(row); - Object[] rowData = (Object[])m_buffer.get(sort.index); + Object[] rowData = getDataAtRow(row); // ignore dataIgnore(); @@ -2472,7 +2685,7 @@ public class GridTable extends AbstractTableModel } // update buffer - m_buffer.set(sort.index, rowDataDB); + setDataAtRow(row, rowDataDB); // info m_rowData = null; m_changed = false; @@ -3045,14 +3258,24 @@ public class GridTable extends AbstractTableModel return; } // Get Data - Object[] rowData = readData(m_rs); + int recordId = 0; + Object[] rowData = null; + if (m_virtual) + recordId = m_rs.getInt(getKeyColumnName()); + else + rowData = readData(m_rs); // add Data - MSort sort = new MSort(m_buffer.size(), null); // index - m_buffer.add(rowData); + 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); - // Statement all 250 rows & sleep - if (m_buffer.size() % 250 == 0) + // Statement all 1000 rows & sleep + if (m_sort.size() % 1000 == 0) { // give the other processes a chance try @@ -3067,7 +3290,7 @@ public class GridTable extends AbstractTableModel return; } DataStatusEvent evt = createDSE(); - evt.setLoading(m_buffer.size()); + evt.setLoading(m_sort.size()); fireDataStatusChanged(evt); } } // while(rs.next()) @@ -3253,11 +3476,11 @@ public class GridTable extends AbstractTableModel int parentLevel = currentLevel-1; if (parentLevel < 0) return tabNo; - while (parentLevel != currentLevel) - { - tabNo--; - currentLevel = Env.getContextAsInt(m_ctx, m_WindowNo, tabNo, GridTab.CTX_TabLevel); - } + while (parentLevel != currentLevel) + { + tabNo--; + currentLevel = Env.getContextAsInt(m_ctx, m_WindowNo, tabNo, GridTab.CTX_TabLevel); + } return tabNo; } } diff --git a/base/src/org/compiere/model/GridWindow.java b/base/src/org/compiere/model/GridWindow.java index 8c417c12b4..f390e891d4 100644 --- a/base/src/org/compiere/model/GridWindow.java +++ b/base/src/org/compiere/model/GridWindow.java @@ -69,28 +69,53 @@ public class GridWindow implements Serializable * @return window or null if not found */ public static GridWindow get (Properties ctx, int WindowNo, int AD_Window_ID) + { + return get(ctx, WindowNo, AD_Window_ID, false); + } + + /** + * Get Grid Window + * @param ctx context + * @param WindowNo window no for ctx + * @param AD_Window_ID window id + * @param virtual + * @return window or null if not found + */ + public static GridWindow get (Properties ctx, int WindowNo, int AD_Window_ID, boolean virtual) { log.config("Window=" + WindowNo + ", AD_Window_ID=" + AD_Window_ID); GridWindowVO mWindowVO = GridWindowVO.create (Env.getCtx(), WindowNo, AD_Window_ID); if (mWindowVO == null) return null; - return new GridWindow(mWindowVO); + return new GridWindow(mWindowVO, virtual); } // get - /************************************************************************** * Constructor * @param vo value object */ public GridWindow (GridWindowVO vo) + { + this(vo, false); + } + + /************************************************************************** + * Constructor + * @param vo value object + * @param virtual + */ + public GridWindow (GridWindowVO vo, boolean virtual) { m_vo = vo; + m_virtual = virtual; if (loadTabData()) enableEvents(); } // MWindow /** Value Object */ private GridWindowVO m_vo; + /** use virtual table */ + private boolean m_virtual; /** Tabs */ private ArrayList m_tabs = new ArrayList(); /** Model last updated */ @@ -141,7 +166,7 @@ public class GridWindow implements Serializable GridTabVO mTabVO = (GridTabVO)m_vo.Tabs.get(t); if (mTabVO != null) { - GridTab mTab = new GridTab(mTabVO, this); + GridTab mTab = new GridTab(mTabVO, this, m_virtual); m_tabs.add(mTab); } } // for all tabs diff --git a/zkwebui/WEB-INF/src/org/adempiere/webui/panel/AbstractADWindowPanel.java b/zkwebui/WEB-INF/src/org/adempiere/webui/panel/AbstractADWindowPanel.java index c574a5e1ae..f0d93bd95d 100644 --- a/zkwebui/WEB-INF/src/org/adempiere/webui/panel/AbstractADWindowPanel.java +++ b/zkwebui/WEB-INF/src/org/adempiere/webui/panel/AbstractADWindowPanel.java @@ -277,7 +277,7 @@ public abstract class AbstractADWindowPanel extends AbstractUIPart implements To "AccessTableNoView") + "(No Window Model Info)"); } - gridWindow = new GridWindow(gWindowVO); + gridWindow = new GridWindow(gWindowVO, true); title = gridWindow.getName(); // Set SO/AutoNew for Window @@ -1519,7 +1519,7 @@ public abstract class AbstractADWindowPanel extends AbstractUIPart implements To if (!getComponent().getDesktop().isServerPushEnabled()) getComponent().getDesktop().enableServerPush(true); - ProcessModalDialog dialog = new ProcessModalDialog(null,this.getTitle(),this,0, + ProcessModalDialog dialog = new ProcessModalDialog(this,0, AD_Process_ID,table_ID, record_ID, true); if (dialog.isValid()) { dialog.setPosition("center"); @@ -1905,8 +1905,7 @@ public abstract class AbstractADWindowPanel extends AbstractUIPart implements To } else { - ProcessModalDialog dialog = new ProcessModalDialog(null, - Env.getHeader(ctx, curWindowNo), this, curWindowNo, + ProcessModalDialog dialog = new ProcessModalDialog(this, curWindowNo, wButton.getProcess_ID(), table_ID, record_ID, startWOasking); if (dialog.isValid())