KickJava   Java API By Example, From Geeks To Geeks.

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


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.io.Serializable JavaDoc;
19
20 import org.compiere.util.NamePair;
21 import org.compiere.util.KeyNamePair;
22 import org.compiere.util.ValueNamePair;
23 import org.compiere.util.DisplayType;
24 import org.compiere.util.DB;
25 import org.compiere.util.Env;
26 import org.compiere.util.Log;
27
28 /**
29  * An intelligent MutableComboBoxModel, which determines what can be cached.
30  * <pre>
31  * Validated - SQL is final / not dynamic
32  * AllLoaded - All Records are loaded
33  *
34  * Get Info about Lookup
35  * - SQL
36  * - KeyColumn
37  * - Zoom Target
38  * </pre>
39  * @author Jorg Janke
40  * @version $Id: MLookup.java,v 1.17 2003/11/06 07:09:09 jjanke Exp $
41  */

42 public final class MLookup extends Lookup implements Serializable JavaDoc
43 {
44     /**
45      * MLookup Constructor
46      * @param info info
47      * @param TabNo tab no
48      */

49     public MLookup (MLookupInfo info, int TabNo)
50     {
51         super();
52         m_info = info;
53         log.debug(m_info.KeyColumn);
54
55         // load into local lookup, if already cached
56
if (MLookupCache.loadFromCache (m_info, m_lookup))
57             return;
58
59         // Don't load Search
60
if (m_info.DisplayType == DisplayType.Search)
61             return;
62         // Don't load Parents
63
if (m_info.IsParent)
64         {
65             m_hasInactive = true; // creates focus listener for dynamic loading
66
return; // required when parent needs to be selected (e.g. price from product)
67
}
68         //
69
m_loader = new Loader();
70     // if (TabNo != 0)
71
// m_loader.setPriority(Thread.MIN_PRIORITY);
72
m_loader.start();
73     // m_loader.run();
74
} // MLookup
75

76     /** Inactive Marker Start */
77     public static final String JavaDoc INACTIVE_S = "~";
78     /** Inactive Marker End */
79     public static final String JavaDoc INACTIVE_E = "~";
80
81     /** The Lookup Info Value Object */
82     private MLookupInfo m_info = null;
83
84     /** Storage of data Key-NamePair */
85     private volatile LinkedHashMap m_lookup = new LinkedHashMap();
86     /** The Data Loader */
87     private Loader m_loader;
88     /** Number of rows to load */
89     private int m_maxRows = 1000000; // 1 Mio
90
//
91

92     /** All Data loaded */
93     private boolean m_allLoaded = false;
94     /** Inactive records exists */
95     private boolean m_hasInactive = false;
96
97     /* Refreshing - disable cashing */
98     private boolean m_refreshing = false;
99     /** Indicator for Null */
100     private static Integer JavaDoc s_minusOne = new Integer JavaDoc(-1);
101
102     /**
103      * Dispose
104      */

105     public void dispose()
106     {
107         if (m_info != null)
108             log.debug(m_info.KeyColumn + ": dispose");
109         if (m_loader != null)
110         {
111             while (m_loader.isAlive())
112                 m_loader.interrupt();
113         }
114         m_loader = null;
115         //
116
if (m_lookup != null)
117             m_lookup.clear();
118         m_lookup = null;
119         if (m_lookupDirect != null)
120             m_lookupDirect.clear();
121         m_lookupDirect = null;
122         //
123
m_info = null;
124         //
125
super.dispose();
126     } // dispose
127

128     /**
129      * Wait until async Load Complete
130      */

131     public void loadComplete()
132     {
133         if (m_loader != null && m_loader.isAlive())
134         {
135             try
136             {
137                 m_loader.join();
138                 m_loader = null;
139             }
140             catch (InterruptedException JavaDoc ie)
141             {
142                 log.error(m_info.KeyColumn + ": loadComplete - join interrupted", ie);
143             }
144         }
145     } // loadComplete
146

147     /**
148      * Get value (name) for key.
149      * If not found return null;
150      * @param key key (Integer for Keys or String for Lists)
151      * @return value
152      */

153     public NamePair get (Object JavaDoc key)
154     {
155         if (key == null || s_minusOne.equals(key)) // indicator for null
156
return null;
157
158         // try cache
159
NamePair retValue = (NamePair)m_lookup.get(key);
160         if (retValue != null)
161             return retValue;
162
163         // Not found and waiting for loader
164
if (m_loader != null && m_loader.isAlive())
165         {
166             if (Log.isTraceLevel(7))
167                 log.debug ((m_info.KeyColumn==null ? "ID="+m_info.Column_ID : m_info.KeyColumn) + ": get - waiting for Loader");
168             loadComplete();
169             // is most current
170
retValue = (NamePair)m_lookup.get(key);
171             if (retValue != null)
172                 return retValue;
173         }
174
175         // Always check for parents - not if we SQL was validated and completely loaded
176
if (!m_info.IsParent && m_info.IsValidated && m_allLoaded)
177         {
178             if (Log.isTraceLevel(7))
179                 log.debug (m_info.KeyColumn + ": get <NULL> - " + key // + "(" + key.getClass()
180
+ "; Size=" + m_lookup.size());
181         // Log.trace(10, m_lookup.keySet().toString(), "ContainsKey = " + m_lookup.containsKey(key));
182
// also for new values and inactive ones
183
return getDirect(key, false, true); // cache locally
184
}
185
186         if (Log.isTraceLevel(7))
187             log.debug (m_info.KeyColumn + ": get - " + key
188                 + "; Size=" + m_lookup.size() + "; Validated=" + m_info.IsValidated
189                 + "; All Loaded=" + m_allLoaded + "; HasInactive=" + m_hasInactive);
190         // never loaded
191
if (!m_allLoaded && m_lookup.size() == 0 && m_info.DisplayType != DisplayType.Search)
192         {
193             m_loader = new Loader();
194             m_loader.run(); // sync!
195
retValue = (NamePair)m_lookup.get(key);
196             if (retValue != null)
197                 return retValue;
198         }
199         // Try to get it directly
200
return getDirect(key, false, false); // do NOT cache
201
} // get
202

203     /**
204      * Get Display value (name).
205      * If not found return key embedded in inactive signs.
206      * @param key key
207      * @return value
208      */

209     public String JavaDoc getDisplay (Object JavaDoc key)
210     {
211         if (key == null)
212             return "";
213         //
214
Object JavaDoc display = get (key);
215         if (display == null)
216             return "<" + key.toString() + ">";
217         return display.toString();
218     } // getDisplay
219

220     /**
221      * The Lookup contains the key
222      * @param key key
223      * @return true if key is known
224      */

225     public boolean containsKey (Object JavaDoc key)
226     {
227         return m_lookup.containsKey(key);
228     } // containsKey
229

230     /**
231      * @return a string representation of the object.
232      */

233     public String JavaDoc toString()
234     {
235         return "MLookup[" + m_info.KeyColumn + ",Column_ID=" + m_info.Column_ID
236             + ",Size=" + m_lookup.size() + ",Validated=" + isValidated()
237             + "-" + getValidation()
238             + "]";
239     } // toString
240

241     /**
242      * Indicates whether some other object is "equal to" this one.
243      * @param obj the reference object with which to compare.
244      * @return <code>true</code> if this object is the same as the obj
245      * argument; <code>false</code> otherwise.
246      */

247     public boolean equals(Object JavaDoc obj)
248     {
249         if (obj instanceof MLookup)
250         {
251             MLookup ll = (MLookup)obj;
252             if (ll.m_info.Column_ID == this.m_info.Column_ID)
253                 return true;
254         }
255         return false;
256     } // equals
257

258     /**
259      * Return Size
260      * @return size
261      */

262     public int size()
263     {
264         return m_lookup.size();
265     } // size
266

267     /**
268      * Is it all loaded
269      * @return true, if all loaded
270      */

271     public boolean isAllLoaded()
272     {
273         return m_allLoaded;
274     } // isAllLoaded
275

276     /**
277      * Is the List fully Validated
278      * @return true, if validated
279      */

280     public boolean isValidated()
281     {
282         return m_info.IsValidated;
283     } // isValidated
284

285     /**
286      * Get Validation SQL
287      * @return Validation SQL
288      */

289     public String JavaDoc getValidation()
290     {
291         return m_info.ValidationCode;
292     } // getValidation
293

294     /**
295      * Get Reference Value
296      * @return Reference Value
297      */

298     public int getAD_Reference_Value_ID()
299     {
300         return m_info.AD_Reference_Value_ID;
301     } // getAD_Reference_Value_ID
302

303
304     /**
305      * Has inactive elements in list
306      * @return true, if list contains inactive values
307      */

308     public boolean hasInactive()
309     {
310         return m_hasInactive;
311     } // hasInactive
312

313     /**
314      * Return info as ArrayList containing Value/KeyNamePair
315      * @param onlyValidated only validated
316      * @param loadParent get Data even for parent lookups
317      * @return List
318      */

319     private ArrayList getData (boolean onlyValidated, boolean loadParent)
320     {
321         if (m_loader != null && m_loader.isAlive())
322         {
323             log.debug((m_info.KeyColumn==null ? "ID="+m_info.Column_ID : m_info.KeyColumn) + ": getData - waiting for Loader");
324             loadComplete();
325         }
326
327         // Never Loaded (correctly)
328
if (!m_allLoaded || m_lookup.size() == 0)
329             refresh (loadParent);
330
331         // already validation included
332
if (m_info.IsValidated)
333             return new ArrayList(m_lookup.values());
334
335         if (!m_info.IsValidated && onlyValidated)
336         {
337             refresh (loadParent);
338             log.debug(m_info.KeyColumn + ": getData Validated - #" + m_lookup.size());
339         }
340
341         return new ArrayList(m_lookup.values());
342     } // getData
343

344     /**
345      * Return data as Array containing Value/KeyNamePair
346      * @param mandatory if not mandatory, an additional empty value is inserted
347      * @param onlyValidated only validated
348      * @param onlyActive only active
349      * @param temporary force load for temporary display
350      * @return list
351      */

352     public ArrayList getData (boolean mandatory, boolean onlyValidated, boolean onlyActive, boolean temporary)
353     {
354         // create list
355
ArrayList list = getData (onlyValidated, temporary);
356
357         // Remove inactive choices
358
if (onlyActive && m_hasInactive)
359         {
360             // list from the back
361
for (int i = list.size(); i > 0; i--)
362             {
363                 Object JavaDoc o = list.get(i-1);
364                 if (o != null)
365                 {
366                     String JavaDoc s = o.toString();
367                     if (s.startsWith(INACTIVE_S) && s.endsWith(INACTIVE_E))
368                         list.remove(i-1);
369                 }
370             }
371         }
372
373         // Add Optional (empty) selection
374
if (!mandatory)
375         {
376             NamePair p = null;
377             if (m_info.KeyColumn != null && m_info.KeyColumn.endsWith("_ID"))
378                 p = new KeyNamePair (-1, "");
379             else
380                 p = new ValueNamePair ("", "");
381             list.add(0, p);
382         }
383
384         return list;
385     } // getData
386

387     /** Save getDirect last return value */
388     private HashMap m_lookupDirect = null;
389     /** Save last unsuccessful */
390     private Object JavaDoc m_directNullKey = null;
391
392     /**
393      * Get Data Direct from Table.
394      * @param key key
395      * @param saveInCache save in cache for r/w
396      * @param cacheLocal cache locally for r/o
397      * @return value
398      */

399     public NamePair getDirect (Object JavaDoc key, boolean saveInCache, boolean cacheLocal)
400     {
401         if (Log.isTraceLevel(7))
402             log.debug (m_info.KeyColumn + ": getDirect - " + key);
403         // Nothing to query
404
if (key == null || m_info.QueryDirect == null || m_info.QueryDirect.length() == 0)
405             return null;
406         if (key.equals(m_directNullKey))
407             return null;
408         //
409
NamePair directValue = null;
410         if (m_lookupDirect != null)
411         {
412             directValue = (NamePair)m_lookupDirect.get(key);
413             if (directValue != null)
414                 return directValue;
415         }
416         boolean isNumber = m_info.KeyColumn.endsWith("_ID");
417         try
418         {
419             // SELECT Key, Value, Name FROM ...
420
PreparedStatement pstmt = DB.prepareStatement(m_info.QueryDirect);
421             if (isNumber)
422                 pstmt.setInt(1, Integer.parseInt(key.toString()));
423             else
424                 pstmt.setString(1, key.toString());
425             ResultSet rs = pstmt.executeQuery();
426             if (rs.next())
427             {
428                 String JavaDoc name = rs.getString(3);
429                 if (isNumber)
430                 {
431                     int keyValue = rs.getInt(1);
432                     KeyNamePair p = new KeyNamePair(keyValue, name);
433                     if (saveInCache) // save if
434
m_lookup.put(new Integer JavaDoc(keyValue), p);
435                     directValue = p;
436                 }
437                 else
438                 {
439                     String JavaDoc value = rs.getString(2);
440                     ValueNamePair p = new ValueNamePair(value, name);
441                     if (saveInCache) // save if
442
m_lookup.put(value, p);
443                     directValue = p;
444                 }
445                 if (rs.next())
446                     log.error(m_info.KeyColumn + ": getDirect - not unique (first returned) for "
447                         + key + " SQL=" + m_info.QueryDirect);
448             }
449             else
450             {
451                 m_directNullKey = key;
452                 directValue = null;
453             }
454
455             rs.close();
456             pstmt.close();
457             if (Log.isTraceLevel(10))
458                 log.debug (m_info.KeyColumn + ": getDirect - " + directValue + " - " + m_info);
459         }
460         catch (SQLException e)
461         {
462             log.error(m_info.KeyColumn + ": getDirect - SQL=" + m_info.QueryDirect + "; Key=" + key, e);
463             directValue = null;
464         }
465         // Cache Local if not added to R/W cache
466
if (cacheLocal && !saveInCache && directValue != null)
467         {
468             if (m_lookupDirect == null)
469                 m_lookupDirect = new HashMap();
470             m_lookupDirect.put(key, directValue);
471         }
472         m_hasInactive = true;
473         return directValue;
474     } // getDirect
475

476     /**
477      * Get Zoom
478      * @return Zoom Window
479      */

480     public int getZoom()
481     {
482         return m_info.ZoomWindow;
483     } // getZoom
484

485     /**
486      * Get Zoom
487      * @param isSOTrx SO trx
488      * @return Zoom Window
489      */

490     public int getZoom(String JavaDoc isSOTrx)
491     {
492         if ("N".equals(isSOTrx) && m_info.ZoomWindowPO != 0)
493             return m_info.ZoomWindowPO;
494         return m_info.ZoomWindow;
495     } // getZoom
496

497     /**
498      * Get Zoom Query String
499      * @return Zoom SQL Where Clause
500      */

501     public MQuery getZoomQuery()
502     {
503         return m_info.ZoomQuery;
504     } // getZoom
505

506     /**
507      * Get underlying fully qualified Table.Column Name
508      * @return Key Column
509      */

510     public String JavaDoc getColumnName()
511     {
512         return m_info.KeyColumn;
513     } // g2etColumnName
514

515     /**
516      * Refresh & return number of items read.
517      * Get get data of parent lookups
518      * @return no of items read
519      */

520     public int refresh ()
521     {
522         return refresh(true);
523     } // refresh
524

525     /**
526      * Refresh & return number of items read
527      * @param loadParent get data of parent lookups
528      * @return no of items read
529      */

530     public int refresh (boolean loadParent)
531     {
532         if (!loadParent && m_info.IsParent)
533             return 0;
534         log.debug(m_info.KeyColumn + ": refresh - start");
535         m_refreshing = true;
536         m_loader = new Loader();
537         m_loader.start();
538         loadComplete();
539         log.info(m_info.KeyColumn + ": refresh - #" + m_lookup.size());
540         m_refreshing = false;
541         return m_lookup.size();
542     } // refresh
543

544     /*************************************************************************/
545
546     /**
547      * MLookup Loader
548      */

549     class Loader extends Thread JavaDoc implements Serializable JavaDoc
550     {
551         public Loader()
552         {
553             super("MLoader-" + m_info.KeyColumn);
554         } // Loader
555

556         private long m_startTime = System.currentTimeMillis();
557
558         /**
559          * Load Lookup
560          */

561         public void run()
562         {
563             long startTime = System.currentTimeMillis();
564             MLookupCache.loadStart (m_info);
565             String JavaDoc sql = m_info.Query;
566
567             // not validated
568
if (!m_info.IsValidated)
569             {
570                 String JavaDoc validation = Env.parseContext(m_info.ctx, m_info.WindowNo, m_info.ValidationCode, false);
571                 if (validation.length() == 0 && m_info.ValidationCode.length() > 0)
572                 {
573                     log.warn(m_info.KeyColumn + ": Loader.run - NOT Validated: " + m_info.ValidationCode);
574                     return;
575                 }
576                 else
577                 {
578                     log.debug(m_info.KeyColumn + ": Loader.run - Validated: " + validation);
579                     int posOrder = sql.lastIndexOf(" ORDER BY ");
580                     if (posOrder != -1)
581                         sql = sql.substring(0, posOrder) + " AND " + validation
582                             + sql.substring(posOrder);
583                     else
584                         sql += " AND " + validation;
585                 }
586             }
587             //
588
if (Log.isTraceLevel(7))
589                 Env.setContext(m_info.ctx, Env.WINDOW_MLOOKUP, m_info.Column_ID, m_info.KeyColumn, sql);
590             //
591
if (isInterrupted())
592             {
593                 log.error(m_info.KeyColumn + ": Loader.run interrupted");
594                 return;
595             }
596
597             if (Log.isTraceLevel(10))
598                 log.debug(m_info.KeyColumn + ": Loader.run - " + sql);
599                 
600             // Reset
601
m_lookup.clear();
602             boolean isNumber = m_info.KeyColumn.endsWith("_ID");
603             m_hasInactive = false;
604             int rows = 0;
605             try
606             {
607                 // SELECT Key, Value, Name, IsActive FROM ...
608
PreparedStatement pstmt = DB.prepareStatement(sql);
609                 ResultSet rs = pstmt.executeQuery();
610
611                 // Get first ... rows
612
m_allLoaded = true;
613                 while (rs.next())
614                 {
615                     if (rows++ > m_maxRows)
616                     {
617                         m_allLoaded = false;
618                         break;
619                     }
620                     // check for interrupted every 50 rows
621
if (rows % 50 == 0 && isInterrupted())
622                         break;
623
624                     // load data
625
String JavaDoc name = rs.getString(3);
626                     boolean isActive = rs.getString(4).equals("Y");
627                     if (!isActive)
628                     {
629                         name = INACTIVE_S + name + INACTIVE_E;
630                         m_hasInactive = true;
631                     }
632                     if (isNumber)
633                     {
634                         int key = rs.getInt(1);
635                         KeyNamePair p = new KeyNamePair(key, name);
636                         m_lookup.put(new Integer JavaDoc(key), p);
637                     }
638                     else
639                     {
640                         String JavaDoc value = rs.getString(2);
641                         ValueNamePair p = new ValueNamePair(value, name);
642                         m_lookup.put(value, p);
643                     }
644                 // Log.trace(Log.l6_Database, m_info.KeyColumn + " " + name);
645
}
646                 rs.close();
647                 pstmt.close();
648             }
649             catch (SQLException e)
650             {
651                 log.error(m_info.KeyColumn + ": Loader.run - SQL=" + sql, e);
652             }
653             int size = m_lookup.size();
654             if (Log.isTraceLevel(8))
655                 log.debug(m_info.KeyColumn + ": Loader.run"
656                 // + " ID=" + m_info.AD_Column_ID + " " +
657
+ " - complete #" + size
658                     + " - ms=" + String.valueOf(System.currentTimeMillis()-m_startTime)
659                     + " (" + String.valueOf(System.currentTimeMillis()-startTime) + ")");
660             MLookupCache.loadEnd (m_info, m_lookup);
661             if (size > 1000)
662                 log.warn(m_info.KeyColumn + ": Loader.run - Too many records=" + size);
663         } // run
664
} // Loader
665

666 } // MLookup
667
Popular Tags