KickJava   Java API By Example, From Geeks To Geeks.

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


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 java.sql.*;
17 import java.util.*;
18 import java.math.*;
19 import java.text.*;
20 import java.beans.*;
21 import java.io.*;
22
23 import org.compiere.util.*;
24
25 /**
26  * Tab Model.
27  * - a combination of AD_Tab (the display attributes) and AD_Table information.
28  * <p>
29  * The Tab owns also it's Table model
30  * and listens to data changes to update the Field values.
31  *
32  * <p>
33  * The Tab maintains the bound property: CurrentRow
34  *
35  * <pre>
36  * Event Hierarchies:
37  * - dataChanged (from MTable)
38  * - setCurrentRow
39  * - Update all Field Values
40  *
41  * - setValue
42  * - Update Field Value
43  * - Callout
44  * </pre>
45  * @author Jorg Janke
46  * @version $Id: MTab.java,v 1.54 2003/11/06 07:08:06 jjanke Exp $
47  */

48 public final class MTab implements DataStatusListener, Serializable
49 {
50     /**
51      * Create Tab (Model) from Value Object.
52      * <p>
53      * MTab provides a property listener for changed rows and a
54      * DataStatusListener for communicating changes of the underlying data
55      * @param vo Value Object
56      */

57     public MTab(MTabVO vo)
58     {
59         m_vo = vo;
60         // Create MTable
61
m_mTable = new MTable (m_vo.ctx, m_vo.AD_Table_ID, m_vo.TableName, m_vo.WindowNo, m_vo.TabNo, true);
62         m_mTable.setReadOnly(m_vo.IsReadOnly || m_vo.IsView);
63         m_mTable.setDeleteable(m_vo.IsDeleteable);
64         // Load Tab
65
// if (vo.TabNo == 0)
66
initTab(false);
67     // else
68
// {
69
// m_loader = new Loader();
70
// m_loader.setPriority(Thread.MIN_PRIORITY);
71
// m_loader.start();
72
// }
73
// waitLoadCompete();
74
} // M_Tab
75

76     /** Value Object */
77     private MTabVO m_vo;
78
79     /** The Table Model for Query */
80     private MTable m_mTable = null;
81
82     private String JavaDoc m_keyColumnName = "";
83     private String JavaDoc m_linkColumnName = "";
84     private String JavaDoc m_extendedWhere;
85     /** Attachments */
86     private HashMap m_Attachment = null;
87     /** Locks */
88     private ArrayList m_Lock = null;
89
90     /** Current Row */
91     private int m_currentRow = -1;
92
93     /** Property Change */
94     private PropertyChangeSupport m_propertyChangeSupport = new PropertyChangeSupport(this);
95     /** Property Change Type */
96     public static final String JavaDoc PROPERTY = "CurrentRow";
97
98     private Vector m_dataStatusListeners = null;
99     private DataStatusEvent m_DataStatusEvent = null;
100     //
101
private MQuery m_query = new MQuery();
102     private String JavaDoc m_oldQuery = "0=9";
103     private String JavaDoc m_linkValue = "999999";
104
105     /** Order By Array if SortNo 1..2 */
106     private String JavaDoc[] m_OrderBys = new String JavaDoc[2];
107     /** List of Key Parents */
108     private ArrayList m_parents = new ArrayList(2);
109
110     /** Map of ColumnName of source field (key) and the dependent field (value) */
111     private MultiMap m_depOnField = new MultiMap();
112
113     /** Async Loader */
114     private Loader m_loader = null;
115     /** Async Loading complete */
116     private volatile boolean m_loadComplete = false;
117     /** Is Tab Included in other Tab */
118     private boolean m_included = false;
119
120     /*************************************************************************/
121
122     /**
123      * Tab loader for Tabs > 0
124      */

125     class Loader extends Thread JavaDoc
126     {
127         /**
128          * Async Loading of Tab > 0
129          */

130         public void run()
131         {
132             initTab (true);
133         } // run
134
} // Loader
135

136     /**
137      * Wait until load is complete
138      */

139     private void waitLoadCompete()
140     {
141         if (m_loadComplete)
142             return;
143         //
144
m_loader.setPriority(Thread.NORM_PRIORITY);
145         Log.trace(Log.l1_User, "MTab.waitLoadComplete");
146         while (m_loader.isAlive())
147         {
148             try
149             {
150                 Thread.sleep(100); // 1/10 sec
151
}
152             catch (Exception JavaDoc e)
153             {
154                 Log.error("MTab.waitLoadComplete", e);
155             }
156         }
157         Log.trace(Log.l1_User, "MTab.waitLoadComplete - fini");
158     } // waitLoadComplete
159

160     /**
161      * Initialize Tab with record from AD_Tab_v
162      * @param async async
163      * @return true, if correctly initialized (ignored)
164      */

165     private boolean initTab (boolean async)
166     {
167         Log.trace(Log.l3_Util, "MTab.initTab #" + m_vo.TabNo + " - Async=" + async + " - Where=" + m_vo.WhereClause);
168
169         m_extendedWhere = m_vo.WhereClause;
170
171
172         // Get Field Data
173
if (!loadFields())
174         {
175             m_loadComplete = true;
176             return false;
177         }
178
179         // Order By
180
m_mTable.setOrderClause(getOrderByClause(m_vo.onlyCurrentRows));
181
182         if (async)
183             Log.trace(Log.l3_Util, "MTab.initTab #" + m_vo.TabNo + " - Async=" + async, "fini");
184         m_loadComplete = true;
185         return true;
186     } // initTab
187

188     /**
189      * Dispose - clean up resources
190      */

191     protected void dispose()
192     {
193         Log.trace(Log.l3_Util, "MTab.dispose #" + m_vo.TabNo);
194         m_OrderBys = null;
195         //
196
m_parents.clear();
197         m_parents = null;
198         //
199
m_mTable.close (true); // also disposes Fields
200
m_mTable = null;
201         //
202
m_depOnField.clear();
203         m_depOnField = null;
204         if (m_Attachment != null)
205             m_Attachment.clear();
206         m_Attachment = null;
207         //
208
m_vo.Fields.clear();
209         m_vo.Fields = null;
210         m_vo = null;
211     } // dispose
212

213
214     /**
215      * Get Field data and add to MTable, if it's required or displayed.
216      * Reqiored fields are keys, parents, or standard Columns
217      * @return true if fields loaded
218      */

219     private boolean loadFields()
220     {
221         Log.trace(Log.l3_Util, "MTab.loadFields #" + m_vo.TabNo);
222
223         if (m_vo.Fields == null)
224             return false;
225
226         // Add RowID if this is not a view
227
if (!m_vo.IsView)
228         {
229             MField rowID = new MField (MFieldVO.createRowID (m_vo.ctx, m_vo.WindowNo, m_vo.TabNo, m_vo.AD_Window_ID));
230             m_mTable.addField(rowID);
231         }
232
233         // Add Fields
234
for (int f = 0; f < m_vo.Fields.size(); f++)
235         {
236             MFieldVO voF = (MFieldVO)m_vo.Fields.get(f);
237             // Add Fields to Table
238
if (voF != null)
239             {
240                 MField field = new MField (voF);
241                 String JavaDoc columnName = field.getColumnName();
242                 // Record Info
243
if (field.isKey())
244                 {
245                     if (m_vo.IsView) // make it a RowID for selection & update
246
voF.initRowID();
247                     m_keyColumnName = columnName;
248                 }
249                 // Parent Column(s)
250
if (field.isParent())
251                     m_parents.add(columnName);
252                 // Order By
253
if (field.getSortNo() == 1)
254                     m_OrderBys[0] = columnName;
255                 else if (field.getSortNo() == 2)
256                     m_OrderBys[1] = columnName;
257                     // Add field
258
m_mTable.addField(field);
259
260                 // List of ColumnNames, this field is dependent on
261
ArrayList list = field.getDependentOn();
262                 for (int i = 0; i < list.size(); i++)
263                     m_depOnField.put(list.get(i), field); // ColumnName, Field
264
// Add fields all fields are dependent on
265
if (columnName.equals("IsActive") || columnName.equals("Processed"))
266                     m_depOnField.put(columnName, null);
267             }
268         } // for all fields
269

270         // Add Standard Fields
271
if (m_mTable.getField("Created") == null)
272         {
273             MField created = new MField (MFieldVO.createStdField(m_vo.ctx, m_vo.WindowNo, m_vo.TabNo, m_vo.AD_Window_ID, false, true, true));
274             m_mTable.addField(created);
275         }
276         if (m_mTable.getField("CreatedBy") == null)
277         {
278             MField createdBy = new MField (MFieldVO.createStdField(m_vo.ctx, m_vo.WindowNo, m_vo.TabNo, m_vo.AD_Window_ID, false, true, false));
279             m_mTable.addField(createdBy);
280         }
281         if (m_mTable.getField("Updated") == null)
282         {
283             MField updated = new MField (MFieldVO.createStdField(m_vo.ctx, m_vo.WindowNo, m_vo.TabNo, m_vo.AD_Window_ID, false, false, true));
284             m_mTable.addField(updated);
285         }
286         if (m_mTable.getField("UpdatedBy") == null)
287         {
288             MField updatedBy = new MField (MFieldVO.createStdField(m_vo.ctx, m_vo.WindowNo, m_vo.TabNo, m_vo.AD_Window_ID, false, false, false));
289             m_mTable.addField(updatedBy);
290         }
291         return true;
292     } // loadFields
293

294     /**
295      * Get TableModel.
296      * <B>Do not directly communicate with the table model,
297      * but through the methods of this class</B>
298      * @return Table Model
299      */

300     public MTable getTableModel()
301     {
302         return m_mTable;
303     } // getTableModel
304

305     /**
306      * Get Tab Icon
307      * @return Icon
308      */

309     public javax.swing.Icon JavaDoc getIcon()
310     {
311         if (m_vo.AD_Image_ID == 0)
312             return null;
313         //
314
/** @todo Load Image */
315         return null;
316     } // getIcon
317

318     /*************************************************************************/
319
320     /**
321      * Has this field dependents ?
322      * @param columnName column name
323      * @return true if column has dependent
324      */

325     public boolean isDependentOn (String JavaDoc columnName)
326     {
327     // m_depOnField.printToLog();
328
return m_depOnField.containsKey(columnName);
329     } // isDependentOn
330

331     /**
332      * Get dependent fields of columnName
333      * @param columnName column name
334      * @return ArrayList with MFields dependent on columnName
335      */

336     public ArrayList getDependentFieldList (String JavaDoc columnName)
337     {
338         return m_depOnField.getValues(columnName);
339     } // getDependentFieldList
340

341     /*************************************************************************/
342
343     /**
344      * Set Query
345      * @param query query
346      */

347     public void setQuery(MQuery query)
348     {
349         if (query == null)
350             m_query = new MQuery();
351         else
352             m_query = query;
353     } // setQuery
354

355     /**
356      * Get Query
357      * @return query
358      */

359     public MQuery getQuery()
360     {
361         return m_query;
362     } // getQuery
363

364     /**
365      * Is Query Active
366      * @return true if query active
367      */

368     public boolean isQueryActive()
369     {
370         return m_query.isActive();
371     } // isQueryActive
372

373     /**
374      * Enable Events - enable data events of tabs (add listeners)
375      */

376     public void enableEvents()
377     {
378         // Setup Events
379
m_mTable.addDataStatusListener(this);
380     // m_mTable.addTableModelListener(this);
381
} // enableEvents
382

383     /**
384      * Assemble whereClause and query MTable and position to row 0.
385      * <pre>
386      * Scenarios:
387      * - Never opened (full query)
388      * - query changed (full query)
389      * - Detail link value changed (full query)
390      * - otherwise (refreshAll)
391      * </pre>
392      * @param onlyCurrentRows only current rows (1 day)
393      */

394     public void query (boolean onlyCurrentRows)
395     {
396         query (onlyCurrentRows, 1);
397     } // query
398

399     /**
400      * Assemble whereClause and query MTable and position to row 0.
401      * <pre>
402      * Scenarios:
403      * - Never opened (full query)
404      * - query changed (full query)
405      * - Detail link value changed (full query)
406      * - otherwise (refreshAll)
407      * </pre>
408      * @param onlyCurrentRows only current rows
409      * @param onlyCurrentDays if only current row, how many days back
410      */

411     public void query (boolean onlyCurrentRows, int onlyCurrentDays)
412     {
413         Log.trace(Log.l3_Util, "MTab.query #" + m_vo.TabNo + " - Only Current Rows=" + onlyCurrentRows + ", Days=" + onlyCurrentDays);
414         // is it same query?
415
boolean refresh = m_oldQuery.equals(m_query.getWhereClause())
416             && m_vo.onlyCurrentRows == onlyCurrentRows && m_vo.onlyCurrentDays == onlyCurrentDays;
417         m_oldQuery = m_query.getWhereClause();
418         m_vo.onlyCurrentRows = onlyCurrentRows;
419         m_vo.onlyCurrentDays = onlyCurrentDays;
420
421         /**
422          * Set Where Clause
423          */

424         // Tab Where Clause
425
StringBuffer JavaDoc where = new StringBuffer JavaDoc(m_vo.WhereClause);
426         // Detail Query
427
if (isDetail())
428         {
429             String JavaDoc lc = getLinkColumnName();
430             if (lc.equals(""))
431                 Log.error("MTab.query - no link column");
432             else
433             {
434                 String JavaDoc value = Env.getContext(m_vo.ctx, m_vo.WindowNo, lc);
435                 // Same link value?
436
if (refresh)
437                     refresh = m_linkValue.equals(value);
438                 m_linkValue = value;
439                 // Check validity
440
if (value.length() == 0)
441                     Log.error("MTab.query - no value for link column " + lc);
442                 else
443                 {
444                     // we have column and value
445
if (where.length() != 0)
446                         where.append(" AND ");
447                     where.append(lc).append("=");
448                     if (lc.endsWith("_ID"))
449                         where.append(value);
450                     else
451                         where.append("'").append(value).append("'");
452                 }
453             }
454         } // isDetail
455

456         m_extendedWhere = where.toString();
457
458         // Final Query
459
if (m_query.isActive())
460         {
461             String JavaDoc q = validateQuery(m_query);
462             if (q != null)
463             {
464                 if (where.length() > 0 )
465                     where.append(" AND ");
466                 where.append(q);
467             }
468         }
469
470         /**
471          * Query
472          */

473         Log.trace(Log.l3_Util, "MTab.query #" + m_vo.TabNo + " - " + where);
474         if (m_mTable.isOpen())
475         {
476             if (refresh)
477                 m_mTable.dataRefreshAll();
478             else
479                 m_mTable.dataRequery(where.toString(), m_vo.onlyCurrentRows && !isDetail(), onlyCurrentDays);
480         }
481         else
482         {
483             m_mTable.setWhereClause(where.toString(), m_vo.onlyCurrentRows && !isDetail(), onlyCurrentDays);
484             m_mTable.open();
485         }
486         // Go to Record 0
487
setCurrentRow(0, true);
488     } // query
489

490     /**
491      * Validate Query.
492      * If query column is not a tab column create EXISTS query
493      * @param query query
494      * @return where clause
495      */

496     private String JavaDoc validateQuery (MQuery query)
497     {
498         if (query == null || query.getRestrictionCount() == 0)
499             return null;
500
501         // Check: only one restriction
502
if (query.getRestrictionCount() != 1)
503         {
504             Log.trace(Log.l6_Database, "MTab.validateQuery - ignored: " + query);
505             return query.getWhereClause();
506         }
507
508         String JavaDoc colName = query.getColumnName(0);
509         // a '(' in the name = function - don't try to resolve
510
if (colName.indexOf('(') != -1)
511         {
512             Log.trace(Log.l6_Database, "MTab.validateQuery - ignored: " + colName);
513             return query.getWhereClause();
514         }
515         // Query is valid ?
516
if (getField(colName) != null)
517             return query.getWhereClause();
518
519         // Find Refernce Column e.g. BillTo_ID -> C_BPartner_Location_ID
520
String JavaDoc sql = "SELECT cc.ColumnName "
521             + "FROM AD_Column c"
522             + " INNER JOIN AD_Ref_Table r ON (c.AD_Reference_Value_ID=r.AD_Reference_ID)"
523             + " INNER JOIN AD_Column cc ON (r.AD_Key=cc.AD_Column_ID) "
524             + "WHERE c.AD_Reference_ID=18" // Table
525
+ " AND c.ColumnName=?";
526         String JavaDoc refColName = null;
527         try
528         {
529             PreparedStatement pstmt = DB.prepareStatement(sql);
530             pstmt.setString(1, colName);
531             ResultSet rs = pstmt.executeQuery();
532             if (rs.next())
533                 refColName = rs.getString(1);
534             rs.close();
535             pstmt.close();
536         }
537         catch (SQLException e)
538         {
539             Log.error("MTab.validateQuery (ref) - Column=" + colName, e);
540             return query.getWhereClause();
541         }
542         // Reference Column found
543
if (refColName != null)
544         {
545             query.setColumnName(0, refColName);
546             if (getField(refColName) != null)
547             {
548                 Log.trace(Log.l6_Database, "MTab.validateQuery - Column " + colName + " replaced with " + refColName);
549                 return query.getWhereClause();
550             }
551             colName = refColName;
552         }
553
554         // Column NOT in Tab - create EXISTS subquery
555
String JavaDoc tableName = null;
556         String JavaDoc tabKeyColumn = getKeyColumnName();
557         // Column=SalesRep_ID, Key=AD_User_ID, Query=SalesRep_ID=101
558

559         sql = "SELECT t.TableName "
560             + "FROM AD_Column c"
561             + " INNER JOIN AD_Table t ON (c.AD_Table_ID=t.AD_Table_ID) "
562             + "WHERE c.ColumnName=? AND IsKey='Y'" // #1 Link Column
563
+ " AND EXISTS (SELECT * FROM AD_Column cc"
564             + " WHERE cc.AD_Table_ID=t.AD_Table_ID AND cc.ColumnName=?)"; // #2 Tab Key Column
565
try
566         {
567             PreparedStatement pstmt = DB.prepareStatement(sql);
568             pstmt.setString(1, colName);
569             pstmt.setString(2, tabKeyColumn);
570             ResultSet rs = pstmt.executeQuery();
571             if (rs.next())
572                 tableName = rs.getString(1);
573             rs.close();
574             pstmt.close();
575         }
576         catch (SQLException e)
577         {
578             Log.error("MTab.validateQuery - Column=" + colName + ", Key=" + tabKeyColumn, e);
579             return null;
580         }
581         // Causes could be functions in query
582
// e.g. Column=UPPER(Name), Key=AD_Element_ID, Query=UPPER(AD_Element.Name) LIKE '%CUSTOMER%'
583
if (tableName == null)
584         {
585             Log.trace(Log.l3_Util, "MTab.validateQuery not successfull - Column=" + colName + ", Key=" + tabKeyColumn + ", Query=" + query);
586             return query.getWhereClause();
587         }
588
589         query.setTableName("xx");
590         StringBuffer JavaDoc result = new StringBuffer JavaDoc ("EXISTS (SELECT * FROM ")
591             .append(tableName).append(" xx WHERE ")
592             .append(query.getWhereClause(true))
593             .append(" AND xx.").append(tabKeyColumn).append("=")
594             .append(getTableName()).append(".").append(tabKeyColumn).append(")");
595         Log.trace(Log.l6_Database, "MTab.validateQuery", result);
596         return result.toString();
597     } // validateQuery
598

599
600     /**
601      * Refresh all data
602      */

603     public void dataRefreshAll ()
604     {
605         Log.trace(Log.l3_Util, "MTab.dataRefreshAll #" + m_vo.TabNo);
606         /** @todo does not work with alpha key */
607         int keyNo = m_mTable.getKeyID(m_currentRow);
608         m_mTable.dataRefreshAll();
609         // Should use RowID - not working for tables with multiple keys
610
if (keyNo != -1)
611         {
612             if (keyNo != m_mTable.getKeyID(m_currentRow)) // something changed
613
{
614                 int size = getRowCount();
615                 for (int i = 0; i < size; i++)
616                 {
617                     if (keyNo == m_mTable.getKeyID(i))
618                     {
619                         m_currentRow = i;
620                         break;
621                     }
622                 }
623             }
624         }
625         setCurrentRow(m_currentRow, true);
626     } // dataRefreshAll
627

628     /**
629      * Refresh current row data
630      */

631     public void dataRefresh ()
632     {
633         dataRefresh (m_currentRow);
634     } // dataRefresh
635

636     /**
637      * Refresh row data
638      * @param row index
639      */

640     public void dataRefresh (int row)
641     {
642         Log.trace(Log.l3_Util, "MTab.dataRefresh #" + m_vo.TabNo, "row=" + row);
643         m_mTable.dataRefresh(row);
644         setCurrentRow(row, true);
645     } // dataRefresh
646

647     /**
648      * Uncoditionally Save data
649      * @param manualCmd if true, no vetoable PropertyChange will be fired for save confirmation from MTable
650      * @return true if save complete (or nor required)
651      */

652     public boolean dataSave(boolean manualCmd)
653     {
654         Log.trace(Log.l3_Util, "MTab.dataSave #" + m_vo.TabNo, "row=" + m_currentRow);
655         boolean retValue = (m_mTable.dataSave(manualCmd) == MTable.SAVE_OK);
656         if (manualCmd)
657             setCurrentRow(m_currentRow, false);
658         return retValue;
659     } // dataSave
660

661
662     /**
663      * Do we need to Save?
664      * @param rowChange row change
665      * @param onlyRealChange if true the value of a field was actually changed
666      * (e.g. for new records, which have not been changed) - default false
667      * @return true it needs to be saved
668      */

669     public boolean needSave (boolean rowChange, boolean onlyRealChange)
670     {
671         if (rowChange)
672         {
673             return m_mTable.needSave(-2, onlyRealChange);
674         }
675         else
676         {
677             if (onlyRealChange)
678                 return m_mTable.needSave();
679             else
680                 return m_mTable.needSave(onlyRealChange);
681         }
682     } // isDataChanged
683

684     /**
685      * Ignore data changes
686      */

687     public void dataIgnore()
688     {
689         Log.trace(Log.l3_Util, "MTab.dataIgnore #" + m_vo.TabNo);
690         m_mTable.dataIgnore();
691         setCurrentRow(m_currentRow, false); // re-load data
692
Log.trace(Log.l3_Util, "MTab.dataIgnore #" + m_vo.TabNo + "- fini");
693     } // dataIgnore
694

695     /**
696      * Create (copy) new Row
697      * and process Callouts
698      * @param copy copy
699      * @return true if copied/new
700      */

701     public boolean dataNew (boolean copy)
702     {
703         Log.trace(Log.l3_Util, "MTab.dataNew #" + m_vo.TabNo);
704         boolean retValue = m_mTable.dataNew (m_currentRow, copy);
705         setCurrentRow(m_currentRow + 1, true);
706         // process all Callouts (no dependency check - assumed that settings are valid)
707
for (int i = 0; i < getFieldCount(); i++)
708             processCallout(getField(i));
709         // check validity of defaults
710
for (int i = 0; i < getFieldCount(); i++)
711             getField(i).validateValue();
712         m_mTable.setChanged(false);
713         return retValue;
714     } // dataNew
715

716     /**
717      * Delete current Row
718      * @return true if deleted
719      */

720     public boolean dataDelete()
721     {
722         Log.trace(Log.l3_Util, "MTab.dataDelete #" + m_vo.TabNo, "row=" + m_currentRow);
723         boolean retValue = m_mTable.dataDelete(m_currentRow);
724         setCurrentRow(m_currentRow, true);
725         return retValue;
726     } // dataDelete
727

728     /**
729      * Get Name of Tab
730      * @return name
731      */

732     public String JavaDoc getName()
733     {
734         return m_vo.Name;
735     } // getName
736

737     /**
738      * Get Description of Tab
739      * @return description
740      */

741     public String JavaDoc getDescription()
742     {
743         return m_vo.Description;
744     } // getDescription
745

746     /**
747      * Get Help of Tab
748      * @return help
749      */

750     public String JavaDoc getHelp()
751     {
752         return m_vo.Help;
753     } // getHelp
754

755     /**
756      * Get Tab Level
757      * @return tab level
758      */

759     public int getTabLevel()
760     {
761         return m_vo.TabLevel;
762     } // getTabLevel
763

764     /**
765      * Get Commit Warning
766      * @return commit warning
767      */

768     public String JavaDoc getCommitWarning()
769     {
770         return m_vo.CommitWarning;
771     } // getCommitWarning
772

773     /**
774      * Return Table Model
775      * @return MTable
776      */

777     protected MTable getMTable()
778     {
779         return m_mTable;
780     } // getMTable
781

782     /**
783      * Return the name of the key column - may be ""
784      * @return key column name
785      */

786     public String JavaDoc getKeyColumnName()
787     {
788         return m_keyColumnName;
789     } // getKeyColumnName
790

791     /**
792      * Return Name of link column
793      * @return link column name
794      */

795     public String JavaDoc getLinkColumnName()
796     {
797         return m_linkColumnName;
798     } // getLinkColumnName
799

800     /**
801      * Set Name of link column.
802      * Set from MWindow.loadTabData
803      * Used in MTab.isCurreny, (.setCurrentRow) .query - APanel.cmd_report
804      * and MField.isEditable and .isDefault via context
805      * @param linkColumnName name of column - or sets name to AD_Column_ID, if exists
806      */

807     public void setLinkColumnName (String JavaDoc linkColumnName)
808     {
809         if (linkColumnName != null)
810             m_linkColumnName = linkColumnName;
811         else
812         {
813             if (m_vo.AD_Column_ID == 0)
814                 return;
815             // we have a link column identified (primary parent column)
816
else
817             {
818                 String JavaDoc SQL = "SELECT ColumnName FROM AD_Column WHERE AD_Column_ID=?";
819                 try
820                 {
821                     PreparedStatement pstmt = DB.prepareStatement(SQL);
822                     pstmt.setInt(1, m_vo.AD_Column_ID); // Link Column
823
ResultSet rs = pstmt.executeQuery();
824                     if (rs.next())
825                         m_linkColumnName = rs.getString(1);
826                     rs.close();
827                     pstmt.close();
828                 }
829                 catch (SQLException e)
830                 {
831                     Log.error("MTab.setLinkColumn", e);
832                 }
833                 Log.trace(Log.l6_Database, "MTab.setLinkColumnName - " + m_vo.AD_Column_ID, m_linkColumnName);
834             }
835         }
836         Env.setContext(m_vo.ctx, m_vo.WindowNo, m_vo.TabNo, "LinkColumnName", m_linkColumnName);
837     } // setLinkColumnName
838

839     /**
840      * Is the tab current?.
841      * <pre>
842      * Yes - Table must be open
843      * - Query String is the same
844      * - Not Detail
845      * - Old link column value is same as current one
846      * </pre>
847      * @return true if current
848      */

849     public boolean isCurrent()
850     {
851         // Open?
852
if (!m_mTable.isOpen())
853             return false;
854         // Same Query
855
if (!m_oldQuery.equals(m_query.getWhereClause()))
856             return false;
857         // Detail?
858
if (!isDetail())
859             return true;
860         // Same link column value
861
String JavaDoc value = Env.getContext(m_vo.ctx, m_vo.WindowNo, getLinkColumnName());
862         return m_linkValue.equals(value);
863     } // isCurrent
864

865     /**
866      * Is the tab/table currently open
867      * @return true if open
868      */

869     public boolean isOpen()
870     {
871         // Open?
872
if (m_mTable != null)
873             return m_mTable.isOpen();
874         return false;
875     } // isCurrent
876

877
878     /**
879      * Is Tab Incluced in other Tab
880      * @return true if included
881      */

882     public boolean isIncluded()
883     {
884         return m_included;
885     } // isIncluded
886

887     /**
888      * Is Tab Incluced in other Tab
889      * @param isIncluded true if included
890      */

891     public void setIncluded(boolean isIncluded)
892     {
893         m_included = isIncluded;
894     } // setIncluded
895

896     /**
897      * Are Only Current Rows displayed
898      * @return true if no history
899      */

900     public boolean isOnlyCurrentRows()
901     {
902         return m_vo.onlyCurrentRows;
903     } // isOnlyCurrentRows
904
/**
905      * Return Parent ArrayList
906      * @return parent column names
907      */

908     public ArrayList getParentColumnNames()
909     {
910         return m_parents;
911     } // getParentColumnNames
912

913     /**
914      * Get Tree ID of this tab
915      * @return ID
916      */

917     private int getTreeID()
918     {
919         Log.trace(Log.l5_DData, "MTab.getTreeID " + m_vo.TableName);
920         String JavaDoc SQL = "SELECT * FROM AD_ClientInfo WHERE AD_Client="
921             + Env.getContext(m_vo.ctx, m_vo.WindowNo, "#AD_Client_ID")
922             + " ORDER BY AD_Org DESC";
923         //
924
if (m_vo.TableName.equals("AD_Menu"))
925             return 10; // MM
926
else if (m_vo.TableName.equals("C_ElementValue"))
927             return 20; // EV
928
else if (m_vo.TableName.equals("M_Product"))
929             return 30; // PR
930
else if (m_vo.TableName.equals("C_BPartner"))
931             return 40; // BP
932
else if (m_vo.TableName.equals("AD_Org"))
933             return 50; // OO
934
else if (m_vo.TableName.equals("C_Project"))
935             return 60; // PJ
936
return 0;
937     } // getTreeID
938

939     /**
940      * Returns true if this is a detail record
941      * @return true if not parent tab
942      */

943     public boolean isDetail()
944     {
945         // We have IsParent columns and/or a link column
946
if (m_parents.size() > 0 || m_vo.AD_Column_ID != 0)
947             return true;
948         return false;
949     } // isDetail
950

951     /**
952      * Is Printed (Document can be printed)
953      * @return true if printing
954      */

955     public boolean isPrinted()
956     {
957         return m_vo.AD_Process_ID != 0;
958     } // isPrinted
959

960     /**
961      * Get WindowNo
962      * @return window no
963      */

964     public int getWindowNo()
965     {
966         return m_vo.WindowNo;
967     } // getWindowNo
968

969     /**
970      * Get TabNo
971      * @return tab no
972      */

973     public int getTabNo()
974     {
975         return m_vo.TabNo;
976     } // getTabNo
977

978     /**
979      * Get Process ID
980      * @return Process ID
981      */

982     public int getAD_Process_ID()
983     {
984         return m_vo.AD_Process_ID;
985     } // getAD_Process_ID
986

987     /**
988      * Is High Volume?
989      * @return true if high volumen table
990      */

991     public boolean isHighVolume()
992     {
993         return m_vo.IsHighVolume;
994     } // isHighVolume
995

996     /**
997      * Is Read Only?
998      * @return true if read only
999      */

1000    public boolean isReadOnly()
1001    {
1002        return m_vo.IsReadOnly;
1003    } // isReadOnly
1004

1005    /**
1006     * Is Single Row
1007     * @return true if single row
1008     */

1009    public boolean isSingleRow()
1010    {
1011        return m_vo.IsSingleRow;
1012    } // isSingleRow;
1013

1014    /**
1015     * Set Single Row.
1016     * Temporary store of current value
1017     * @param isSingleRow toggle
1018     */

1019    public void setSingleRow (boolean isSingleRow)
1020    {
1021        m_vo.IsSingleRow = isSingleRow;
1022    } // setSingleRow
1023

1024
1025    /**
1026     * Has Tree
1027     * @return true if tree exists
1028     */

1029    public boolean isTreeTab()
1030    {
1031        return m_vo.HasTree;
1032    } // isTreeTab
1033

1034    /**
1035     * Get Tab ID
1036     * @return Tab ID
1037     */

1038    public int getAD_Tab_ID()
1039    {
1040        return m_vo.AD_Tab_ID;
1041    } // getAD_Tab_ID
1042

1043    /**
1044     * Get Table ID
1045     * @return Table ID
1046     */

1047    public int getAD_Table_ID()
1048    {
1049        return m_vo.AD_Table_ID;
1050    } // getAD_Table_ID
1051

1052    /**
1053     * Get Included Tab ID
1054     * @return Included_Tab_ID
1055     */

1056    public int getIncluded_Tab_ID()
1057    {
1058        return m_vo.Included_Tab_ID;
1059    } // getIncluded_Tab_ID
1060

1061    /**
1062     * Get TableName
1063     * @return Table Name
1064     */

1065    public String JavaDoc getTableName()
1066    {
1067        return m_vo.TableName;
1068    } // getTableName
1069

1070    /**
1071     * Get Tab Where Clause
1072     * @return where clause
1073     */

1074    public String JavaDoc getWhereClause()
1075    {
1076        return m_vo.WhereClause;
1077    } // getWhereClause
1078

1079    /**
1080     * Is Sort Tab
1081     * @return true if sort tab
1082     */

1083    public boolean isSortTab()
1084    {
1085        return m_vo.IsSortTab;
1086    } // isSortTab
1087

1088    /**
1089     * Get Order column for sort tab
1090     * @return AD_Column_ID
1091     */

1092    public int getAD_ColumnSortOrder_ID()
1093    {
1094        return m_vo.AD_ColumnSortOrder_ID;
1095    } // getAD_ColumnSortOrder_ID
1096

1097    /**
1098     * Get Yes/No column for sort tab
1099     * @return AD_Column_ID
1100     */

1101    public int getAD_ColumnSortYesNo_ID()
1102    {
1103        return m_vo.AD_ColumnSortYesNo_ID;
1104    } // getAD_ColumnSortYesNo_ID
1105

1106    /*************************************************************************/
1107
1108    /**
1109     * Get extended Where Clause (parent link)
1110     * @return parent link
1111     */

1112    public String JavaDoc getWhereExtended()
1113    {
1114        return m_extendedWhere;
1115    } // getWhereExtended
1116

1117    /**
1118     * Get Order By Clause
1119     * @param onlyCurrentRows only current rows
1120     * @return Order By Clause
1121     */

1122    private String JavaDoc getOrderByClause(boolean onlyCurrentRows)
1123    {
1124        // If there is no Tab Order By - use info from Fields
1125
if (m_vo.OrderByClause.length() == 0 && m_OrderBys[0] != null)
1126        {
1127            m_vo.OrderByClause = m_OrderBys[0];
1128            if (onlyCurrentRows && !isDetail()) // transaction order descending
1129
m_vo.OrderByClause += " DESC";
1130            if (m_OrderBys[1] != null)
1131                m_vo.OrderByClause += "," + m_OrderBys[1];
1132        }
1133        return m_vo.OrderByClause;
1134    } // getOrderByClause
1135

1136    /*************************************************************************/
1137
1138    /**
1139     * Transaction support.
1140     * Depending on Table returns transaction info
1141     * @return info
1142     */

1143    public String JavaDoc getTrxInfo()
1144    {
1145        // Order || Invoice
1146
if (m_vo.TableName.startsWith("C_Order") || m_vo.TableName.startsWith("C_Invoice"))
1147        {
1148            int Record_ID;
1149            boolean isOrder = m_vo.TableName.startsWith("C_Order");
1150            //
1151
StringBuffer JavaDoc sql = new StringBuffer JavaDoc("SELECT COUNT(*) AS Lines,c.ISO_Code,o.TotalLines,o.GrandTotal,"
1152                + "C_Base_Convert(o.GrandTotal,o.C_Currency_ID,o.AD_Client_ID,SysDate,o.AD_Org_ID) AS ConvAmt ");
1153            if (isOrder)
1154            {
1155                Record_ID = Env.getContextAsInt(m_vo.ctx, m_vo.WindowNo, "C_Order_ID");
1156                sql.append("FROM C_Order o, C_Currency c, C_OrderLine l "
1157                    + "WHERE o.C_Currency_ID=c.C_Currency_ID"
1158                    + " AND o.C_Order_ID=?"
1159                    + " AND o.C_Order_ID=l.C_Order_ID ");
1160            }
1161            else
1162            {
1163                Record_ID = Env.getContextAsInt(m_vo.ctx, m_vo.WindowNo, "C_Invoice_ID");
1164                sql.append("FROM C_Invoice o, C_Currency c, C_InvoiceLine l "
1165                    + "WHERE o.C_Currency_ID=c.C_Currency_ID"
1166                    + " AND o.C_Invoice_ID=?"
1167                    + " AND o.C_Invoice_ID=l.C_Invoice_ID ");
1168            }
1169            sql.append("GROUP BY o.C_Currency_ID, c.ISO_Code, o.TotalLines, o.GrandTotal, o.AD_Client_ID, o.AD_Org_ID");
1170
1171            Log.trace(Log.l3_Util, "MTab.getTrxInfo " + m_vo.TableName + " - " + Record_ID);
1172            MessageFormat mf = null;
1173            try
1174            {
1175                mf = new MessageFormat(Msg.getMsg(Env.getAD_Language(m_vo.ctx), "OrderSummary"));
1176            }
1177            catch (Exception JavaDoc e)
1178            {
1179                Log.error("MTab.getTrxInfo - OrderSummary=" + Msg.getMsg(Env.getAD_Language(m_vo.ctx), "OrderSummary"), e);
1180            }
1181            if (mf == null)
1182                return " ";
1183            /**********************************************************************
1184             * ** Message: OrderSummary **
1185             * {0} Line(s) - {1,number,#,##0.00} - Toral: {2,number,#,##0.00} {3} = {4,number,#,##0.00}
1186             *
1187             * {0} - Number of lines
1188             * {1} - Line toral
1189             * {2} - Grand total (including tax, etc.)
1190             * {3} - Currency
1191             * (4) - Grand total converted to local currency
1192             */

1193            Object JavaDoc[] arguments = new Object JavaDoc[5];
1194            boolean filled = false;
1195            //
1196
try
1197            {
1198                PreparedStatement pstmt = DB.prepareStatement(sql.toString());
1199                pstmt.setInt(1, Record_ID);
1200                ResultSet rs = pstmt.executeQuery();
1201                if (rs.next())
1202                {
1203                    // {0} - Number of lines
1204
Integer JavaDoc lines = new Integer JavaDoc(rs.getInt(1));
1205                    arguments[0] = lines;
1206                    // {1} - Line toral
1207
Double JavaDoc lineTotal = new Double JavaDoc(rs.getDouble(3));
1208                    arguments[1] = lineTotal;
1209                    // {2} - Grand total (including tax, etc.)
1210
Double JavaDoc grandTotal = new Double JavaDoc(rs.getDouble(4));
1211                    arguments[2] = grandTotal;
1212                    // {3} - Currency
1213
String JavaDoc currency = rs.getString(2);
1214                    arguments[3] = currency;
1215                    // (4) - Grand total converted to Euro
1216
Double JavaDoc grandEuro = new Double JavaDoc(rs.getDouble(5));
1217                    arguments[4] = grandEuro;
1218                    filled = true;
1219                }
1220                rs.close();
1221                pstmt.close();
1222            }
1223            catch (SQLException e)
1224            {
1225                Log.error("MTab.getTrxInfo " + m_vo.TableName + "\nSQL=" + sql, e);
1226            }
1227            if (filled)
1228                return mf.format (arguments);
1229            return " ";
1230        } // Order || Invoice
1231

1232        // Expense Report
1233
else if (m_vo.TableName.startsWith("S_TimeExpense") && m_vo.TabNo == 0)
1234        {
1235            int Record_ID = Env.getContextAsInt(m_vo.ctx, m_vo.WindowNo, "S_TimeExpense_ID");
1236            Log.trace(Log.l3_Util, "MTab.getTrxInfo " + m_vo.TableName + " - " + Record_ID);
1237            MessageFormat mf = null;
1238            try
1239            {
1240                mf = new MessageFormat(Msg.getMsg(Env.getAD_Language(m_vo.ctx), "ExpenseSummary"));
1241            }
1242            catch (Exception JavaDoc e)
1243            {
1244                Log.error("MTab.getTrxInfo - ExpenseSummary=" + Msg.getMsg(Env.getAD_Language(m_vo.ctx), "ExpenseSummary"), e);
1245            }
1246            if (mf == null)
1247                return " ";
1248            /**********************************************************************
1249             * ** Message: ExpenseSummary **
1250             * {0} Line(s) - Total: {1,number,#,##0.00} {2}
1251             *
1252             * {0} - Number of lines
1253             * {1} - Toral
1254             * {2} - Currency
1255             */

1256            Object JavaDoc[] arguments = new Object JavaDoc[3];
1257            boolean filled = false;
1258            //
1259
String JavaDoc SQL = "SELECT COUNT(*) AS Lines, SUM(ConvertedAmt*Qty) "
1260                + "FROM S_TimeExpenseLine "
1261                + "WHERE S_TimeExpense_ID=?";
1262
1263            //
1264
try
1265            {
1266                PreparedStatement pstmt = DB.prepareStatement(SQL);
1267                pstmt.setInt(1, Record_ID);
1268                ResultSet rs = pstmt.executeQuery();
1269                if (rs.next())
1270                {
1271                    // {0} - Number of lines
1272
Integer JavaDoc lines = new Integer JavaDoc(rs.getInt(1));
1273                    arguments[0] = lines;
1274                    // {1} - Line toral
1275
Double JavaDoc total = new Double JavaDoc(rs.getDouble(2));
1276                    arguments[1] = total;
1277                    // {3} - Currency
1278
arguments[2] = " ";
1279                    filled = true;
1280                }
1281                rs.close();
1282                pstmt.close();
1283            }
1284            catch (SQLException e)
1285            {
1286                Log.error("MTab.getTrxInfo " + m_vo.TableName + "\nSQL=" + SQL, e);
1287            }
1288            if (filled)
1289                return mf.format (arguments);
1290            return " ";
1291        } // S_TimeExpense
1292

1293
1294        // Default - No Trx Info
1295
return null;
1296    } // getTrxInfo
1297

1298    /**
1299     * Load Dependant Information
1300     */

1301    private void loadDependentInfo()
1302    {
1303        /**
1304         * Load Order Type from C_DocTypeTarget_ID
1305         */

1306        if (m_vo.TableName.equals("C_Order"))
1307        {
1308            int C_DocTyp_ID = 0;
1309            Integer JavaDoc target = (Integer JavaDoc)getValue("C_DocTypeTarget_ID");
1310            if (target != null)
1311                C_DocTyp_ID = target.intValue();
1312            if (C_DocTyp_ID == 0)
1313                return;
1314
1315            String JavaDoc sql = "SELECT DocSubTypeSO FROM C_DocType WHERE C_DocType_ID=?";
1316            try
1317            {
1318                PreparedStatement pstmt = DB.prepareStatement(sql);
1319                pstmt.setInt(1, C_DocTyp_ID);
1320                ResultSet rs = pstmt.executeQuery();
1321                if (rs.next())
1322                    Env.setContext(m_vo.ctx, m_vo.WindowNo, "OrderType", rs.getString(1));
1323                rs.close();
1324                pstmt.close();
1325            }
1326            catch (SQLException e)
1327            {
1328                Log.error("MTab.loadOrderType", e);
1329            }
1330        } // loadOrderInfo
1331
} // loadDependentInfo
1332

1333    /*************************************************************************/
1334
1335    /**
1336     * Load Attachments for this table
1337     */

1338    public void loadAttachments()
1339    {
1340        Log.trace(Log.l3_Util, "MTab.loadAttachments #" + m_vo.TabNo);
1341        if (!canHaveAttachment())
1342            return;
1343
1344        String JavaDoc SQL = "SELECT AD_Attachment_ID, Record_ID FROM AD_Attachment "
1345            + "WHERE AD_Table_ID=?";
1346        try
1347        {
1348            if (m_Attachment == null)
1349                m_Attachment = new HashMap();
1350            else
1351                m_Attachment.clear();
1352            PreparedStatement pstmt = DB.prepareStatement(SQL);
1353            pstmt.setInt(1, m_vo.AD_Table_ID);
1354            ResultSet rs = pstmt.executeQuery();
1355            while (rs.next())
1356            {
1357                Integer JavaDoc key = new Integer JavaDoc(rs.getInt(2));
1358                Integer JavaDoc value = new Integer JavaDoc(rs.getInt(1));
1359                m_Attachment.put(key, value);
1360            }
1361            rs.close();
1362            pstmt.close();
1363        }
1364        catch (SQLException e)
1365        {
1366            Log.error("MTab.loadAttachments", e);
1367        }
1368        Log.trace(Log.l4_Data, "Attachments=" + m_Attachment.size());
1369    } // loadAttachment
1370

1371    /**
1372     * Can this tab have Attachments?.
1373     * <p>
1374     * It can have an attachment if it has a key column ending with _ID.
1375     * The key column is empty, if there is no single identifying key.
1376     * @return true if record can have attachment
1377     */

1378    public boolean canHaveAttachment()
1379    {
1380        if (getKeyColumnName().endsWith("_ID"))
1381            return true;
1382        return false;
1383    } // canHaveAttachment
1384

1385    /**
1386     * Returns true, if current row has an Attachment
1387     * @return true if record has attchment
1388     */

1389    public boolean hasAttachment()
1390    {
1391        if (m_Attachment == null)
1392            loadAttachments();
1393        if (m_Attachment == null || m_Attachment.isEmpty())
1394            return false;
1395        //
1396
Integer JavaDoc key = new Integer JavaDoc(m_mTable.getKeyID (m_currentRow));
1397        return m_Attachment.containsKey(key);
1398    } // hasAttachment
1399

1400    /**
1401     * Get Attachment_ID.
1402     * @return ID or 0, if not found
1403     */

1404    public int getAD_AttachmentID()
1405    {
1406        if (m_Attachment == null)
1407            loadAttachments();
1408        if (m_Attachment.isEmpty())
1409            return 0;
1410        //
1411
Integer JavaDoc key = new Integer JavaDoc(m_mTable.getKeyID (m_currentRow));
1412        Integer JavaDoc value = (Integer JavaDoc)m_Attachment.get(key);
1413        if (value == null)
1414            return 0;
1415        else
1416            return value.intValue();
1417    } // getAttachmentID
1418

1419
1420    /*************************************************************************/
1421
1422    /**
1423     * Load Locks for Table and User
1424     */

1425    public void loadLocks()
1426    {
1427        int AD_User_ID = Env.getContextAsInt(Env.getCtx(), "#AD_User_ID");
1428        Log.trace(Log.l3_Util, "MTab.loadLocks #" + m_vo.TabNo + " - AD_User_ID=" + AD_User_ID);
1429        if (!canHaveAttachment())
1430            return;
1431
1432        String JavaDoc SQL = "SELECT Record_ID "
1433            + "FROM AD_Private_Access "
1434            + "WHERE AD_User_ID=? AND AD_Table_ID=? AND IsActive='Y' "
1435            + "ORDER BY Record_ID";
1436        try
1437        {
1438            if (m_Lock == null)
1439                m_Lock = new ArrayList();
1440            else
1441                m_Lock.clear();
1442            PreparedStatement pstmt = DB.prepareStatement(SQL);
1443            pstmt.setInt(1, AD_User_ID);
1444            pstmt.setInt(2, m_vo.AD_Table_ID);
1445            ResultSet rs = pstmt.executeQuery();
1446            while (rs.next())
1447            {
1448                Integer JavaDoc key = new Integer JavaDoc(rs.getInt(1));
1449                m_Lock.add(key);
1450            }
1451            rs.close();
1452            pstmt.close();
1453        }
1454        catch (SQLException e)
1455        {
1456            Log.error("MTab.loadLocks", e);
1457        }
1458        Log.trace(Log.l4_Data, "Locks=" + m_Lock.size());
1459    } // loadLooks
1460

1461    /**
1462     * Record Is Locked
1463     */

1464    public boolean isLocked()
1465    {
1466        if (!MRole.getDefault(m_vo.ctx, false).isPersonalLock())
1467            return false;
1468        if (m_Lock == null)
1469            loadLocks();
1470        if (m_Lock == null || m_Lock.isEmpty())
1471            return false;
1472        //
1473
Integer JavaDoc key = new Integer JavaDoc(m_mTable.getKeyID (m_currentRow));
1474        return m_Lock.contains(key);
1475    } // isLocked
1476

1477    /**
1478     * Lock Record
1479     * @param Record_ID id
1480     * @param lock true if lock, otherwise unlock
1481     */

1482    public void lock (Properties ctx, int Record_ID, boolean lock)
1483    {
1484        int AD_User_ID = Env.getContextAsInt(ctx, "#AD_User_ID");
1485        Log.trace (Log.l3_Util, "MTab.lock - " + lock + ", AD_User_ID=" + AD_User_ID + ", AD_Table_ID=" + m_vo.AD_Table_ID + ", Record_ID=" + Record_ID);
1486        MPrivateAccess access = MPrivateAccess.get (ctx, AD_User_ID, m_vo.AD_Table_ID, Record_ID);
1487        if (access == null)
1488            access = new MPrivateAccess (ctx, AD_User_ID, m_vo.AD_Table_ID, Record_ID);
1489        access.setIsActive(lock);
1490        access.save();
1491        //
1492
loadLocks();
1493    } // lock
1494

1495    /*************************************************************************/
1496
1497    /**
1498     * Data Status Listener from MTable.
1499     * - get raw info and add current row information
1500     * - update the current row
1501     * - redistribute (fire) Data Status event
1502     * @param e event
1503     */

1504    public void dataStatusChanged (DataStatusEvent e)
1505    {
1506        Log.trace(Log.l3_Util, "MTab.dataStatusChanged #" + m_vo.TabNo, e.toString());
1507        int oldCurrentRow = e.getCurrentRow();
1508        m_DataStatusEvent = e; // save it
1509
// when sorted set current row to 0
1510
String JavaDoc msg = m_DataStatusEvent.getAD_Message();
1511        if (msg != null && msg.equals("Sorted"))
1512            setCurrentRow(0, true);
1513        // set current row
1514
m_DataStatusEvent.setCurrentRow(m_currentRow);
1515        // Same row - update value
1516
if (oldCurrentRow == m_currentRow)
1517        {
1518            MField field = m_mTable.getField(e.getChangedColumn());
1519            if (field != null)
1520            {
1521                Object JavaDoc value = m_mTable.getValueAt(m_currentRow, e.getChangedColumn());
1522                field.setValue(value, m_mTable.isInserting());
1523            }
1524        }
1525        else // Redistribute Info with current row info
1526
fireDataStatusChanged(m_DataStatusEvent);
1527    // Log.trace(Log.l3_Util, "MTab.dataStatusChanged #" + m_vo.TabNo + "- fini", e.toString());
1528
} // dataStatusChanged
1529

1530    /**
1531     * Inform Listeners and build WHO info
1532     * @param e event
1533     */

1534    private void fireDataStatusChanged (DataStatusEvent e)
1535    {
1536        Log.trace(Log.l4_Data, "MTab.fireDataStatusChanged", e.toString());
1537        if (m_dataStatusListeners != null)
1538        {
1539            // WHO Info
1540
if (e.getCurrentRow() >= 0)
1541            {
1542                e.Created = (Timestamp)getValue("Created");
1543                e.CreatedBy = getValue("CreatedBy");
1544                e.Updated = (Timestamp)getValue("Updated");
1545                e.UpdatedBy = getValue("UpdatedBy");
1546                // Info
1547
StringBuffer JavaDoc info = new StringBuffer JavaDoc(getTableName());
1548                // We have a key column
1549
if (m_keyColumnName != null && m_keyColumnName.length() > 0)
1550                {
1551                    info.append(" - ")
1552                        .append(m_keyColumnName).append("=").append(getValue(m_keyColumnName));
1553                }
1554                else // we have multiple parents
1555
{
1556                    for (int i = 0; i < m_parents.size(); i++)
1557                    {
1558                        String JavaDoc keyCol = (String JavaDoc)m_parents.get(i);
1559                        info.append(" - ")
1560                            .append(keyCol).append("=").append(getValue(keyCol));
1561                    }
1562                }
1563                e.Info = info.toString();
1564            }
1565            e.setInserting(m_mTable.isInserting());
1566            // Distribute/fire it
1567
Vector listeners = m_dataStatusListeners;
1568            int count = listeners.size();
1569            for (int i = 0; i < count; i++)
1570                ((DataStatusListener) listeners.elementAt(i)).dataStatusChanged(e);
1571        }
1572    // Log.trace(Log.l4_Data, "MTab.fireDataStatusChanged - fini", e.toString());
1573
} // fireDataStatusChanged
1574

1575    /**
1576     * Create and fire Data Status Error Event
1577     * @param AD_Message message
1578     * @param info info
1579     */

1580    protected void fireDataStatusEEvent(String JavaDoc AD_Message, String JavaDoc info)
1581    {
1582        m_mTable.fireDataStatusEEvent(AD_Message, info);
1583    } // fireDataStatusEvent
1584

1585    /**
1586     * Create and fire Data Status Error Event (from Error Log)
1587     * @param errorLog log
1588     */

1589    protected void fireDataStatusEEvent (ValueNamePair errorLog)
1590    {
1591        if (errorLog != null)
1592            m_mTable.fireDataStatusEEvent(errorLog);
1593    } // fireDataStatusEvent
1594

1595    /**
1596     * Get Current Row
1597     * @return current row
1598     */

1599    public int getCurrentRow()
1600    {
1601        if (m_currentRow != verifyRow(m_currentRow))
1602            setCurrentRow(m_mTable.getRowCount()-1, true);
1603        return m_currentRow;
1604    } // getCurrentRow
1605

1606    /**
1607     * Get Current TableKey_ID
1608     * @return ID
1609     */

1610    public int getCurrentKeyID()
1611    {
1612        return m_mTable.getKeyID(m_currentRow);
1613    } // getCurrentKeyID
1614

1615    /**
1616     * Get Key ID of row
1617     * @param row row number
1618     * @return The Key ID of the row or -1 if not found
1619     */

1620    public int getKeyID (int row)
1621    {
1622        return m_mTable.getKeyID (row);
1623    } // getCurrentKeyID
1624

1625    /**
1626     * Navigate absulute - goto Row - (zero based).
1627     * - does nothing, if in current row
1628     * - saves old row if required
1629     * @param targetRow target row
1630     * @return current row
1631     */

1632    public int navigate (int targetRow)
1633    {
1634        // nothing to do
1635
if (targetRow == m_currentRow)
1636            return m_currentRow;
1637        Log.trace(Log.l1_User, "MTab.navigate - Row=" + targetRow);
1638
1639        // Row range check
1640
int newRow = verifyRow(targetRow);
1641
1642        // Check, if we have old uncommitted data
1643
m_mTable.dataSave(newRow, false);
1644
1645        // new position
1646
return setCurrentRow(newRow, true);
1647    } // navigate
1648

1649    /**
1650     * Navigate relatively - i.e. plus/minus from current position
1651     * @param rowChange row change
1652     * @return current row
1653     */

1654    public int navigateRelative (int rowChange)
1655    {
1656        return navigate (m_currentRow + rowChange);
1657    } // navigateRelative
1658

1659    /**
1660     * Navigate to current now (reload)
1661     * @return current row
1662     */

1663    public int navigateCurrent()
1664    {
1665        Log.trace(Log.l1_User, "MTab.navigateCurrent");
1666        return setCurrentRow(m_currentRow, true);
1667    } // navigateCurrent
1668

1669    /**
1670     * Row Range check
1671     * @param targetRow target row
1672     * @return checked row
1673     */

1674    private int verifyRow (int targetRow)
1675    {
1676        int newRow = targetRow;
1677        // Table Open?
1678
if (!m_mTable.isOpen())
1679        {
1680            Log.error("MTab.verifyRow - Table not open");
1681            return -1;
1682        }
1683        // Row Count
1684
int rows = getRowCount();
1685        if (rows == 0)
1686        {
1687            Log.trace(Log.l4_Data, "MTab.verifyRow", "No Rows");
1688            return -1;
1689        }
1690        if (newRow >= rows)
1691        {
1692            newRow = rows-1;
1693            Log.trace(Log.l4_Data, "MTab.verifyRow", "Set to max Row: " + newRow);
1694        }
1695        else if (newRow < 0)
1696        {
1697            newRow = 0;
1698            Log.trace(Log.l4_Data, "MTab.verifyRow", "Set to first Row");
1699        }
1700        return newRow;
1701    } // verifyRow
1702

1703    /**
1704     * Set current row and load data into fields.
1705     * If there is no row - load nulls
1706     * @param newCurrentRow new current row
1707     * @param fireEvents fire events
1708     * @return current row
1709     */

1710    private int setCurrentRow (int newCurrentRow, boolean fireEvents)
1711    {
1712        int oldCurrentRow = m_currentRow;
1713        m_currentRow = verifyRow (newCurrentRow);
1714        Log.trace(Log.l4_Data, "MTab.setCurrentRow = " + m_currentRow, "fire=" + fireEvents);
1715
1716        // Update Field Values
1717
int size = m_mTable.getColumnCount();
1718        for (int i = 0; i < size; i++)
1719        {
1720            MField mField = m_mTable.getField(i);
1721            // get Value from Table
1722
if (m_currentRow >= 0)
1723            {
1724                Object JavaDoc value = m_mTable.getValueAt(m_currentRow, i);
1725                mField.setValue(value, m_mTable.isInserting());
1726                if (m_mTable.isInserting()) // set invalid values to null
1727
mField.validateValue();
1728            }
1729            else
1730            { // no rows - set to a reasonable value - not updateable
1731
// Object value = null;
1732
// if (mField.isKey() || mField.isParent() || mField.getColumnName().equals(m_linkColumnName))
1733
// value = mField.getDefault();
1734
mField.setValue();
1735            }
1736        }
1737        loadDependentInfo();
1738
1739        if (!fireEvents) // prevents informing twice
1740
return m_currentRow;
1741
1742        // inform VTable/.. -> rowChanged
1743
m_propertyChangeSupport.firePropertyChange(PROPERTY, oldCurrentRow, m_currentRow);
1744
1745        // inform APanel/.. -> dataStatus with row updated
1746
if (m_DataStatusEvent == null)
1747        {
1748            Log.trace(Log.l5_DData, "MTab.setCurrentRow - no existing data status event");
1749        }
1750        else
1751        {
1752            m_DataStatusEvent.setCurrentRow(m_currentRow);
1753            String JavaDoc status = m_DataStatusEvent.getAD_Message();
1754            if (status == null || status.length() == 0)
1755                 m_DataStatusEvent.setInfo("NavigateOrUpdate", null, false);
1756            fireDataStatusChanged(m_DataStatusEvent);
1757        }
1758        return m_currentRow;
1759    } // setCurrentRow
1760

1761    /*************************************************************************/
1762
1763    /**
1764     * Get RowCount
1765     * @return row count
1766     */

1767    public int getRowCount()
1768    {
1769        int count = m_mTable.getRowCount();
1770        // Wait a bit if currently loading
1771
if (count == 0 && m_mTable.isLoading())
1772        {
1773            try
1774            {
1775                Thread.sleep(100); // .1 sec
1776
}
1777            catch (Exception JavaDoc e) {}
1778            count = m_mTable.getRowCount();
1779        }
1780        return count;
1781    } // getRowCount
1782

1783    /**
1784     * Get Column/Field Count
1785     * @return field count
1786     */

1787    public int getFieldCount()
1788    {
1789        return m_mTable.getColumnCount();
1790    } // getFieldCount
1791

1792    /**
1793     * Get Field by index
1794     * @param index index
1795     * @return MField
1796     */

1797    public MField getField (int index)
1798    {
1799        return m_mTable.getField(index);
1800    } // getField
1801

1802    /**
1803     * Get Field by DB column name
1804     * @param columnName column name
1805     * @return MField
1806     */

1807    public MField getField (String JavaDoc columnName)
1808    {
1809        return m_mTable.getField(columnName);
1810    } // getField
1811

1812    /**
1813     * Set New Value & call Callout
1814     * @param columnName database column name
1815     * @param value value
1816     * @return error message or ""
1817     */

1818    public String JavaDoc setValue (String JavaDoc columnName, Object JavaDoc value)
1819    {
1820        if (columnName == null)
1821            return "NoColumn";
1822        return setValue(m_mTable.getField(columnName), value);
1823    } // setValue
1824

1825    /**
1826     * Set New Value & call Callout
1827     * @param field field
1828     * @param value value
1829     * @return error message or ""
1830     */

1831    public String JavaDoc setValue (MField field, Object JavaDoc value)
1832    {
1833        if (field == null)
1834            return "NoField";
1835
1836        Log.trace(Log.l3_Util, "MTab.setValue - "
1837            + field.getColumnName() + "=" + value + " for row=" + m_currentRow);
1838
1839        int col = m_mTable.findColumn(field.getColumnName());
1840        m_mTable.setValueAt(value, m_currentRow, col, false);
1841        //
1842
return processFieldChange (field);
1843    } // setValue
1844

1845    /**
1846     * Process Field Change - evaluate Dependencies and process Callouts.
1847     *
1848     * called from MTab.setValue or GridController.dataStatusChanged
1849     * @param changedField changed field
1850     * @return error message or ""
1851     */

1852    public String JavaDoc processFieldChange (MField changedField)
1853    {
1854        processDependencies (changedField);
1855        return processCallout (changedField);
1856    } // processFieldChange
1857

1858    /**
1859     * Evaluate Dependencies
1860     * @param changedField changed field
1861     */

1862    private void processDependencies (MField changedField)
1863    {
1864        String JavaDoc columnName = changedField.getColumnName();
1865    // Log.trace(Log.l4_Data, "Changed Column", columnName);
1866

1867        // when column name is not in list of DependentOn fields - fini
1868
if (!isDependentOn(columnName))
1869            return;
1870
1871        // Get dependent MFields (may be because of display or dynamic lookup)
1872
ArrayList list = getDependentFieldList(columnName);
1873        for (int i = 0; i < list.size(); i++)
1874        {
1875            MField dependentField = (MField)list.get(i);
1876        // Log.trace(Log.l5_DData, "Dependent Field", dependentField==null ? "null" : dependentField.getColumnName());
1877
// if the field has a lookup
1878
if (dependentField != null && dependentField.getLookup() instanceof MLookup)
1879            {
1880                MLookup mLookup = (MLookup)dependentField.getLookup();
1881            // Log.trace(Log.l6_Database, "Lookup Validation", mLookup.getValidation());
1882
// if the lookup is dynamic (i.e. contains this columnName as variable)
1883
if (mLookup.getValidation().indexOf("@"+columnName+"@") != -1)
1884                {
1885                    Log.trace(Log.l6_Database, "MTab.processDependencies",
1886                        columnName + " changed - " + dependentField.getColumnName() + " set to null");
1887                    // invalidate current selection
1888
setValue(dependentField, null);
1889                }
1890            }
1891        } // for all dependent fields
1892
} // processDependencies
1893

1894    /**
1895     * Process Callout(s).
1896     * <p>
1897     * The Callout is in the string of
1898     * "class.method;class.method;"
1899     * If there is no class name, i.e. only a method name, the class is regarded
1900     * as CalloutSystem.
1901     * The class needs to comply with the Interface Callout.
1902     *
1903     * For a limited time, the old notation of Sx_matheod / Ux_menthod is maintained.
1904     *
1905     * @param field field
1906     * @return error message or ""
1907     * @see org.compiere.model.Callout
1908     */

1909    private String JavaDoc processCallout (MField field)
1910    {
1911        String JavaDoc callout = field.getCallout();
1912        if (callout.length() == 0)
1913            return "";
1914
1915        Object JavaDoc value = field.getValue();
1916        Object JavaDoc oldValue = field.getOldValue();
1917        Log.trace(Log.l3_Util, "MTab.processCallout - " + field.getColumnName() + "=" + value
1918            + " (" + callout + ") - old=" + oldValue);
1919
1920        StringTokenizer st = new StringTokenizer(callout, ";", false);
1921        while (st.hasMoreTokens()) // for each callout
1922
{
1923            String JavaDoc cmd = st.nextToken().trim();
1924            Callout call = null;
1925            String JavaDoc method = null;
1926            int methodStart = cmd.lastIndexOf(".");
1927            try
1928            {
1929                // The old way - CalloutSystem ** start to be discontinued
1930
if (methodStart == -1) // no class
1931
{
1932                    // old version compatibility
1933
if (cmd.charAt(0) == 'S' && cmd.charAt(2) == '_')
1934                    {
1935                        call = new CalloutSystem();
1936                        if (cmd.length() > 3)
1937                            method = cmd.substring(3);
1938                    }
1939                    else if (cmd.charAt(0) == 'U' && cmd.charAt(2) == '_')
1940                    {
1941                        Class JavaDoc cClass = Class.forName("com.compiere.custom.CalloutUser");
1942                        call = (Callout)cClass.newInstance();
1943                        if (cmd.length() > 3)
1944                            method = cmd.substring(3);
1945                    }
1946                    else
1947                    {
1948                        call = new CalloutSystem();
1949                        method = cmd;
1950                    }
1951                } // ** end to be discontinued
1952
else
1953                {
1954                    Class JavaDoc cClass = Class.forName(cmd.substring(0,methodStart));
1955                    call = (Callout)cClass.newInstance();
1956                    method = cmd.substring(methodStart+1);
1957                }
1958            }
1959            catch (Exception JavaDoc e)
1960            {
1961                Log.error("MTab.processCallout", e);
1962                return "CalloutNameInvalid = " + cmd + " (" + e.toString() + ")";
1963            }
1964
1965            if (call == null || method == null || method.length() == 0)
1966                return "CalloutNameInvalid = " + method;
1967                
1968            String JavaDoc retValue = "";
1969            try
1970            {
1971                retValue = call.start(m_vo.ctx, method, m_vo.WindowNo, this, field, value, oldValue);
1972            }
1973            catch (Exception JavaDoc e)
1974            {
1975                Log.error("MTab.processCallout", e);
1976                retValue = "CalloutNameInvalid " + e.toString();
1977            }
1978            if (!retValue.equals("")) // interrupt on first error
1979
{
1980                Log.error("MTab.processCallout - " + retValue);
1981                return retValue;
1982            }
1983        } // for each callout
1984
return "";
1985    } // processCallout
1986

1987
1988    /**
1989     * Get Value of Field with columnName
1990     * @param columnName column name
1991     * @return value
1992     */

1993    public Object JavaDoc getValue (String JavaDoc columnName)
1994    {
1995        if (columnName == null)
1996            return null;
1997        MField field = m_mTable.getField(columnName);
1998        return getValue(field);
1999    } // getValue
2000

2001    /**
2002     * Get Value of Field
2003     * @param field field
2004     * @return value
2005     */

2006    public Object JavaDoc getValue (MField field)
2007    {
2008        if (field == null)
2009            return null;
2010        return field.getValue();
2011    } // getValue
2012

2013    /**
2014     * Get Value of Field in row
2015     * @param row row
2016     * @param columnName column name
2017     * @return value
2018     */

2019    public Object JavaDoc getValue (int row, String JavaDoc columnName)
2020    {
2021        int col = m_mTable.findColumn(columnName);
2022        if (col == -1)
2023            return null;
2024        return m_mTable.getValueAt(row, col);
2025    } // getValue
2026

2027    /**
2028     * toString
2029     * @return String representation
2030     */

2031    public String JavaDoc toString()
2032    {
2033        String JavaDoc retValue = "MTab #" + m_vo.TabNo;
2034        if (m_vo != null)
2035            retValue += " " + m_vo.Name + " (" + m_vo.AD_Tab_ID + ")";
2036        return retValue;
2037    } // toString
2038

2039    /*************************************************************************/
2040
2041    /**
2042     * @param l listener
2043     */

2044    public synchronized void removePropertyChangeListener(PropertyChangeListener l)
2045    {
2046        m_propertyChangeSupport.removePropertyChangeListener(l);
2047    }
2048    /**
2049     * @param l listener
2050     */

2051    public synchronized void addPropertyChangeListener(PropertyChangeListener l)
2052    {
2053        m_propertyChangeSupport.addPropertyChangeListener(l);
2054    }
2055
2056    /**
2057     * @param l listener
2058     */

2059    public synchronized void removeDataStatusListener(DataStatusListener l)
2060    {
2061        if (m_dataStatusListeners != null && m_dataStatusListeners.contains(l))
2062        {
2063            Vector v = (Vector) m_dataStatusListeners.clone();
2064            v.removeElement(l);
2065            m_dataStatusListeners = v;
2066        }
2067    }
2068    /**
2069     * @param l listener
2070     */

2071    public synchronized void addDataStatusListener(DataStatusListener l)
2072    {
2073        Vector v = m_dataStatusListeners == null ? new Vector(2) : (Vector) m_dataStatusListeners.clone();
2074        if (!v.contains(l))
2075        {
2076            v.addElement(l);
2077            m_dataStatusListeners = v;
2078        }
2079    }
2080
2081} // M_Tab
2082
Popular Tags