KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > compiere > model > MTable


1 /******************************************************************************
2  * The contents of this file are subject to the Compiere License Version 1.1
3  * ("License"); You may not use this file except in compliance with the License
4  * You may obtain a copy of the License at http://www.compiere.org/license.html
5  * Software distributed under the License is distributed on an "AS IS" basis,
6  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
7  * the specific language governing rights and limitations under the License.
8  * The Original Code is Compiere ERP & CRM Business Solution
9  * The Initial Developer of the Original Code is Jorg Janke and ComPiere, Inc.
10  * Portions created by Jorg Janke are Copyright (C) 1999-2001 Jorg Janke, parts
11  * created by ComPiere are Copyright (C) ComPiere, Inc.; All Rights Reserved.
12  * Contributor(s): ______________________________________.
13  *****************************************************************************/

14 package org.compiere.model;
15
16 import javax.swing.table.*;
17 import javax.swing.event.*;
18
19 import java.sql.*;
20 import java.util.*;
21 import java.math.*;
22 import java.beans.*;
23 import java.io.Serializable JavaDoc;
24
25 import org.compiere.util.*;
26
27 /**
28  * Grid Table Model for JDBC access including buffering.
29  * <pre>
30  * The following data types are handeled
31  * Integer for all IDs
32  * BigDecimal for all Numbers
33  * Timestamp for all Dates
34  * String for all others
35  * The data is read via r/o resultset and cached in m_buffer. Writes/updates
36  * are via dynamically constructed SQL INSERT/UPDATE statements. The record
37  * is re-read via the resultset to get results of triggers.
38  *
39  * </pre>
40  * The model maintains and fires the requires TableModelEvent changes,
41  * the DataChanged events (loading, changed, etc.)
42  * as well as Vetoable Change event "RowChange"
43  * (for row changes initiated by moving the row in the table grid).
44  *
45  * @author Jorg Janke
46  * @version $Id: MTable.java,v 1.50 2003/11/06 07:08:05 jjanke Exp $
47  */

48 public final class MTable extends AbstractTableModel
49     implements Serializable JavaDoc
50 {
51     /**
52      * JDBC Based Buffered Table
53      *
54      * @param ctx Properties
55      * @param TableName table name
56      * @param WindowNo window no
57      * @param TabNo tab no
58      * @param withAccessControl if true adds AD_Client/Org restrictuins
59      */

60     public MTable(Properties ctx, int AD_Table_ID, String JavaDoc TableName, int WindowNo, int TabNo,
61         boolean withAccessControl)
62     {
63         super();
64         log.info(TableName);
65         m_ctx = ctx;
66         m_AD_Table_ID = AD_Table_ID;
67         setTableName(TableName);
68         m_WindowNo = WindowNo;
69         m_TabNo = TabNo;
70         m_withAccessControl = withAccessControl;
71     } // MTable
72

73     private Logger log = Logger.getCLogger(getClass());
74     private Properties m_ctx;
75     private int m_AD_Table_ID;
76     private String JavaDoc m_tableName = "";
77     private int m_WindowNo;
78     private int m_TabNo;
79     private boolean m_withAccessControl;
80     private boolean m_readOnly = true;
81     private boolean m_deleteable = true;
82     //
83

84     /** Rowcount */
85     private int m_rowCount = 0;
86     /** Has Data changed? */
87     private boolean m_changed = false;
88     /** Index of changed row via SetValueAt */
89     private int m_rowChanged = -1;
90     /** Insert mode active */
91     private boolean m_inserting = false;
92     /** Inserted Row number */
93     private int m_newRow = -1;
94     /** Is the Resultset open? */
95     private boolean m_open = false;
96     /** Compare to DB before save */
97     private boolean m_compareDB = true; // set to true after every save
98

99     // The buffer for all data
100
private volatile ArrayList m_buffer = new ArrayList(100);
101     private volatile ArrayList m_sort = new ArrayList(100);
102     /** Original row data */
103     private Object JavaDoc[] m_rowData = null;
104     /** Original data [row,col,data] */
105     private Object JavaDoc[] m_oldValue = null;
106     //
107
private Loader m_loader = null;
108
109     /** Columns */
110     private ArrayList m_fields = new ArrayList(30);
111     private ArrayList m_parameterSELECT = new ArrayList(5);
112     private ArrayList m_parameterWHERE = new ArrayList(5);
113
114     /** Complete SQL statement */
115     private String JavaDoc m_SQL;
116     /** SQL Statement for Row Count */
117     private String JavaDoc m_SQL_Count;
118     /** The SELECT clause with FROM */
119     private String JavaDoc m_SQL_Select;
120     /** The static where clause */
121     private String JavaDoc m_whereClause = "";
122     /** Show only Processed='N' and last 24h records */
123     private boolean m_onlyCurrentRows = false;
124     /** Show only Not processed and x days */
125     private int m_onlyCurrentDays = 1;
126     /** Static ORDER BY clause */
127     private String JavaDoc m_orderClause = "";
128
129     /** Index of Key Column */
130     private int m_indexKeyColumn = -1;
131     /** Index of RowID column */
132     private int m_indexRowIDColumn = -1;
133     /** Index of Color Column */
134     private int m_indexColorColumn = -1;
135     /** Index of Processed Column */
136     private int m_indexProcessedColumn = -1;
137     /** Index of IsActive Column */
138     private int m_indexActiveColumn = -1;
139     /** Index of AD_Client_ID Column */
140     private int m_indexClientColumn = -1;
141     /** Index of AD_Org_ID Column */
142     private int m_indexOrgColumn = -1;
143
144     /** List of DataStatus Listeners */
145     private Vector m_dataStatusListeners;
146     /** Vetoable Change Bean support */
147     private VetoableChangeSupport m_vetoableChangeSupport = new VetoableChangeSupport(this);
148     /** Property of Vetoable Bean support "RowChange" */
149     public static final String JavaDoc PROPERTY = "MTable-RowSave";
150
151     /**
152      * Set Table Name
153      * @param newTableName table name
154      */

155     public void setTableName(String JavaDoc newTableName)
156     {
157         if (m_open)
158         {
159             log.error("setTableName - Table already open - ignored");
160             return;
161         }
162         if (newTableName == null || newTableName.length() == 0)
163             return;
164         m_tableName = newTableName;
165     } // setTableName
166

167     /**
168      * Get Table Name
169      * @return table name
170      */

171     public String JavaDoc getTableName()
172     {
173         return m_tableName;
174     } // getTableName
175

176     /**
177      * Set Where Clause (w/o the WHERE and w/o History).
178      * @param newWhereClause sql where clause
179      * @param onlyCurrentRows only current rows
180      * @param onlyCurrentDays how many days back for current
181      * @return true if where clase set
182      */

183     public boolean setWhereClause(String JavaDoc newWhereClause, boolean onlyCurrentRows, int onlyCurrentDays)
184     {
185         if (m_open)
186         {
187             log.error("setWhereClause - Table already open - ignored");
188             return false;
189         }
190         //
191
m_whereClause = newWhereClause;
192         m_onlyCurrentRows = onlyCurrentRows;
193         m_onlyCurrentDays = onlyCurrentDays;
194         if (m_whereClause == null)
195             m_whereClause = "";
196         return true;
197     } // setWhereClause
198

199     /**
200      * Get Where Clause (w/o the WHERE and w/o History)
201      * @return where clause
202      */

203     public String JavaDoc getWhereClause()
204     {
205         return m_whereClause;
206     } // getWhereClause
207

208     /**
209      * Is History displayed
210      * @return true if history displayed
211      */

212     public boolean isOnlyCurrentRowsDisplayed()
213     {
214         return !m_onlyCurrentRows;
215     } // isHistoryDisplayed
216

217     /**
218      * Set Order Clause (w/o the ORDER BY)
219      * @param newOrderClause sql order by clause
220      */

221     public void setOrderClause(String JavaDoc newOrderClause)
222     {
223         m_orderClause = newOrderClause;
224         if (m_orderClause == null)
225             m_orderClause = "";
226     } // setOrderClause
227

228     /**
229      * Get Order Clause (w/o the ORDER BY)
230      * @return order by clause
231      */

232     public String JavaDoc getOrderClause()
233     {
234         return m_orderClause;
235     } // getOrderClause
236

237     /**
238      * Assemble & store
239      * m_SQL and m_countSQL
240      * @return m_SQL
241      */

242     private String JavaDoc createSelectSql()
243     {
244         if (m_fields.size() == 0 || m_tableName == null || m_tableName.equals(""))
245             return "";
246
247         // Create SELECT Part
248
StringBuffer JavaDoc select = new StringBuffer JavaDoc("SELECT ");
249         for (int i = 0; i < m_fields.size(); i++)
250         {
251             if (i > 0)
252                 select.append(",");
253             MField field = (MField)m_fields.get(i);
254             select.append(field.getColumnName());
255         }
256         //
257
select.append(" FROM ").append(m_tableName);
258         m_SQL_Select = select.toString();
259         m_SQL_Count = "SELECT COUNT(*) FROM " + m_tableName;
260         //
261

262         StringBuffer JavaDoc where = new StringBuffer JavaDoc("");
263         // WHERE
264
if (m_whereClause.length() > 0)
265         {
266             where.append(" WHERE ");
267             if (m_whereClause.indexOf("@") == -1)
268                 where.append(m_whereClause);
269             else // replace variables
270
where.append(Env.parseContext(m_ctx, m_WindowNo, m_whereClause, false));
271         }
272         if (m_onlyCurrentRows)
273         {
274             if (where.toString().indexOf(" WHERE ") == -1)
275                 where.append(" WHERE ");
276             else
277                 where.append(" AND ");
278             // Show only unprocessed or the one updated within x days
279
where.append("(Processed='N' OR Updated>SysDate-").append(m_onlyCurrentDays).append(")");
280         }
281
282         // RO/RW Access
283
m_SQL = m_SQL_Select + where.toString();
284         m_SQL_Count += where.toString();
285         if (m_withAccessControl)
286         {
287             boolean ro = MRole.SQL_RO;
288         // if (!m_readOnly)
289
// ro = MRole.SQL_RW;
290
m_SQL = MRole.getDefault(m_ctx, false).addAccessSQL(m_SQL,
291                 m_tableName, MRole.SQL_FULLYQUALIFIED, MRole.SQL_RO);
292             m_SQL_Count = MRole.getDefault(m_ctx, false).addAccessSQL(m_SQL_Count,
293                 m_tableName, MRole.SQL_FULLYQUALIFIED, MRole.SQL_RO);
294         }
295
296         // ORDER BY
297
if (!m_orderClause.equals(""))
298             m_SQL += " ORDER BY " + m_orderClause;
299         //
300
log.debug("createSelectSql - " + m_SQL_Count);
301         Env.setContext(m_ctx, m_WindowNo, m_TabNo, "SQL", m_SQL);
302         return m_SQL;
303     } // createSelectSql
304

305     /**
306      * Add Field to Table
307      * @param field field
308      */

309     public void addField (MField field)
310     {
311         log.debug ("addField (" + m_tableName + ") - " + field.getColumnName());
312         if (m_open)
313         {
314             log.error("addField - Table already open - ignored: " + field.getColumnName());
315             return;
316         }
317         if (!MRole.getDefault(m_ctx, false).isColumnAccess (m_AD_Table_ID, field.getAD_Column_ID(), true))
318         {
319             log.debug ("addField - No Column Access " + field.getColumnName());
320             return;
321         }
322         // Set Index for RowID column
323
if (field.getDisplayType() == DisplayType.RowID)
324             m_indexRowIDColumn = m_fields.size();
325         // Set Index for Key column
326
if (field.isKey())
327             m_indexKeyColumn = m_fields.size();
328         else if (field.getColumnName().equals("IsActive"))
329             m_indexActiveColumn = m_fields.size();
330         else if (field.getColumnName().equals("Processed"))
331             m_indexProcessedColumn = m_fields.size();
332         else if (field.getColumnName().equals("AD_Client_ID"))
333             m_indexClientColumn = m_fields.size();
334         else if (field.getColumnName().equals("AD_Org_ID"))
335             m_indexOrgColumn = m_fields.size();
336         //
337
m_fields.add(field);
338     } // addColumn
339

340     /**
341      * Returns database column name
342      *
343      * @param index the column being queried
344      * @return column name
345      */

346     public String JavaDoc getColumnName (int index)
347     {
348         if (index < 0 || index > m_fields.size())
349         {
350             log.error("getColumnName - invalid index=" + index);
351             return "";
352         }
353         //
354
MField field = (MField)m_fields.get(index);
355         return field.getColumnName();
356     } // getColumnName
357

358     /**
359      * Returns a column given its name.
360      *
361      * @param columnName string containing name of column to be located
362      * @return the column index with <code>columnName</code>, or -1 if not found
363      */

364     public int findColumn (String JavaDoc columnName)
365     {
366         for (int i = 0; i < m_fields.size(); i++)
367         {
368             MField field = (MField)m_fields.get(i);
369             if (columnName.equals(field.getColumnName()))
370                 return i;
371         }
372         return -1;
373     } // findColumn
374

375     /**
376      * Returns Class of database column/field
377      *
378      * @param index the column being queried
379      * @return the class
380      */

381     public Class JavaDoc getColumnClass (int index)
382     {
383         if (index < 0 || index >= m_fields.size())
384         {
385             log.error("getColumnClass - invalid index=" + index);
386             return null;
387         }
388         MField field = (MField)m_fields.get(index);
389         return DisplayType.getClass(field.getDisplayType(), false);
390     } // getColumnClass
391

392     /**
393      * Set Select Clause Parameter.
394      * Assumes that you set parameters starting from index zero
395      * @param index index
396      * @param parameter parameter
397      */

398     public void setParameterSELECT (int index, Object JavaDoc parameter)
399     {
400         if (index >= m_parameterSELECT.size())
401             m_parameterSELECT.add(parameter);
402         else
403             m_parameterSELECT.set(index, parameter);
404     } // setParameterSELECT
405

406     /**
407      * Set Where Clause Parameter.
408      * Assumes that you set parameters starting from index zero
409      * @param index index
410      * @param parameter parameter
411      */

412     public void setParameterWHERE (int index, Object JavaDoc parameter)
413     {
414         if (index >= m_parameterWHERE.size())
415             m_parameterWHERE.add(parameter);
416         else
417             m_parameterWHERE.set(index, parameter);
418     } // setParameterWHERE
419

420
421     /**
422      * Get Column at index
423      * @param index index
424      * @return MField
425      */

426     protected MField getField (int index)
427     {
428         if (index < 0 || index >= m_fields.size())
429             return null;
430         return (MField)m_fields.get(index);
431     } // getColumn
432

433     /**
434      * Return Columns with Indentifier (ColumnName)
435      * @param identifier column name
436      * @return MField
437      */

438     protected MField getField (String JavaDoc identifier)
439     {
440         if (identifier == null || identifier.length() == 0)
441             return null;
442         int cols = m_fields.size();
443         for (int i = 0; i < cols; i++)
444         {
445             MField field = (MField)m_fields.get(i);
446             if (identifier.equalsIgnoreCase(field.getColumnName()))
447                 return field;
448         }
449     // log.error ("getField - not found: '" + identifier + "'");
450
return null;
451     } // getField
452

453
454     /*************************************************************************/
455
456     /**
457      * Open Database.
458      * if already opened, data is refreshed
459      *
460      * @return true if success
461      */

462     public boolean open ()
463     {
464         log.info("open");
465         if (m_open)
466         {
467             log.debug("open - already open");
468             dataRefreshAll();
469             return true;
470         }
471
472         // create m_SQL and m_countSQL
473
createSelectSql();
474         if (m_SQL == null || m_SQL.equals(""))
475         {
476             log.error("open - No SQL");
477             return false;
478         }
479
480         // Start Loading
481
m_loader = new Loader();
482         m_rowCount = m_loader.open();
483         m_buffer = new ArrayList(m_rowCount+10);
484         m_sort = new ArrayList(m_rowCount+10);
485         if (m_rowCount > 0)
486             m_loader.start();
487         else
488             m_loader.close();
489         m_open = true;
490         //
491
m_changed = false;
492         m_rowChanged = -1;
493         return true;
494     } // open
495

496     /**
497      * Wait until async loader of Table and Lookup Fields is complete
498      * Used for performance tests
499      */

500     public void loadComplete()
501     {
502         // Wait for loader
503
if (m_loader != null)
504         {
505             if (m_loader.isAlive())
506             {
507                 try
508                 {
509                     m_loader.join();
510                 }
511                 catch (InterruptedException JavaDoc ie)
512                 {
513                     log.error("loadComplete - join interrupted", ie);
514                 }
515             }
516         }
517         // wait for field lookup loaders
518
for (int i = 0; i < m_fields.size(); i++)
519         {
520             MField field = (MField)m_fields.get(i);
521             field.lookupLoadComplete();
522         }
523     } // loadComplete
524

525     /**
526      * Is Loading
527      * @return true if loading
528      */

529     public boolean isLoading()
530     {
531         if (m_loader != null && m_loader.isAlive())
532             return true;
533         return false;
534     } // isLoading
535

536     /**
537      * Is it open?
538      * @return true if opened
539      */

540     public boolean isOpen()
541     {
542         return m_open;
543     } // isOpen
544

545     /**
546      * Close Resultset
547      * @param finalCall final call
548      */

549     public void close (boolean finalCall)
550     {
551         if (!m_open)
552             return;
553         log.debug("close - final=" + finalCall);
554
555         // remove listeners
556
if (finalCall)
557         {
558             m_dataStatusListeners.clear();
559             EventListener evl[] = listenerList.getListeners(TableModelListener.class);
560             for (int i = 0; i < evl.length; i++)
561                 listenerList.remove(TableModelListener.class, evl[i]);
562             VetoableChangeListener vcl[] = m_vetoableChangeSupport.getVetoableChangeListeners();
563             for (int i = 0; i < vcl.length; i++)
564                 m_vetoableChangeSupport.removeVetoableChangeListener(vcl[i]);
565         }
566
567         // Stop loader
568
while (m_loader != null && m_loader.isAlive())
569         {
570             log.debug("close - interrupting Loader");
571             m_loader.interrupt();
572             try
573             {
574                 Thread.sleep(200); // .2 second
575
}
576             catch (InterruptedException JavaDoc ie)
577             {}
578         }
579
580         if (!m_inserting)
581             dataSave(true);
582
583         if (m_buffer != null)
584             m_buffer.clear();
585         m_buffer = null;
586         if (m_sort != null)
587             m_sort.clear();
588         m_sort = null;
589
590         if (finalCall)
591             dispose();
592
593         // Fields are disposed from MTab
594
log.debug("close - complete");
595         m_open = false;
596     } // close
597

598     /**
599      * Dispose MTable.
600      * Called by close-final
601      */

602     private void dispose()
603     {
604         // MFields
605
for (int i = 0; i < m_fields.size(); i++)
606             ((MField)m_fields.get(i)).dispose();
607         m_fields.clear();
608         m_fields = null;
609         //
610
m_dataStatusListeners = null;
611         m_vetoableChangeSupport = null;
612         //
613
m_parameterSELECT.clear();
614         m_parameterSELECT = null;
615         m_parameterWHERE.clear();
616         m_parameterWHERE = null;
617         // clear data arrays
618
m_buffer = null;
619         m_sort = null;
620         m_rowData = null;
621         m_oldValue = null;
622         m_loader = null;
623     } // dispose
624

625     /**
626      * Get total database column count (displayed and not displayed)
627      * @return column count
628      */

629     public int getColumnCount()
630     {
631         return m_fields.size();
632     } // getColumnCount
633

634     /**
635      * Get (displayed) field count
636      * @return field count
637      */

638     public int getFieldCount()
639     {
640         return m_fields.size();
641     } // getFieldCount
642

643     /**
644      * Return number of rows
645      * @return Number of rows or 0 if not opened
646      */

647     public int getRowCount()
648     {
649         return m_rowCount;
650     } // getRowCount
651

652     /**
653      * Set the Column to determine the color of the row
654      * @param columnName column name
655      */

656     public void setColorColumn (String JavaDoc columnName)
657     {
658         m_indexColorColumn = findColumn(columnName);
659     } // setColorColumn
660

661     /**
662      * Get ColorCode for Row.
663      * <pre>
664      * If numerical value in compare column is
665      * negative = -1,
666      * positive = 1,
667      * otherwise = 0
668      * </pre>
669      * @see #setColorColumn
670      * @param row row
671      * @return color code
672      */

673     public int getColorCode (int row)
674     {
675         if (m_indexColorColumn == -1)
676             return 0;
677         Object JavaDoc data = getValueAt(row, m_indexColorColumn);
678         // We need to have a Number
679
if (data == null || !(data instanceof BigDecimal))
680             return 0;
681         int cmp = Env.ZERO.compareTo(data);
682         if (cmp > 0)
683             return -1;
684         if (cmp < 0)
685             return 1;
686         return 0;
687     } // getColorCode
688

689
690     /**
691      * Sort Entries by Column.
692      * <p>
693      * actually the rows are not sorted, just the access pointer ArrayList
694      * with the same size as m_buffer with MSort entities
695      * @param col col
696      * @param ascending ascending
697      */

698     public void sort (int col, boolean ascending)
699     {
700         log.info("sort #" + col + " " + ascending);
701         if (getRowCount() == 0)
702             return;
703         MField field = getField (col);
704         // RowIDs are not sorted
705
if (field.getDisplayType() == DisplayType.RowID)
706             return;
707         boolean isLookup = DisplayType.isLookup(field.getDisplayType());
708
709         // fill MSort entities with data entity
710
for (int i = 0; i < m_sort.size(); i++)
711         {
712             MSort sort = (MSort)m_sort.get(i);
713             Object JavaDoc[] rowData = (Object JavaDoc[])m_buffer.get(sort.index);
714             if (isLookup)
715                 sort.data = field.getLookup().getDisplay(rowData[col]); // lookup
716
else
717                 sort.data = rowData[col]; // data
718
}
719
720         // sort it
721
MSort sort = new MSort(0, null);
722         sort.setSortAsc(ascending);
723         Collections.sort(m_sort, sort);
724         // update UI
725
fireTableDataChanged();
726         // Info detected by MTab.dataStatusChanged and current row set to 0
727
fireDataStatusIEvent("Sorted");
728     } // sort
729

730     /**
731      * Get Key ID or -1 of none
732      * @param row row
733      * @return ID or -1
734      */

735     public int getKeyID (int row)
736     {
737     // Log.info("MTable.getKeyID - row=" + row + ", keyColIdx=" + m_indexKeyColumn);
738
if (m_indexKeyColumn != -1)
739         {
740             try
741             {
742                 Integer JavaDoc ii = (Integer JavaDoc)getValueAt(row, m_indexKeyColumn);
743                 if (ii == null)
744                     return -1;
745                 return ii.intValue();
746             }
747             catch (Exception JavaDoc e) // Alpha Key
748
{
749                 return -1;
750             }
751         }
752         return -1;
753     } // getKeyID
754

755     /**
756      * Get Key ColumnName
757      * @return key column name
758      */

759     public String JavaDoc getKeyColumnName()
760     {
761         if (m_indexKeyColumn != -1)
762             return getColumnName(m_indexKeyColumn);
763         return "";
764     } // getKeyColumnName
765

766     /**
767      * Get Selected ROWID or null, if no RowID exists
768      * @param row row
769      * @return ROWID
770      */

771     public Object JavaDoc getRowID (int row)
772     {
773         Object JavaDoc[] rid = getRID(row);
774         if (rid == null)
775             return null;
776         return rid[0];
777     } // getSelectedRowID
778

779     /**
780      * Get RowID Structure [0]=RowID, [1]=Selected, [2]=ID.
781      * <p>
782      * Either RowID or ID is populated (views don't have RowID)
783      * @param row row
784      * @return RowID
785      */

786     public Object JavaDoc[] getRID (int row)
787     {
788         if (m_indexRowIDColumn == -1 || row < 0 || row >= getRowCount())
789             return null;
790         return (Object JavaDoc[])getValueAt(row, m_indexRowIDColumn);
791     } // getRID
792

793     /**
794      * Find Row with RowID
795      * @param RowID row id or oid
796      * @return number of row or 0 if not found
797      */

798     public int getRow (Object JavaDoc RowID)
799     {
800         if (RowID == null)
801             return 0;
802
803         // the value to find
804
String JavaDoc find = RowID.toString();
805
806         // Wait a bit to load rows
807
if (m_loader != null && m_loader.isAlive())
808         {
809             try
810             {
811                 Thread.sleep(250); // 1/4 second
812
}
813             catch (InterruptedException JavaDoc ie)
814             {}
815         }
816
817         // Build search vector
818
int size = m_sort.size(); // may still change
819
ArrayList search = new ArrayList(size);
820         for (int i = 0; i < size; i++)
821         {
822             Object JavaDoc[] r = (Object JavaDoc[])getValueAt(i, 0);
823             String JavaDoc s = r[0].toString();
824             MSort so = new MSort(i, s);
825             search.add(so);
826         }
827
828         // Sort it
829
MSort sort = new MSort(0, null);
830         Collections.sort(search, sort);
831
832         // Find it
833
int index = Collections.binarySearch(search, find, sort);
834         if (index < 0) // not found
835
{
836             search.clear();
837             return 0;
838         }
839         // Get Info
840
MSort result = (MSort)search.get(index);
841         // Clean up
842
search.clear();
843         return result.index;
844     } // getRow
845

846
847     /*************************************************************************/
848
849     /**
850      * Get Value in Resultset
851      * @param row row
852      * @param col col
853      * @return Object of that row/column
854      */

855     public Object JavaDoc getValueAt (int row, int col)
856     {
857     // Log.trace(Log.l4_Data, "MTable.getValueAt r=" + row + " c=" + col);
858
if (!m_open || row < 0 || col < 0 || row >= m_rowCount)
859         {
860         // Log.trace(Log.l5_DData, "Out of bounds - Open=" + m_open + ", RowCount=" + m_rowCount);
861
return null;
862         }
863
864         // need to wait for data read into buffer
865
int loops = 0;
866         while (row >= m_buffer.size() && m_loader.isAlive() && loops < 15)
867         {
868             log.debug("getValueAt - waiting for loader row=" + row + ", size=" + m_buffer.size());
869             try
870             {
871                 Thread.sleep(500); // 1/2 second
872
}
873             catch (InterruptedException JavaDoc ie)
874             {}
875             loops++;
876         }
877
878         // empty buffer
879
if (row >= m_buffer.size())
880         {
881         // Log.trace(Log.l5_DData, "Empty buffer");
882
return null;
883         }
884
885         // return Data item
886
MSort sort = (MSort)m_sort.get(row);
887         Object JavaDoc[] rowData = (Object JavaDoc[])m_buffer.get(sort.index);
888         // out of bounds
889
if (rowData == null || col > rowData.length)
890         {
891         // Log.trace(Log.l5_DData, "No data or Column out of bounds");
892
return null;
893         }
894         return rowData[col];
895     } // getValueAt
896

897     /**
898      * Indicate that there will be a change
899      * @param changed changed
900      */

901     public void setChanged (boolean changed)
902     {
903         // Can we edit?
904
if (!m_open || m_readOnly)
905             return;
906
907         // Indicate Change
908
m_changed = changed;
909         if (!changed)
910             m_rowChanged = -1;
911         fireDataStatusIEvent("");
912     } // setChanged
913

914     /**
915      * Set Value in data and update MField.
916      * (called directly or from JTable.editingStopped())
917      *
918      * @param value value to assign to cell
919      * @param row row index of cell
920      * @param col column index of cell
921      */

922     public final void setValueAt (Object JavaDoc value, int row, int col)
923     {
924         setValueAt (value, row, col, false);
925     } // setValueAt
926

927     /**
928      * Set Value in data and update MField.
929      * (called directly or from JTable.editingStopped())
930      *
931      * @param value value to assign to cell
932      * @param row row index of cell
933      * @param col column index of cell
934      * @param force force setting new value
935      */

936     public final void setValueAt (Object JavaDoc value, int row, int col, boolean force)
937     {
938         // Can we edit?
939
if (!m_open || m_readOnly // not accessible
940
|| row < 0 || col < 0 // invalid index
941
|| col == 0 // cannot change ID
942
|| m_rowCount == 0) // no rows
943
return;
944
945         dataSave(row, false);
946
947         // Has anything changed?
948
Object JavaDoc oldValue = getValueAt(row, col);
949         if (!force && (
950             (oldValue == null && value == null)
951             || (oldValue != null && oldValue.equals(value))
952             || (oldValue != null && value != null && oldValue.toString().equals(value.toString()))
953             ))
954             return;
955
956         log.debug("setValueAt r=" + row + " c=" + col + " = " + value + " (" + oldValue + ")");
957
958         // Save old value
959
m_oldValue = new Object JavaDoc[3];
960         m_oldValue[0] = new Integer JavaDoc(row);
961         m_oldValue[1] = new Integer JavaDoc(col);
962         m_oldValue[2] = oldValue;
963
964         // Set Data item
965
MSort sort = (MSort)m_sort.get(row);
966         Object JavaDoc[] rowData = (Object JavaDoc[])m_buffer.get(sort.index);
967         m_rowChanged = row;
968
969         // Selection
970
if (col == 0)
971         {
972             rowData[col] = value;
973             m_buffer.set(sort.index, rowData);
974             return;
975         }
976
977         // save original value - shallow copy
978
if (m_rowData == null)
979         {
980             int size = m_fields.size();
981             m_rowData = new Object JavaDoc[size];
982             for (int i = 0; i < size; i++)
983                 m_rowData[i] = rowData[i];
984         }
985
986         // save & update
987
rowData[col] = value;
988         m_buffer.set(sort.index, rowData);
989         // update Table
990
fireTableCellUpdated(row, col);
991         // update MField
992
MField field = getField(col);
993         field.setValue(value, m_inserting);
994         // inform
995
DataStatusEvent evt = createDSE();
996         evt.setChangedColumn(col);
997         fireDataStatusChanged(evt);
998     } // setValueAt
999

1000    /**
1001     * Get Old Value
1002     * @param row row
1003     * @param col col
1004     * @return old value
1005     */

1006    public Object JavaDoc getOldValue (int row, int col)
1007    {
1008        if (m_oldValue == null)
1009            return null;
1010        if (((Integer JavaDoc)m_oldValue[0]).intValue() == row
1011                && ((Integer JavaDoc)m_oldValue[1]).intValue() == col)
1012            return m_oldValue[2];
1013        return null;
1014    } // getOldValue
1015

1016    /**
1017     * Check if the current row needs to be saved.
1018     * @param onlyRealChange if true the value of a field was actually changed
1019     * (e.g. for new records, which have not been changed) - default false
1020     * @return true it needs to be saved
1021     */

1022    public boolean needSave(boolean onlyRealChange)
1023    {
1024        return needSave(m_rowChanged, onlyRealChange);
1025    } // needSave
1026

1027    /**
1028     * Check if the row needs to be saved.
1029     * - only if nothing was changed
1030     * @return true it needs to be saved
1031     */

1032    public boolean needSave()
1033    {
1034        return needSave(m_rowChanged, false);
1035    } // needSave
1036

1037    /**
1038     * Check if the row needs to be saved.
1039     * - only when row changed
1040     * - only if nothing was changed
1041     * @param newRow to check
1042     * @return true it needs to be saved
1043     */

1044    public boolean needSave(int newRow)
1045    {
1046        return needSave(newRow, false);
1047    } // needSave
1048

1049    /**
1050     * Check if the row needs to be saved.
1051     * - only when row changed
1052     * - only if nothing was changed
1053     * @param newRow to check
1054     * @param onlyRealChange if true the value of a field was actually changed
1055     * (e.g. for new records, which have not been changed) - default false
1056     * @return true it needs to be saved
1057     */

1058    public boolean needSave(int newRow, boolean onlyRealChange)
1059    {
1060        log.debug("needSave - Row=" + newRow +
1061            ", Changed=" + m_rowChanged + "/" + m_changed); // m_rowChanged set in setValueAt
1062
// nothing done
1063
if (!m_changed && m_rowChanged == -1)
1064            return false;
1065        // E.g. New unchanged records
1066
if (m_changed && m_rowChanged == -1 && onlyRealChange)
1067            return false;
1068        // same row
1069
if (newRow == m_rowChanged)
1070            return false;
1071
1072        return true;
1073    } // needSave
1074

1075    /*************************************************************************/
1076
1077    public static final char SAVE_OK = 'O'; // the only OK condition
1078
public static final char SAVE_ERROR = 'E';
1079    public static final char SAVE_ACCESS = 'A';
1080    public static final char SAVE_MANDATORY = 'M';
1081    public static final char SAVE_ABORT = 'U';
1082
1083    /**
1084     * Check if it needs to be saved and save it.
1085     * @param newRow row
1086     * @param manualCmd manual command to save
1087     * @return true if not needed to be saved or successful saved
1088     */

1089    public boolean dataSave (int newRow, boolean manualCmd)
1090    {
1091        log.debug("dataSave - Row=" + newRow +
1092            ", Changed=" + m_rowChanged + "/" + m_changed); // m_rowChanged set in setValueAt
1093
// nothing done
1094
if (!m_changed && m_rowChanged == -1)
1095            return true;
1096        // same row, don't save yet
1097
if (newRow == m_rowChanged)
1098            return true;
1099
1100        return (dataSave(manualCmd) == SAVE_OK);
1101    } // dataSave
1102

1103    /**
1104     * Save unconditional.
1105     * @param manualCmd if true, no vetoable PropertyChange will be fired for save confirmation
1106     * @return OK Or Error condition
1107     * Error info (Access*, FillMandatory, SaveErrorNotUnique,
1108     * SaveErrorRowNotFound, SaveErrorDataChanged) is saved in the log
1109     */

1110    public char dataSave (boolean manualCmd)
1111    {
1112        // cannot save
1113
if (!m_open)
1114        {
1115            log.warn ("dataSave - Error - Open=" + m_open);
1116            return SAVE_ERROR;
1117        }
1118        // no need - not changed - row not positioned - no Value changed
1119
if (m_rowChanged == -1)
1120        {
1121            log.info("dataSave - NoNeed - Changed=" + m_changed + ", Row=" + m_rowChanged);
1122        // return SAVE_ERROR;
1123
if (!manualCmd)
1124                return SAVE_OK;
1125        }
1126        // Value not changed
1127
if (m_rowData == null)
1128        {
1129            log.warn ("dataSave - Error - DataNull=" + (m_rowData == null));
1130            return SAVE_ERROR;
1131        }
1132
1133        if (m_readOnly)
1134        /** @todo save changes when processed */
1135        // If Processed - not editable (Find always editable) -> ok for changing payment terms, etc.
1136
{
1137            log.warn("dataSave - IsReadOnly - ignored");
1138            dataIgnore();
1139            return SAVE_ACCESS;
1140        }
1141
1142        // row not positioned - no Value changed
1143
if (m_rowChanged == -1)
1144        {
1145            if (m_newRow != -1) // new row and nothing changed - might be OK
1146
m_rowChanged = m_newRow;
1147            else
1148            {
1149                fireDataStatusEEvent("SaveErrorNoChange", "");
1150                return SAVE_ERROR;
1151            }
1152        }
1153
1154        // Can we change?
1155
int[] co = getClientOrg(m_rowChanged);
1156        int AD_Client_ID = co[0];
1157        int AD_Org_ID = co[1];
1158        if (!MRole.getDefault(m_ctx, false).canUpdate(AD_Client_ID, AD_Org_ID, m_AD_Table_ID, true))
1159        {
1160            fireDataStatusEEvent(Log.retrieveError());
1161            dataIgnore();
1162            return SAVE_ACCESS;
1163        }
1164
1165        log.info("dataSave - Saving row " + m_rowChanged);
1166
1167        // inform about data save action, if not manually initiated
1168
try
1169        {
1170            if (!manualCmd)
1171                m_vetoableChangeSupport.fireVetoableChange(PROPERTY, 0, m_rowChanged);
1172        }
1173        catch (PropertyVetoException pve)
1174        {
1175            log.warn("dataSave - " + pve.getMessage());
1176            dataIgnore();
1177            return SAVE_ABORT;
1178        }
1179
1180        // get updated row data
1181
MSort sort = (MSort)m_sort.get(m_rowChanged);
1182        Object JavaDoc[] rowData = (Object JavaDoc[])m_buffer.get(sort.index);
1183
1184        // Check Mandatory
1185
String JavaDoc missingColumns = getMandatory(rowData);
1186        if (missingColumns.length() != 0)
1187        {
1188            fireDataStatusEEvent("FillMandatory", missingColumns);
1189            return SAVE_MANDATORY;
1190        }
1191
1192        /**
1193         * Update row *****
1194         */

1195        boolean error = false;
1196        //
1197
String JavaDoc is = null;
1198        final String JavaDoc ERROR = "ERROR: ";
1199        final String JavaDoc INFO = "Info: ";
1200
1201        // SQL with specific where clause
1202
String JavaDoc SQL = m_SQL_Select;
1203        StringBuffer JavaDoc refreshSQL = new StringBuffer JavaDoc(SQL).append(" WHERE "); // to be completed when key known
1204
StringBuffer JavaDoc singleRowWHERE = new StringBuffer JavaDoc();
1205        StringBuffer JavaDoc multiRowWHERE = new StringBuffer JavaDoc();
1206        // Create SQL & RowID
1207
Object JavaDoc rowID = null;
1208        if (m_inserting)
1209        {
1210            SQL += " WHERE 1=2";
1211        }
1212        else
1213        {
1214            // FOR UPDATE causes - ORA-01002 fetch out of sequence
1215
SQL += " WHERE ROWID=?";
1216            rowID = getRowID (m_rowChanged);
1217        }
1218        PreparedStatement pstmt = null;
1219        try
1220        {
1221            pstmt = DB.prepareStatement (SQL, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
1222            if (!m_inserting)
1223                DB.getDatabase().setRowID(pstmt, 1, rowID);
1224            ResultSet rs = pstmt.executeQuery();
1225            // only one row
1226
if (!(m_inserting || rs.next()))
1227            {
1228                rs.close();
1229                pstmt.close();
1230                fireDataStatusEEvent("SaveErrorRowNotFound", "");
1231                dataRefresh(m_rowChanged);
1232                return SAVE_ERROR;
1233            }
1234
1235            Object JavaDoc[] rowDataDB = null;
1236            // Prepare
1237
boolean manualUpdate = ResultSet.CONCUR_READ_ONLY == rs.getConcurrency();
1238            if (manualUpdate)
1239                createUpdateSqlReset();
1240            if (m_inserting)
1241            {
1242                if (manualUpdate)
1243                    log.debug("dataSave - prepare inserting ... manual");
1244                else
1245                {
1246                    log.debug ("dataSave - prepare inserting ... RowSet");
1247                    rs.moveToInsertRow ();
1248                }
1249            }
1250            else
1251            {
1252                log.debug("dataSave - prepare updating ... manual=" + manualUpdate);
1253                // get current Data in DB
1254
rowDataDB = readData (rs);
1255            }
1256
1257            /** Data:
1258             * m_rowData = original Data
1259             * rowData = updated Data
1260             * rowDataDB = current Data in DB
1261             * 1) Difference between original & updated Data? N:next
1262             * 2) Difference between original & current Data? Y:don't update
1263             * 3) Update current Data
1264             * 4) Refresh to get last Data (changed by trigger, ...)
1265             */

1266
1267            // Constants for Created/Updated(By)
1268
Timestamp now = new Timestamp(System.currentTimeMillis());
1269            int user = Env.getContextAsInt(m_ctx, "#AD_User_ID");
1270
1271            /**
1272             * for every column
1273             */

1274            int size = m_fields.size();
1275            for (int col = 0; col < size; col++)
1276            {
1277                MField field = (MField)m_fields.get (col);
1278                String JavaDoc columnName = field.getColumnName ();
1279            // log.debug ("dataSave - " + columnName + "= " + m_rowData[col] + " <> DB: " + rowDataDB[col] + " -> " + rowData[col]);
1280

1281                // RowID
1282
if (field.getDisplayType () == DisplayType.RowID)
1283                    ; // ignore
1284

1285                // New Key
1286
else if (field.isKey () && m_inserting)
1287                {
1288                    if (columnName.endsWith ("_ID"))
1289                    {
1290                        int insertID = DB.getKeyNextNo (m_ctx, m_WindowNo, m_tableName);
1291                        if (manualUpdate)
1292                            createUpdateSql (columnName, String.valueOf (insertID));
1293                        else
1294                            rs.updateInt (col + 1, insertID); // ***
1295
singleRowWHERE.append (columnName).append ("=").append (insertID);
1296                        //
1297
is = INFO + columnName + " -> " + insertID + " (Key)";
1298                    }
1299                    else // Key with String value
1300
{
1301                        String JavaDoc str = rowData[col].toString ();
1302                        if (manualUpdate)
1303                            createUpdateSql (columnName, DB.TO_STRING (str));
1304                        else
1305                            rs.updateString (col + 1, str); // ***
1306
singleRowWHERE.append (columnName).append ("=").append (DB.TO_STRING(str));
1307                        //
1308
is = INFO + columnName + " -> " + str + " (StringKey)";
1309                    }
1310                    log.debug ("dataSave - " + is);
1311                } // New Key
1312

1313                // New DocumentNo
1314
else if (columnName.equals ("DocumentNo"))
1315                {
1316                    boolean newDocNo = false;
1317                    String JavaDoc docNo = (String JavaDoc)rowData[col];
1318                    // we need to have a doc number
1319
if (docNo == null || docNo.length () == 0)
1320                        newDocNo = true;
1321                        // Preliminary ID from CalloutSystem
1322
else if (docNo.startsWith ("<") && docNo.endsWith (">"))
1323                        newDocNo = true;
1324
1325                    if (newDocNo || m_inserting)
1326                    {
1327                        String JavaDoc insertDoc = null;
1328                        // always overwrite if insering with mandatory DocType DocNo
1329
if (m_inserting)
1330                            insertDoc = DB.getDocumentNo (m_ctx, m_WindowNo, m_tableName, true);
1331                        log.debug ("dataSave - DocumentNo entered=" + docNo + ", DocTypeInsert=" + insertDoc + ", newDocNo=" + newDocNo);
1332                        // can we use entered DocNo?
1333
if (insertDoc == null || insertDoc.length () == 0)
1334                        {
1335                            if (!newDocNo && docNo != null && docNo.length () > 0)
1336                                insertDoc = docNo;
1337                            else // get a number from DocType or Table
1338
insertDoc = DB.getDocumentNo (m_ctx, m_WindowNo, m_tableName, false);
1339                        }
1340                        // There might not be an automatic document no for this document
1341
if (insertDoc == null || insertDoc.length () == 0)
1342                        {
1343                            // in case DB function did not return a value
1344
if (docNo != null && docNo.length () != 0)
1345                                insertDoc = (String JavaDoc)rowData[col];
1346                            else
1347                            {
1348                                error = true;
1349                                is = ERROR + field.getColumnName () + "= " + rowData[col] + " NO DocumentNo";
1350                                log.debug ("dataSave - " + is);
1351                                break;
1352                            }
1353                        }
1354                        //
1355
if (manualUpdate)
1356                            createUpdateSql (columnName, DB.TO_STRING (insertDoc));
1357                        else
1358                            rs.updateString (col + 1, insertDoc); // ***
1359
//
1360
is = INFO + columnName + " -> " + insertDoc + " (DocNo)";
1361                        log.debug ("dataSave - " + is);
1362                    }
1363                } // New DocumentNo
1364

1365                // New Value(key)
1366
else if (columnName.equals ("Value") && m_inserting)
1367                {
1368                    String JavaDoc value = (String JavaDoc)rowData[col];
1369                    // Get from Sequence, if not entered
1370
if (value == null || value.length () == 0)
1371                    {
1372                        value = DB.getDocumentNo (m_ctx, m_WindowNo, m_tableName, false);
1373                        // No DocumentNo
1374
if (value == null || value.length () == 0)
1375                        {
1376                            error = true;
1377                            is = ERROR + field.getColumnName () + "= " + rowData[col]
1378                                 + " No DocumentNo";
1379                            log.debug ("dataSave - " + is);
1380                            break;
1381                        }
1382                    }
1383                    if (manualUpdate)
1384                        createUpdateSql (columnName, DB.TO_STRING (value));
1385                    else
1386                        rs.updateString (col + 1, value); // ***
1387
//
1388
is = INFO + columnName + " -> " + value + " (Value)";
1389                    log.debug ("dataSave - " + is);
1390                } // New Value(key)
1391

1392                // Updated - check database
1393
else if (columnName.equals ("Updated"))
1394                {
1395                    if (m_compareDB && !m_inserting && !m_rowData[col].equals (rowDataDB[col])) // changed
1396
{
1397                        error = true;
1398                        is = ERROR + field.getColumnName () + "= " + m_rowData[col]
1399                             + " != DB: " + rowDataDB[col];
1400                        log.debug ("dataSave - " + is);
1401                        break;
1402                    }
1403                    if (manualUpdate)
1404                        createUpdateSql (columnName, DB.TO_DATE (now, false));
1405                    else
1406                        rs.updateTimestamp (col + 1, now); // ***
1407
//
1408
is = INFO + "Updated/By -> " + now + " - " + user;
1409                    log.debug ("dataSave - " + is);
1410                } // Updated
1411

1412                // UpdatedBy - update
1413
else if (columnName.equals ("UpdatedBy"))
1414                {
1415                    if (manualUpdate)
1416                        createUpdateSql (columnName, String.valueOf (user));
1417                    else
1418                        rs.updateInt (col + 1, user); // ***
1419
} // UpdatedBy
1420

1421                // Created
1422
else if (m_inserting && columnName.equals ("Created"))
1423                {
1424                    if (manualUpdate)
1425                        createUpdateSql (columnName, DB.TO_DATE (now, false));
1426                    else
1427                        rs.updateTimestamp (col + 1, now); // ***
1428
} // Created
1429

1430                // CreatedBy
1431
else if (m_inserting && columnName.equals ("CreatedBy"))
1432                {
1433                    if (manualUpdate)
1434                        createUpdateSql (columnName, String.valueOf (user));
1435                    else
1436                        rs.updateInt (col + 1, user); // ***
1437
} // CreatedBy
1438

1439                // Nothing changed & null
1440
else if (m_rowData[col] == null && rowData[col] == null)
1441                {
1442                    if (m_inserting)
1443                    {
1444                        if (manualUpdate)
1445                            createUpdateSql (columnName, "NULL");
1446                        else
1447                            rs.updateNull (col + 1); // ***
1448
is = INFO + columnName + "= NULL";
1449                        log.debug ("dataSave - " + is);
1450                    }
1451                }
1452
1453                // *** Data changed ***
1454
else if (m_inserting
1455                  || (m_rowData[col] == null && rowData[col] != null)
1456                  || (m_rowData[col] != null && rowData[col] == null)
1457                  || !m_rowData[col].equals (rowData[col])) // changed
1458
{
1459                    // Original == DB
1460
if (m_inserting || !m_compareDB
1461                      || (m_rowData[col] == null && rowDataDB[col] == null)
1462                      || (m_rowData[col] != null && m_rowData[col].equals (rowDataDB[col])))
1463                    {
1464                        String JavaDoc type = "String";
1465                        if (rowData[col] == null)
1466                        {
1467                            if (manualUpdate)
1468                                createUpdateSql (columnName, "NULL");
1469                            else
1470                                rs.updateNull (col + 1); // ***
1471
}
1472                        // Numeric - BigDecimal
1473
else if (DisplayType.isNumeric (field.getDisplayType ()))
1474                        {
1475                            if (manualUpdate)
1476                                createUpdateSql (columnName, rowData[col].toString ());
1477                            else
1478                                rs.updateBigDecimal (col + 1, (BigDecimal)rowData[col]); // ***
1479
type = "Number";
1480                        }
1481                        // ID - int
1482
else if (DisplayType.isID (field.getDisplayType ()))
1483                        {
1484                            int number = 0;
1485                            try
1486                            {
1487                                number = Integer.parseInt (rowData[col].toString ());
1488                                if (manualUpdate)
1489                                    createUpdateSql (columnName, String.valueOf (number));
1490                                else
1491                                    rs.updateInt (col + 1, number); // ***
1492
}
1493                            catch (Exception JavaDoc e) // could also be a String (AD_Language, AD_Message)
1494
{
1495                                if (manualUpdate)
1496                                    createUpdateSql (columnName, DB.TO_STRING (rowData[col].toString ()));
1497                                else
1498                                    rs.updateString (col + 1, rowData[col].toString ()); // ***
1499
}
1500                            type = "ID";
1501                        }
1502                        // Date - Timestamp
1503
else if (DisplayType.isDate (field.getDisplayType ()))
1504                        {
1505                            if (manualUpdate)
1506                                createUpdateSql (columnName, DB.TO_DATE ((Timestamp)rowData[col], false));
1507                            else
1508                                rs.updateTimestamp (col + 1, (Timestamp)rowData[col]); // ***
1509
type = "Date";
1510                        }
1511                        // LOB
1512
else if (field.getDisplayType() == DisplayType.TextLong)
1513                        {
1514                            if (manualUpdate)
1515                                createUpdateSql (columnName, "empty_clob()");
1516                        // else
1517
// rs.updateClob(col + 1, ); // ***
1518
type = "CLOB";
1519                        }
1520                        // String and others
1521
else
1522                        {
1523                            if (manualUpdate)
1524                                createUpdateSql (columnName, DB.TO_STRING (rowData[col].toString ()));
1525                            else
1526                                rs.updateString (col + 1, rowData[col].toString ()); // ***
1527
}
1528                        //
1529
is = INFO + columnName + "= " + m_rowData[col]
1530                             + " -> " + rowData[col] + " (" + type + ")";
1531                        log.debug ("dataSave - " + is);
1532                    }
1533                    // Original != DB
1534
else
1535                    {
1536                        error = true;
1537                        is = ERROR + field.getColumnName () + "= " + m_rowData[col]
1538                             + " != DB: " + rowDataDB[col] + " -> " + rowData[col];
1539                        log.debug ("dataSave - " + is);
1540                    }
1541                } // Data changed
1542

1543                // Single Key - retrieval sql
1544
if (field.isKey() && !m_inserting)
1545                {
1546                    if (rowData[col] == null)
1547                        throw new RuntimeException JavaDoc("dataSave - Key " + columnName + " is NULL");
1548                    if (columnName.endsWith ("_ID"))
1549                        singleRowWHERE.append (columnName).append ("=").append (rowData[col]);
1550                    else
1551                        singleRowWHERE.append (columnName).append ("=").append (DB.TO_STRING(rowData[col].toString()));
1552                }
1553                // MultiKey Inserting - retrieval sql
1554
if (field.isParent())
1555                {
1556                    if (rowData[col] == null)
1557                        throw new RuntimeException JavaDoc("dataSave - MultiKey Parent " + columnName + " is NULL");
1558                    if (multiRowWHERE.length() != 0)
1559                        multiRowWHERE.append(" AND ");
1560                    if (columnName.endsWith ("_ID"))
1561                        multiRowWHERE.append (columnName).append ("=").append (rowData[col]);
1562                    else
1563                        multiRowWHERE.append (columnName).append ("=").append (DB.TO_STRING(rowData[col].toString()));
1564                }
1565            } // for every column
1566

1567            if (error)
1568            {
1569                if (manualUpdate)
1570                    createUpdateSqlReset();
1571                else
1572                    rs.cancelRowUpdates();
1573                rs.close();
1574                pstmt.close();
1575                fireDataStatusEEvent("SaveErrorDataChanged", "");
1576                dataRefresh(m_rowChanged);
1577                return SAVE_ERROR;
1578            }
1579
1580            /**
1581             * Save to Database
1582             */

1583            String JavaDoc whereClause = singleRowWHERE.toString();
1584            if (whereClause.length() == 0)
1585                whereClause = multiRowWHERE.toString();
1586            //
1587
if (m_inserting)
1588            {
1589                log.debug("dataSave - inserting ...");
1590                if (manualUpdate)
1591                {
1592                    String JavaDoc sql = createUpdateSql(true, null);
1593                    int no = DB.executeUpdateEx(sql);
1594                    if (no != 1)
1595                        log.error("dataSave - insert #=" + no + " - " + sql);
1596                }
1597                else
1598                    rs.insertRow();
1599            }
1600            else
1601            {
1602                log.debug("dataSave - updating ... " + whereClause);
1603                if (manualUpdate)
1604                {
1605                    String JavaDoc sql = createUpdateSql(false, whereClause);
1606                    int no = DB.executeUpdateEx(sql);
1607                    if (no != 1)
1608                        log.error("dataSave - update #=" + no + " - " + sql);
1609                }
1610                else
1611                    rs.updateRow();
1612            }
1613
1614            log.debug("dataSave - committing ...");
1615            DB.commit(true);
1616            // data may be updated by trigger after update
1617
if (m_inserting || manualUpdate)
1618            {
1619                rs.close();
1620                pstmt.close();
1621                // need to re-read row to get ROWID, Key, DocumentNo
1622
log.debug("dataSave - reading ... " + whereClause);
1623                refreshSQL.append(whereClause);
1624                pstmt = DB.prepareStatement(refreshSQL.toString());
1625                rs = pstmt.executeQuery();
1626                if (rs.next())
1627                {
1628                    rowDataDB = readData(rs);
1629                    // update buffer
1630
m_buffer.set(sort.index, rowDataDB);
1631                    fireTableRowsUpdated(m_rowChanged, m_rowChanged);
1632                }
1633                else
1634                    log.error("dataSave - inserted row not found");
1635            }
1636            else
1637            {
1638                log.debug("dataSave - refreshing ...");
1639                rs.refreshRow(); // only use
1640
rowDataDB = readData(rs);
1641                // update buffer
1642
m_buffer.set(sort.index, rowDataDB);
1643                fireTableRowsUpdated(m_rowChanged, m_rowChanged);
1644            }
1645            //
1646
rs.close();
1647            pstmt.close();
1648            pstmt = null;
1649        }
1650        catch (SQLException e)
1651        {
1652            try
1653            {
1654                if (pstmt != null)
1655                  pstmt.close ();
1656                pstmt = null;
1657            }
1658            catch (Exception JavaDoc ex)
1659            {
1660            }
1661
1662            String JavaDoc msg = "SaveError";
1663            if (e.getErrorCode() == 1) // Unique Constraint
1664
{
1665                log.error ("dataSave - Key Not Unique", e);
1666                msg = "SaveErrorNotUnique";
1667            }
1668            else
1669                log.error ("dataSave\nSQL= " + SQL, e);
1670            fireDataStatusEEvent(msg, e.getLocalizedMessage());
1671            return SAVE_ERROR;
1672        }
1673
1674        // everything ok
1675
m_rowData = null;
1676        m_changed = false;
1677        m_compareDB = true;
1678        m_rowChanged = -1;
1679        m_newRow = -1;
1680        m_inserting = false;
1681        fireDataStatusIEvent("Saved");
1682        //
1683
log.info("dataSave - fini");
1684        return SAVE_OK;
1685    } // dataSave
1686

1687    private ArrayList m_createSqlColumn = new ArrayList();
1688    private ArrayList m_createSqlValue = new ArrayList();
1689
1690    /**
1691     * Prepare SQL creation
1692     * @param columnName column name
1693     * @param value value
1694     */

1695    private void createUpdateSql (String JavaDoc columnName, String JavaDoc value)
1696    {
1697        m_createSqlColumn.add(columnName);
1698        m_createSqlValue.add(value);
1699        if (Log.isTraceLevel(10))
1700            log.debug("createUpdateSql #" + m_createSqlColumn.size()
1701                + " - " + columnName + "=" + value);
1702    } // createUpdateSQL
1703

1704    /**
1705     * Create update/insert SQL
1706     * @param insert true if insert - update otherwise
1707     * @param whereClause where clause for update
1708     * @return sql statement
1709     */

1710    private String JavaDoc createUpdateSql (boolean insert, String JavaDoc whereClause)
1711    {
1712        StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
1713        if (insert)
1714        {
1715            sb.append("INSERT INTO ").append(m_tableName).append(" (");
1716            for (int i = 0; i < m_createSqlColumn.size(); i++)
1717            {
1718                if (i != 0)
1719                    sb.append(",");
1720                sb.append(m_createSqlColumn.get(i));
1721            }
1722            sb.append(") VALUES ( ");
1723            for (int i = 0; i < m_createSqlValue.size(); i++)
1724            {
1725                if (i != 0)
1726                    sb.append(",");
1727                sb.append(m_createSqlValue.get(i));
1728            }
1729            sb.append(")");
1730        }
1731        else
1732        {
1733            sb.append("UPDATE ").append(m_tableName).append(" SET ");
1734            for (int i = 0; i < m_createSqlColumn.size(); i++)
1735            {
1736                if (i != 0)
1737                    sb.append(",");
1738                sb.append(m_createSqlColumn.get(i)).append("=").append(m_createSqlValue.get(i));
1739            }
1740            sb.append(" WHERE ").append(whereClause);
1741        }
1742        log.debug("createUpdateSql=" + sb.toString());
1743        // reset
1744
createUpdateSqlReset();
1745        return sb.toString();
1746    } // createUpdateSql
1747

1748    /**
1749     * Reset Update Data
1750     */

1751    private void createUpdateSqlReset()
1752    {
1753        m_createSqlColumn = new ArrayList();
1754        m_createSqlValue = new ArrayList();
1755    } // createUpdateSqlReset
1756

1757    /**
1758     * Get Mandatory empty columns
1759     * @param rowData row data
1760     * @return String with missing column headers/labels
1761     */

1762    private String JavaDoc getMandatory(Object JavaDoc[] rowData)
1763    {
1764        // see also => ProcessParameter.saveParameter
1765
StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
1766
1767        // Check all columns
1768
int size = m_fields.size();
1769        for (int i = 0; i < size; i++)
1770        {
1771            MField field = (MField)m_fields.get(i);
1772            if (field.isMandatory(true)) // check context
1773
{
1774                if (rowData[i] == null || rowData[i].toString().length() == 0)
1775                {
1776                    field.setInserting (true); // set editable otherwise deadlock
1777
field.setError(true);
1778                    if (sb.length() > 0)
1779                        sb.append(", ");
1780                    sb.append(field.getHeader());
1781                }
1782                else
1783                    field.setError(false);
1784            }
1785        }
1786
1787        if (sb.length() == 0)
1788            return "";
1789        return sb.toString();
1790    } // getMandatory
1791

1792    /*************************************************************************/
1793
1794    /**
1795     * New Record after current Row
1796     * @param currentRow row
1797     * @param copyCurrent copy
1798     * @return true if success -
1799     * Error info (Access*, AccessCannotInsert) is saved in the log
1800     */

1801    public boolean dataNew (int currentRow, boolean copyCurrent)
1802    {
1803        log.info("dataNew - Current=" + currentRow + ", Copy=" + copyCurrent);
1804        // see if we need to save
1805
dataSave(-2, false);
1806
1807        // Read only
1808
if (m_readOnly)
1809        {
1810            fireDataStatusEEvent("AccessCannotInsert", "");
1811            return false;
1812        }
1813        /** @todo No TableLevel */
1814        // || !Access.canViewInsert(m_ctx, m_WindowNo, tableLevel, true, true))
1815
// fireDataStatusEvent(Log.retrieveError());
1816

1817        m_inserting = true;
1818        // Create default data
1819
int size = m_fields.size();
1820        m_rowData = new Object JavaDoc[size]; // "original" data
1821
Object JavaDoc[] rowData = new Object JavaDoc[size];
1822        // fill data
1823
if (copyCurrent)
1824        {
1825            MSort sort = (MSort) m_sort.get(currentRow);
1826            Object JavaDoc[] origData = (Object JavaDoc[])m_buffer.get(sort.index);
1827            for (int i = 0; i < size; i++)
1828                rowData[i] = origData[i];
1829        }
1830        else // new
1831
{
1832            for (int i = 0; i < size; i++)
1833            {
1834                MField field = (MField)m_fields.get(i);
1835                rowData[i] = field.getDefault();
1836                field.setValue(rowData[i], m_inserting);
1837            }
1838        }
1839        m_changed = true;
1840        m_compareDB = true;
1841        m_rowChanged = -1; // only changed in setValueAt
1842
m_newRow = currentRow + 1;
1843        // if there is no record, the current row could be 0 (and not -1)
1844
if (m_buffer.size() < m_newRow)
1845            m_newRow = m_buffer.size();
1846
1847        // add Data at end of buffer
1848
MSort sort = new MSort(m_buffer.size(), null); // index
1849
m_buffer.add(rowData);
1850        // add Sort pointer
1851
m_sort.add(m_newRow, sort);
1852        m_rowCount++;
1853
1854        // inform
1855
log.debug("dataNew - Current=" + currentRow + ", New=" + m_newRow);
1856        fireTableRowsInserted(m_newRow, m_newRow);
1857        fireDataStatusIEvent(copyCurrent ? "UpdateCopied" : "Inserted");
1858        log.debug("dataNew - Current=" + currentRow + ", New=" + m_newRow + " - complete");
1859        return true;
1860    } // dataNew
1861

1862
1863    /*************************************************************************/
1864
1865    /**
1866     * Delete Data
1867     * @param row row
1868     * @return true if success -
1869     * Error info (Access*, AccessNotDeleteable, DeleteErrorDependent,
1870     * DeleteError) is saved in the log
1871     */

1872    public boolean dataDelete (int row)
1873    {
1874        log.info("dataDelete - " + row);
1875        if (row < 0)
1876            return false;
1877        Object JavaDoc rowID = getRowID(row);
1878        if (rowID == null)
1879            return false;
1880
1881        // Tab R/O
1882
if (m_readOnly)
1883        {
1884            fireDataStatusEEvent("AccessCannotDelete", "");
1885            return false;
1886        }
1887
1888        // Is this record deletable?
1889
if (!m_deleteable
1890            || (m_indexProcessedColumn > 0 // processed
1891
&& "Y".equals(getValueAt(row, m_indexProcessedColumn)) // = Y
1892
&& !m_tableName.startsWith("I_") )) // ignore Import tables
1893
{
1894            fireDataStatusEEvent("AccessNotDeleteable", "");
1895            return false;
1896        }
1897        /** @todo check Access */
1898        // fireDataStatusEvent(Log.retrieveError());
1899

1900
1901        //
1902
StringBuffer JavaDoc SQL = new StringBuffer JavaDoc("DELETE ");
1903        SQL.append(m_tableName).append(" WHERE ROWID=?");
1904        int no = 0;
1905        try
1906        {
1907            PreparedStatement pstmt = DB.prepareStatement(SQL.toString());
1908            DB.getDatabase().setRowID(pstmt, 1, rowID);
1909            no = pstmt.executeUpdate();
1910            pstmt.close();
1911        }
1912        catch (SQLException e)
1913        {
1914            log.error ("dataDelete", e);
1915            String JavaDoc msg = "DeleteError";
1916            if (e.getErrorCode() == 2292) // Child Record Found
1917
msg = "DeleteErrorDependent";
1918            fireDataStatusEEvent(msg, e.getLocalizedMessage());
1919            return false;
1920        }
1921        // Check Result
1922
if (no != 1)
1923        {
1924            log.error("dataDelete - Number of deleted rows = " + no);
1925            return false;
1926        }
1927
1928        // Get Sort
1929
MSort sort = (MSort)m_sort.get(row);
1930        int bufferRow = sort.index;
1931
1932        // Delete row in Buffer and shifts all below up
1933
m_buffer.remove(bufferRow);
1934        m_rowCount--;
1935
1936        // Delete row in Sort
1937
m_sort.remove(row);
1938        // Correct pointer in Sort
1939
for (int i = 0; i < m_sort.size(); i++)
1940        {
1941            MSort ptr = (MSort)m_sort.get(i);
1942            if (ptr.index > bufferRow)
1943                ptr.index--; // move up
1944
}
1945
1946        // inform
1947
m_changed = false;
1948        m_rowChanged = -1;
1949        fireTableRowsDeleted(row, row);
1950        fireDataStatusIEvent("Deleted");
1951        log.debug("dataDelete - " + row + " complete");
1952        return true;
1953    } // dataDelete
1954

1955    /*************************************************************************/
1956
1957    /**
1958     * Ignore changes
1959     */

1960    public void dataIgnore()
1961    {
1962        log.info("dataIgnore - Inserting=" + m_inserting);
1963        if (!m_inserting && !m_changed && m_rowChanged < 0)
1964        {
1965            log.debug("dataIgnore - Nothing to ignore");
1966            return;
1967        }
1968
1969        // Inserting - delete new row
1970
if (m_inserting)
1971        {
1972            // Get Sort
1973
MSort sort = (MSort)m_sort.get(m_newRow);
1974            int bufferRow = sort.index;
1975            // Delete row in Buffer and shifts all below up
1976
m_buffer.remove(bufferRow);
1977            m_rowCount--;
1978            // Delete row in Sort
1979
m_sort.remove(m_newRow); // pintint to the last column, so no adjustment
1980
//
1981
m_changed = false;
1982            m_rowData = null;
1983            m_rowChanged = -1;
1984            m_inserting = false;
1985            // inform
1986
fireTableRowsDeleted(m_newRow, m_newRow);
1987        }
1988        else
1989        {
1990            // update buffer
1991
if (m_rowData != null)
1992            {
1993                MSort sort = (MSort)m_sort.get(m_rowChanged);
1994                m_buffer.set(sort.index, m_rowData);
1995            }
1996            m_changed = false;
1997            m_rowData = null;
1998            m_rowChanged = -1;
1999            m_inserting = false;
2000            // inform
2001
// fireTableRowsUpdated(m_rowChanged, m_rowChanged); >> messes up display?? (clearSelection)
2002
}
2003        m_newRow = -1;
2004        fireDataStatusIEvent("Ignored");
2005    } // dataIgnore
2006

2007
2008    /**
2009     * Refresh Row - ignore changes
2010     * @param row row
2011     */

2012    public void dataRefresh (int row)
2013    {
2014        log.info("dataRefresh " + row);
2015
2016        if (row < 0)
2017            return;
2018        Object JavaDoc rowID = getRowID(row);
2019        if (rowID == null)
2020            return;
2021
2022        // ignore
2023
dataIgnore();
2024
2025        // Create SQL
2026
String JavaDoc SQL = m_SQL_Select + " WHERE ROWID=?";
2027        MSort sort = (MSort)m_sort.get(row);
2028        Object JavaDoc[] rowDataDB = null;
2029        try
2030        {
2031            PreparedStatement pstmt = DB.prepareStatement(SQL);
2032            DB.getDatabase().setRowID(pstmt, 1, rowID);
2033            ResultSet rs = pstmt.executeQuery();
2034            // only one row
2035
if (rs.next())
2036                rowDataDB = readData(rs);
2037            rs.close();
2038            pstmt.close();
2039        }
2040        catch (SQLException e)
2041        {
2042            log.error ("dataRefresh\nSQL=" + SQL, e);
2043            fireTableRowsUpdated(row, row);
2044            fireDataStatusEEvent("RefreshError", "");
2045            return;
2046        }
2047
2048        // update buffer
2049
m_buffer.set(sort.index, rowDataDB);
2050        // info
2051
m_rowData = null;
2052        m_changed = false;
2053        m_rowChanged = -1;
2054        m_inserting = false;
2055        fireTableRowsUpdated(row, row);
2056        fireDataStatusIEvent("Refreshed");
2057    } // dataRefresh
2058

2059
2060    /**
2061     * Refresh all Rows - ignore changes
2062     */

2063    public void dataRefreshAll()
2064    {
2065        log.info("dataRefreshAll");
2066        dataIgnore();
2067        close(false);
2068        open();
2069        // Info
2070
m_rowData = null;
2071        m_changed = false;
2072        m_rowChanged = -1;
2073        m_inserting = false;
2074        fireTableDataChanged();
2075        fireDataStatusIEvent("Refreshed");
2076    } // dataRefreshAll
2077

2078
2079    /**
2080     * Requery with new whereClause
2081     * @param whereClause sql where clause
2082     * @param onlyCurrentRows only current rows
2083     * @param onlyCurrentDays how many days back
2084     * @return true if success
2085     */

2086    public boolean dataRequery (String JavaDoc whereClause, boolean onlyCurrentRows, int onlyCurrentDays)
2087    {
2088        log.info("dataRequery - " + whereClause + "; OnlyCurrent=" + onlyCurrentRows);
2089        close(false);
2090        m_onlyCurrentDays = onlyCurrentDays;
2091        setWhereClause(whereClause, onlyCurrentRows, m_onlyCurrentDays);
2092        open();
2093        // Info
2094
m_rowData = null;
2095        m_changed = false;
2096        m_rowChanged = -1;
2097        m_inserting = false;
2098        fireTableDataChanged();
2099        fireDataStatusIEvent("Refreshed");
2100        return true;
2101    } // dataRequery
2102

2103
2104    /*************************************************************************/
2105
2106    /**
2107     * Is Cell Editable
2108     *
2109     * @param row the row index being queried
2110     * @param col the column index being queried
2111     * @return true, if editable
2112     */

2113    public boolean isCellEditable (int row, int col)
2114    {
2115    // Log.trace(Log.l6_Database, "MTable.isCellEditable");
2116
// Make Rows selectable
2117
if (col == 0)
2118            return true;
2119
2120        // Entire Table not editable
2121
if (m_readOnly)
2122            return false;
2123        // Key & ID not editable
2124
if (col == m_indexRowIDColumn || col == m_indexKeyColumn)
2125            return false;
2126        /** @todo check link columns */
2127
2128        // Check column range
2129
if (col < 0 && col >= m_fields.size())
2130            return false;
2131        // Row
2132
if (!isRowEditable(row))
2133            return false;
2134        // IsActive Column always editable if no processed exists
2135
if (col == m_indexActiveColumn && m_indexProcessedColumn == -1)
2136            return true;
2137
2138        // Column
2139
return ((MField)m_fields.get(col)).isEditable(false);
2140    } // IsCellEditable
2141

2142
2143    /**
2144     * Is Current Row Editable
2145     * @param row row
2146     * @return true if editable
2147     */

2148    public boolean isRowEditable (int row)
2149    {
2150    // Log.trace(Log.l6_Database, "MTable.isRowEditable");
2151
// Entire Table not editable or no row
2152
if (m_readOnly || row < 0)
2153            return false;
2154        // If not Active - not editable
2155
if (m_indexActiveColumn > 0 && "N".equals(getValueAt(row, m_indexActiveColumn))) // && m_TabNo != Find.s_TabNo)
2156
return false;
2157        // If Processed - not editable (Find always editable)
2158
if (m_indexProcessedColumn > 0 && "Y".equals(getValueAt(row, m_indexProcessedColumn))) // && m_TabNo != Find.s_TabNo)
2159
return false;
2160        //
2161
int[] co = getClientOrg(row);
2162        int AD_Client_ID = co[0];
2163        int AD_Org_ID = co[1];
2164        return MRole.getDefault(m_ctx, false).canUpdate(AD_Client_ID, AD_Org_ID, m_AD_Table_ID, false);
2165    } // isRowEditable
2166

2167    /**
2168     * Get Client Org for row
2169     * @param row row
2170     * @return array [0] = Client [1] = Org - a value of -1 is not defined/found
2171     */

2172    private int[] getClientOrg (int row)
2173    {
2174        int AD_Client_ID = -1;
2175        if (m_indexClientColumn != -1)
2176        {
2177            Integer JavaDoc ii = (Integer JavaDoc)getValueAt(row, m_indexClientColumn);
2178            if (ii != null)
2179                AD_Client_ID = ii.intValue();
2180        }
2181        int AD_Org_ID = 0;
2182        if (m_indexOrgColumn != -1)
2183        {
2184            Integer JavaDoc ii = (Integer JavaDoc)getValueAt(row, m_indexOrgColumn);
2185            if (ii != null)
2186                AD_Org_ID = ii.intValue();
2187        }
2188        return new int[] {AD_Client_ID, AD_Org_ID};
2189    } // getClientOrg
2190

2191    /**
2192     * Set entire table as read only
2193     * @param value new read only value
2194     */

2195    public void setReadOnly (boolean value)
2196    {
2197        log.debug("setReadOnly " + value);
2198        m_readOnly = value;
2199    } // setReadOnly
2200

2201    /**
2202     * Is entire Table Read/Only
2203     * @return true if read only
2204     */

2205    public boolean isReadOnly()
2206    {
2207        return m_readOnly;
2208    } // isReadOnly
2209

2210    /**
2211     * Is inserting
2212     * @return true if inserting
2213     */

2214    public boolean isInserting()
2215    {
2216        return m_inserting;
2217    } // isInserting
2218

2219    /**
2220     * Set Compare DB.
2221     * If Set to false, save overwrites the record, regardless of DB changes.
2222     * (When a payment is changed in Sales Order, the payment reversal clears the payment id)
2223     * @param compareDB compare DB - false forces overwrite
2224     */

2225    public void setCompareDB (boolean compareDB)
2226    {
2227        m_compareDB = compareDB;
2228    } // setCompareDB
2229

2230    /**
2231     * Get Compare DB.
2232     * @return false if save overwrites the record, regardless of DB changes
2233     * (false forces overwrite).
2234     */

2235    public boolean getCompareDB ()
2236    {
2237        return m_compareDB;
2238    } // getCompareDB
2239

2240
2241    /**
2242     * Can Table rows be deleted
2243     * @param value new deleteable value
2244     */

2245    public void setDeleteable (boolean value)
2246    {
2247        log.debug("setDeleteable " + value);
2248        m_deleteable = value;
2249    } // setDeleteable
2250

2251    /*************************************************************************/
2252
2253    /**
2254     * Read Data from Recordset
2255     * @param rs result set
2256     * @return Data Array
2257     */

2258    private Object JavaDoc[] readData (ResultSet rs)
2259    {
2260        int size = m_fields.size();
2261        Object JavaDoc[] rowData = new Object JavaDoc[size];
2262        String JavaDoc columnName = null;
2263        int displayType = 0;
2264
2265        try
2266        {
2267            // get row data
2268
for (int j = 0; j < size; j++)
2269            {
2270                // Column Info
2271
MField field = (MField)m_fields.get(j);
2272                columnName = field.getColumnName();
2273                displayType = field.getDisplayType();
2274                // Number
2275
if (DisplayType.isNumeric(displayType))
2276                    rowData[j] = rs.getBigDecimal(j+1); // BigDecimal
2277
// ID, Lookup (UpdatedBy is a numeric column)
2278
else if (DisplayType.isID(displayType) && (columnName.endsWith("_ID")) || columnName.endsWith("edBy"))
2279                {
2280                    rowData[j] = new Integer JavaDoc(rs.getInt(j+1)); // Integer
2281
if (rs.wasNull())
2282                        rowData[j] = null;
2283                }
2284                // Date
2285
else if (DisplayType.isDate(displayType))
2286                    rowData[j] = rs.getTimestamp(j+1); // Timestamp
2287
// RowID or Key (and Selection)
2288
else if (displayType == DisplayType.RowID)
2289                {
2290                    Object JavaDoc[] rid = new Object JavaDoc[3];
2291                    if (columnName.equals("ROWID"))
2292                        rid[0] = DB.getDatabase().getRowID(rs, j+1);
2293                    else
2294                        rid[2] = new Integer JavaDoc (rs.getInt(j+1));
2295                    rid[1] = new Boolean JavaDoc(false);
2296                    rowData[j] = rid;
2297                }
2298                // LOB
2299
else if (displayType == DisplayType.TextLong)
2300                {
2301                    Object JavaDoc value = rs.getObject(j+1);
2302                    if (rs.wasNull())
2303                        rowData[j] = null;
2304                    else if (value instanceof Clob)
2305                    {
2306                        Clob lob = (Clob)value;
2307                        long length = lob.length();
2308                        rowData[j] = lob.getSubString(1, (int)length);
2309                    }
2310                }
2311                // String
2312
else
2313                    rowData[j] = rs.getString(j+1); // String
2314
}
2315        }
2316        catch (SQLException e)
2317        {
2318            log.error("readData - " + columnName + ", DT=" + displayType, e);
2319        }
2320        return rowData;
2321    } // readData
2322

2323    /*************************************************************************/
2324
2325    /**
2326     * Remove Data Status Listener
2327     * @param l listener
2328     */

2329    public synchronized void removeDataStatusListener(DataStatusListener l)
2330    {
2331        if (m_dataStatusListeners != null && m_dataStatusListeners.contains(l))
2332        {
2333            Vector v = (Vector) m_dataStatusListeners.clone();
2334            v.removeElement(l);
2335            m_dataStatusListeners = v;
2336        }
2337    } // removeDataStatusListener
2338

2339    /**
2340     * Add Data Status Listener
2341     * @param l listener
2342     */

2343    public synchronized void addDataStatusListener(DataStatusListener l)
2344    {
2345        Vector v = m_dataStatusListeners == null ? new Vector(2) : (Vector) m_dataStatusListeners.clone();
2346        if (!v.contains(l))
2347        {
2348            v.addElement(l);
2349            m_dataStatusListeners = v;
2350        }
2351    } // addDataStatusListener
2352

2353    /**
2354     * Inform Listeners
2355     * @param e event
2356     */

2357    private void fireDataStatusChanged (DataStatusEvent e)
2358    {
2359        if (m_dataStatusListeners != null)
2360        {
2361            Vector listeners = m_dataStatusListeners;
2362            int count = listeners.size();
2363            for (int i = 0; i < count; i++)
2364                ((DataStatusListener) listeners.elementAt(i)).dataStatusChanged(e);
2365        }
2366    } // fireDataStatusChanged
2367

2368    /**
2369     * Create Data Status Event
2370     * @return data status event
2371     */

2372    private DataStatusEvent createDSE()
2373    {
2374        boolean changed = m_changed;
2375        if (m_rowChanged != -1)
2376            changed = true;
2377        DataStatusEvent dse = new DataStatusEvent(this, m_rowCount, changed,
2378            Env.isAutoCommit(m_ctx, m_WindowNo), m_inserting);
2379        return dse;
2380    } // createDSE
2381

2382    /**
2383     * Create and fire Data Status Info Event
2384     * @param AD_Message message
2385     */

2386    protected void fireDataStatusIEvent (String JavaDoc AD_Message)
2387    {
2388        DataStatusEvent e = createDSE();
2389        e.setInfo(AD_Message, "", false);
2390        fireDataStatusChanged (e);
2391    } // fireDataStatusEvent
2392

2393    /**
2394     * Create and fire Data Status Error Event
2395     * @param AD_Message message
2396     * @param info info
2397     */

2398    protected void fireDataStatusEEvent (String JavaDoc AD_Message, String JavaDoc info)
2399    {
2400    // org.compiere.util.Trace.printStack();
2401
//
2402
DataStatusEvent e = createDSE();
2403        e.setInfo(AD_Message, info, true);
2404        Log.saveError(AD_Message, info);
2405        fireDataStatusChanged (e);
2406    } // fireDataStatusEvent
2407

2408    /**
2409     * Create and fire Data Status Event (from Error Log)
2410     * @param errorLog error log info
2411     */

2412    protected void fireDataStatusEEvent (ValueNamePair errorLog)
2413    {
2414        if (errorLog != null)
2415            fireDataStatusEEvent (errorLog.getValue(), errorLog.getName());
2416    } // fireDataStatusEvent
2417

2418    /*************************************************************************/
2419
2420    /**
2421     * Remove Vetoable change listener for row changes
2422     * @param l listener
2423     */

2424    public synchronized void removeVetoableChangeListener(VetoableChangeListener l)
2425    {
2426        m_vetoableChangeSupport.removeVetoableChangeListener(l);
2427    } // removeVetoableChangeListener
2428

2429    /**
2430     * Add Vetoable change listener for row changes
2431     * @param l listener
2432     */

2433    public synchronized void addVetoableChangeListener(VetoableChangeListener l)
2434    {
2435        m_vetoableChangeSupport.addVetoableChangeListener(l);
2436    } // addVetoableChangeListener
2437

2438    /**
2439     * Fire Vetoable change listener for row changes
2440     * @param e event
2441     * @throws PropertyVetoException
2442     */

2443    protected void fireVetoableChange(PropertyChangeEvent e) throws java.beans.PropertyVetoException JavaDoc
2444    {
2445        m_vetoableChangeSupport.fireVetoableChange(e);
2446    } // fireVetoableChange
2447

2448    /**
2449     * toString
2450     * @return String representation
2451     */

2452    public String JavaDoc toString()
2453    {
2454        return new StringBuffer JavaDoc("MTable[").append(m_tableName)
2455            .append(",WindowNo=").append(m_WindowNo)
2456            .append(",Tab=").append(m_TabNo).append("]").toString();
2457    } // toString
2458

2459
2460    /*************************************************************************/
2461
2462    /**
2463     * ASync Loader
2464     */

2465    class Loader extends Thread JavaDoc implements Serializable JavaDoc
2466    {
2467        /**
2468         * Construct Loader
2469         */

2470        public Loader()
2471        {
2472            super("TLoader");
2473        } // Loader
2474

2475        private PreparedStatement m_pstmt = null;
2476        private ResultSet m_rs = null;
2477
2478        /**
2479         * Open ResultSet
2480         * @return number of records
2481         */

2482        private int open()
2483        {
2484        // Log.trace(Log.l4_Data, "MTable Loader.open");
2485
// Get Number of Rows
2486
int rows = 0;
2487            try
2488            {
2489                PreparedStatement pstmt = DB.prepareStatement(m_SQL_Count);
2490                setParameter (pstmt, true);
2491                ResultSet rs = pstmt.executeQuery();
2492                if (rs.next())
2493                    rows = rs.getInt(1);
2494                rs.close();
2495                pstmt.close();
2496            }
2497            catch (SQLException e0)
2498            {
2499                // Zoom Query may have invalid where clause
2500
if (e0.getErrorCode() == 904) // ORA-00904: "C_x_ID": invalid identifier
2501
log.warn("Loader.open Count - " + e0.getLocalizedMessage() + "\nSQL=" + m_SQL_Count);
2502                else
2503                    log.error ("Loader.open Count SQL=" + m_SQL_Count, e0);
2504                return 0;
2505            }
2506
2507            // open Statement (closed by Loader.close)
2508
try
2509            {
2510                m_pstmt = DB.prepareStatement(m_SQL);
2511            // m_pstmt.setFetchSize(20);
2512
setParameter (m_pstmt, false);
2513                m_rs = m_pstmt.executeQuery();
2514            }
2515            catch (SQLException e)
2516            {
2517                log.error ("Loader.open\nFull SQL=" + m_SQL, e);
2518                return 0;
2519            }
2520            StringBuffer JavaDoc info = new StringBuffer JavaDoc("Rows=");
2521            info.append(rows);
2522            if (rows == 0)
2523                info.append(" - ").append(m_SQL_Count);
2524            log.debug("Loader.open - " + info.toString());
2525            return rows;
2526        } // open
2527

2528        /**
2529         * Close RS and Statement
2530         */

2531        private void close()
2532        {
2533        // Log.trace(Log.l4_Data, "MTable Loader.close");
2534
try
2535            {
2536                if (m_rs != null)
2537                    m_rs.close();
2538                if (m_pstmt != null)
2539                    m_pstmt.close();
2540            }
2541            catch (SQLException e)
2542            {
2543                log.error ("Loader.closeRS", e);
2544            }
2545            m_rs = null;
2546            m_pstmt = null;
2547        } // close
2548

2549        /**
2550         * Fill Buffer to include Row
2551         */

2552        public void run()
2553        {
2554            log.info("Loader.run");
2555            if (m_rs == null)
2556                return;
2557
2558            try
2559            {
2560                while(m_rs.next())
2561                {
2562                    if (this.isInterrupted())
2563                    {
2564                        log.debug("Loader interrupted");
2565                        close();
2566                        return;
2567                    }
2568                    // Get Data
2569
Object JavaDoc[] rowData = readData(m_rs);
2570                    // add Data
2571
MSort sort = new MSort(m_buffer.size(), null); // index
2572
m_buffer.add(rowData);
2573                    m_sort.add(sort);
2574
2575                    // Statement all 250 rows & sleep
2576
if (m_buffer.size() % 250 == 0)
2577                    {
2578                        // give the other processes a chance
2579
try
2580                        {
2581                            yield();
2582                            sleep(10); // .01 second
2583
}
2584                        catch (InterruptedException JavaDoc ie)
2585                        {
2586                            log.debug("Loader interrupted while sleeping");
2587                            close();
2588                            return;
2589                        }
2590                        DataStatusEvent evt = createDSE();
2591                        evt.setLoading(m_buffer.size());
2592                        fireDataStatusChanged(evt);
2593                    }
2594                } // while(rs.next())
2595
}
2596            catch (SQLException e)
2597            {
2598                log.error ("Loader.run", e);
2599            }
2600            close();
2601            fireDataStatusIEvent("");
2602        } // run
2603

2604        /**
2605         * Set Parameter for Query.
2606         * elements must be Integer, BigDecimal, String (default)
2607         * @param pstmt prepared statement
2608         * @param countSQL count
2609         */

2610        private void setParameter (PreparedStatement pstmt, boolean countSQL)
2611        {
2612            if (m_parameterSELECT.size() == 0 && m_parameterWHERE.size() == 0)
2613                return;
2614            try
2615            {
2616                int pos = 1; // position in Statement
2617
// Select Clause Parameters
2618
for (int i = 0; !countSQL && i < m_parameterSELECT.size(); i++)
2619                {
2620                    Object JavaDoc para = m_parameterSELECT.get(i);
2621                    if (para != null)
2622                        log.debug("setParameter Select " + i + "=" + para);
2623                    //
2624
if (para == null)
2625                        ;
2626                    else if (para instanceof Integer JavaDoc)
2627                    {
2628                        Integer JavaDoc ii = (Integer JavaDoc)para;
2629                        pstmt.setInt (pos++, ii.intValue());
2630                    }
2631                    else if (para instanceof BigDecimal)
2632                        pstmt.setBigDecimal (pos++, (BigDecimal)para);
2633                    else
2634                        pstmt.setString(pos++, para.toString());
2635                }
2636                // Where Clause Parameters
2637
for (int i = 0; i < m_parameterWHERE.size(); i++)
2638                {
2639                    Object JavaDoc para = m_parameterWHERE.get(i);
2640                    if (para != null)
2641                        log.debug("setParameter Where " + i + "=" + para);
2642                    //
2643
if (para == null)
2644                        ;
2645                    else if (para instanceof Integer JavaDoc)
2646                    {
2647                        Integer JavaDoc ii = (Integer JavaDoc)para;
2648                        pstmt.setInt (pos++, ii.intValue());
2649                    }
2650                    else if (para instanceof BigDecimal)
2651                        pstmt.setBigDecimal (pos++, (BigDecimal)para);
2652                    else
2653                        pstmt.setString(pos++, para.toString());
2654                }
2655            }
2656            catch (SQLException e)
2657            {
2658                log.error("Loader.setParameter", e);
2659            }
2660        } // setParameter
2661

2662    } // Loader
2663

2664} // MTable
2665
Popular Tags