IDEMPIERE-1540 Autocomplete for field type "Search" (#91)

Refactoring
Fix table/tabledir auto refresh
Fix MLookupInfo.clone
This commit is contained in:
hengsin 2020-06-01 21:29:04 +08:00 committed by GitHub
parent 9a35f2054d
commit f2cbbc662d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 210 additions and 172 deletions

View File

@ -121,8 +121,10 @@ public final class MLookup extends Lookup implements Serializable
/** Inactive records exists */ /** Inactive records exists */
private boolean m_hasInactive = false; private boolean m_hasInactive = false;
/* Refreshing - disable cashing */ /* Refreshing */
private boolean m_refreshing = false; private boolean m_refreshing = false;
/* Refresh cache(if exists) */
private boolean m_refreshCache = false;
/** Next Read for Parent */ /** Next Read for Parent */
private long m_nextRead = 0; private long m_nextRead = 0;
@ -552,23 +554,17 @@ public final class MLookup extends Lookup implements Serializable
CCache<String, ValueNamePair> vnpCache = null; CCache<String, ValueNamePair> vnpCache = null;
if (isNumber) if (isNumber)
{ {
knpCache = s_directKeyNamePairCache.get(cacheKey); knpCache = getDirectKeyNamePairCache(m_info, cacheKey);
if (knpCache != null) KeyNamePair knp = knpCache.get(Integer.parseInt(key.toString()));
{ if (knp != null)
KeyNamePair knp = knpCache.get(Integer.parseInt(key.toString())); return knp;
if (knp != null)
return knp;
}
} }
else else
{ {
vnpCache = s_directValueNamePairCache.get(cacheKey); vnpCache = getDirectValueNamePairCache(m_info, cacheKey);
if (vnpCache != null) ValueNamePair vnp = vnpCache.get(key.toString());
{ if (vnp != null)
ValueNamePair vnp = vnpCache.get(key.toString()); return vnp;
if (vnp != null)
return vnp;
}
} }
PreparedStatement pstmt = null; PreparedStatement pstmt = null;
ResultSet rs = null; ResultSet rs = null;
@ -596,16 +592,7 @@ public final class MLookup extends Lookup implements Serializable
if (saveInCache) // save if if (saveInCache) // save if
m_lookup.put(Integer.valueOf(keyValue), p); m_lookup.put(Integer.valueOf(keyValue), p);
directValue = p; directValue = p;
if (knpCache != null) knpCache.put(p.getKey(), p);
{
knpCache.put(p.getKey(), p);
}
else
{
knpCache = new CCache<Integer, KeyNamePair>(null, "MLookup.DirectKeyNamePairCache", 100, 60, false, MAX_NAMEPAIR_CACHE_SIZE);
knpCache.put(p.getKey(), p);
s_directKeyNamePairCache.put(cacheKey, knpCache);
}
} }
else else
{ {
@ -614,16 +601,7 @@ public final class MLookup extends Lookup implements Serializable
if (saveInCache) // save if if (saveInCache) // save if
m_lookup.put(value, p); m_lookup.put(value, p);
directValue = p; directValue = p;
if (vnpCache != null) vnpCache.put(p.getValue(), p);
{
vnpCache.put(p.getValue(), p);
}
else
{
vnpCache = new CCache<String, ValueNamePair>(null, "MLookup.DirectValueNamePairCache", 100, 60, false, MAX_NAMEPAIR_CACHE_SIZE);
vnpCache.put(p.getValue(), p);
s_directValueNamePairCache.put(cacheKey, vnpCache);
}
} }
if (rs.next()) if (rs.next())
log.log(Level.SEVERE, m_info.KeyColumn + ": Not unique (first returned) for " log.log(Level.SEVERE, m_info.KeyColumn + ": Not unique (first returned) for "
@ -682,110 +660,93 @@ public final class MLookup extends Lookup implements Serializable
{ {
KeyNamePair knp = null; KeyNamePair knp = null;
int id = Integer.parseInt(key.toString()); int id = Integer.parseInt(key.toString());
knpCache = s_directKeyNamePairCache.get(cacheKey); knpCache = getDirectKeyNamePairCache(m_info, cacheKey);
if (knpCache != null) knp = knpCache.get(id);
if (knp == null)
{ {
knp = knpCache.get(id);
}
if (knp == null)
knp = new KeyNamePair(id, null); knp = new KeyNamePair(id, null);
list.add(knp); notInCaches.put(id, i);
notInCaches.put(id, i); }
list.add(knp);
} }
else else
{ {
ValueNamePair vnp = null; ValueNamePair vnp = null;
vnpCache = s_directValueNamePairCache.get(cacheKey); vnpCache = getDirectValueNamePairCache(m_info, cacheKey);
if (vnpCache != null) vnp = vnpCache.get(key.toString());
{
vnp = vnpCache.get(key.toString());
}
if (vnp == null) if (vnp == null)
{
vnp = new ValueNamePair(key.toString(), null); vnp = new ValueNamePair(key.toString(), null);
list.add(vnp); notInCaches.put(key.toString(), i);
notInCaches.put(key.toString(), i); }
list.add(vnp);
} }
} }
StringBuilder builder = new StringBuilder(); if (notInCaches.size() > 0)
for(int i = 0; i < notInCaches.size(); i++)
{ {
if (builder.length() > 0) StringBuilder builder = new StringBuilder();
builder.append(" UNION ALL "); for(int i = 0; i < notInCaches.size(); i++)
builder.append(m_info.QueryDirect);
}
try (PreparedStatement pstmt = DB.prepareStatement(builder.toString(), null))
{
Set<Object> keySet = notInCaches.keySet();
int i = 0;
for(Object id : keySet)
{ {
i++; if (builder.length() > 0)
if (id instanceof Integer) builder.append(" UNION ALL ");
{ builder.append(m_info.QueryDirect);
pstmt.setInt(i, (int) id);
}
else
{
pstmt.setString(i, id.toString());
}
} }
ResultSet rs = pstmt.executeQuery();
while (rs.next()) try (PreparedStatement pstmt = DB.prepareStatement(builder.toString(), null))
{ {
StringBuilder name = new StringBuilder().append(rs.getString(3)); Set<Object> keySet = notInCaches.keySet();
boolean isActive = rs.getString(4).equals("Y"); int i = 0;
if (!isActive) for(Object id : keySet)
{ {
name.insert(0, INACTIVE_S).append(INACTIVE_E); i++;
} if (id instanceof Integer)
if (isNumber)
{
int keyValue = rs.getInt(1);
KeyNamePair p = new KeyNamePair(keyValue, name.toString());
if (knpCache != null)
{ {
knpCache.put(p.getKey(), p); pstmt.setInt(i, (int) id);
} }
else else
{ {
knpCache = new CCache<Integer, KeyNamePair>(null, "MLookup.DirectKeyNamePairCache", 100, 60, false, MAX_NAMEPAIR_CACHE_SIZE); pstmt.setString(i, id.toString());
knpCache.put(p.getKey(), p);
s_directKeyNamePairCache.put(cacheKey, knpCache);
} }
Integer idx = notInCaches.get(p.getKey());
if (idx != null)
list.set(idx.intValue(), p);
} }
else ResultSet rs = pstmt.executeQuery();
while (rs.next())
{ {
String value = rs.getString(2); StringBuilder name = new StringBuilder().append(rs.getString(3));
ValueNamePair p = new ValueNamePair(value, name.toString()); boolean isActive = rs.getString(4).equals("Y");
if (vnpCache != null) if (!isActive)
{ {
vnpCache.put(p.getValue(), p); name.insert(0, INACTIVE_S).append(INACTIVE_E);
}
if (isNumber)
{
int keyValue = rs.getInt(1);
KeyNamePair p = new KeyNamePair(keyValue, name.toString());
knpCache.put(p.getKey(), p);
Integer idx = notInCaches.get(p.getKey());
if (idx != null)
list.set(idx.intValue(), p);
} }
else else
{ {
vnpCache = new CCache<String, ValueNamePair>(null, "MLookup.DirectValueNamePairCache", 100, 60, false, MAX_NAMEPAIR_CACHE_SIZE); String value = rs.getString(2);
ValueNamePair p = new ValueNamePair(value, name.toString());
vnpCache.put(p.getValue(), p); vnpCache.put(p.getValue(), p);
s_directValueNamePairCache.put(cacheKey, vnpCache); Integer idx = notInCaches.get(p.getValue());
if (idx != null)
list.set(idx.intValue(), p);
} }
Integer idx = notInCaches.get(p.getValue());
if (idx != null)
list.set(idx.intValue(), p);
} }
} catch (SQLException e) {
log.log(Level.SEVERE, e.getMessage(), e);
}
for(int i = list.size()-1; i >= 0; i--)
{
NamePair np = list.get(i);
if (np.getName() == null)
list.remove(i);
} }
} catch (SQLException e) {
log.log(Level.SEVERE, e.getMessage(), e);
}
for(int i = list.size()-1; i >= 0; i--)
{
NamePair np = list.get(i);
if (np.getName() == null)
list.remove(i);
} }
return list.toArray(new NamePair[0]); return list.toArray(new NamePair[0]);
@ -854,6 +815,20 @@ public final class MLookup extends Lookup implements Serializable
return refresh(true); return refresh(true);
} // refresh } // refresh
public int refreshItemsAndCache()
{
if (m_refreshing) return 0;
m_refreshCache = true;
try
{
return refresh();
}
finally
{
m_refreshCache = false;
}
}
/** /**
* Refresh & return number of items read * Refresh & return number of items read
* @param loadParent get data of parent lookups * @param loadParent get data of parent lookups
@ -874,11 +849,17 @@ public final class MLookup extends Lookup implements Serializable
} }
m_refreshing = true; m_refreshing = true;
//force refresh try
m_lookup.clear(); {
fillComboBox(isMandatory(), true, true, false, isShortList()); // idempiere 90 //force refresh
m_refreshing = false; m_lookup.clear();
return m_lookup.size(); fillComboBox(isMandatory(), true, true, false, isShortList()); // idempiere 90
return m_lookup.size();
}
finally
{
m_refreshing = false;
}
} // refresh } // refresh
/** /**
@ -934,12 +915,70 @@ public final class MLookup extends Lookup implements Serializable
return m_info; return m_info;
} }
private final static CCache<String, List<KeyNamePair>> s_keyNamePairCache = new CCache<String, List<KeyNamePair>>(null, "MLookup.KeyNamePairCache", 100, 60, false, 500);
private final static CCache<String, List<ValueNamePair>> s_valueNamePairCache = new CCache<String, List<ValueNamePair>>(null, "MLookup.ValueNamePairCache", 100, 60, false, 500);
private final static CCache<String, CCache<String, List<KeyNamePair>>> s_keyNamePairCache = new CCache<String, CCache<String, List<KeyNamePair>>>(null, "MLookup.KeyNamePairCache", 100, 60, false, 500);
private final static CCache<String, CCache<String, List<ValueNamePair>>> s_valueNamePairCache = new CCache<String, CCache<String, List<ValueNamePair>>>(null, "MLookup.ValueNamePairCache", 100, 60, false, 500);
private final static CCache<String, CCache<Integer, KeyNamePair>> s_directKeyNamePairCache = new CCache<String, CCache<Integer,KeyNamePair>>(null, "", 100, 60, false, 500); private final static CCache<String, CCache<Integer, KeyNamePair>> s_directKeyNamePairCache = new CCache<String, CCache<Integer,KeyNamePair>>(null, "", 100, 60, false, 500);
private final static CCache<String, CCache<String, ValueNamePair>> s_directValueNamePairCache = new CCache<String, CCache<String,ValueNamePair>>(null, "", 100, 60, false, 500); private final static CCache<String, CCache<String, ValueNamePair>> s_directValueNamePairCache = new CCache<String, CCache<String,ValueNamePair>>(null, "", 100, 60, false, 500);
private synchronized static List<KeyNamePair> getKeyNamePairCache(MLookupInfo lookupInfo, String cacheKey)
{
CCache<String, List<KeyNamePair>> knpCache = s_keyNamePairCache.get(lookupInfo.TableName);
if (knpCache == null)
{
knpCache = new CCache<String, List<KeyNamePair>>(lookupInfo.TableName, lookupInfo.TableName + " KeyNamePair Cache", 100, 60, false, 500);
s_keyNamePairCache.put(lookupInfo.TableName, knpCache);
}
List<KeyNamePair> list = knpCache.get(cacheKey);
if (list == null)
{
list = new ArrayList<KeyNamePair>();
knpCache.put(cacheKey, list);
}
return list;
}
private synchronized static List<ValueNamePair> getValueNamePairCache(MLookupInfo lookupInfo, String cacheKey)
{
CCache<String, List<ValueNamePair>> vnpCache = s_valueNamePairCache.get(lookupInfo.TableName);
if (vnpCache == null)
{
vnpCache = new CCache<String, List<ValueNamePair>>(lookupInfo.TableName, lookupInfo.TableName + " ValueNamePair Cache", 100, 60, false, 500);
s_valueNamePairCache.put(lookupInfo.TableName, vnpCache);
}
List<ValueNamePair> list = vnpCache.get(cacheKey);
if (list == null)
{
list = new ArrayList<ValueNamePair>();
vnpCache.put(cacheKey, list);
}
return list;
}
private synchronized static CCache<Integer, KeyNamePair> getDirectKeyNamePairCache(MLookupInfo lookupInfo, String cacheKey)
{
CCache<Integer, KeyNamePair> knpCache = s_directKeyNamePairCache.get(cacheKey);
if (knpCache == null)
{
knpCache = new CCache<Integer, KeyNamePair>(lookupInfo.TableName, lookupInfo.TableName + " DirectKeyNamePairCache", 100, 60, false, MAX_NAMEPAIR_CACHE_SIZE);
s_directKeyNamePairCache.put(cacheKey, knpCache);
}
return knpCache;
}
private synchronized static CCache<String, ValueNamePair> getDirectValueNamePairCache(MLookupInfo lookupInfo, String cacheKey)
{
CCache<String, ValueNamePair> vnpCache = s_directValueNamePairCache.get(cacheKey);
if (vnpCache == null)
{
vnpCache = new CCache<String, ValueNamePair>(lookupInfo.TableName, lookupInfo.TableName + " DirectValueNamePairCache", 100, 60, false, MAX_NAMEPAIR_CACHE_SIZE);
s_directValueNamePairCache.put(cacheKey, vnpCache);
}
return vnpCache;
}
/************************************************************************** /**************************************************************************
* MLookup Loader * MLookup Loader
*/ */
@ -1034,36 +1073,42 @@ public final class MLookup extends Lookup implements Serializable
List<ValueNamePair> vnpCache = null; List<ValueNamePair> vnpCache = null;
if (isNumber) if (isNumber)
{ {
knpCache = s_keyNamePairCache.get(cacheKey); knpCache = getKeyNamePairCache(m_info, cacheKey);
if (knpCache != null) if (knpCache.size() > 0)
{ {
for(KeyNamePair knp : knpCache) if (m_refreshCache)
{ {
m_lookup.put(knp.getKey(), knp); knpCache.clear();
}
else
{
for(KeyNamePair knp : knpCache)
{
m_lookup.put(knp.getKey(), knp);
}
return;
} }
return;
}
else
{
knpCache = new ArrayList<KeyNamePair>();
} }
} }
else else
{ {
vnpCache = s_valueNamePairCache.get(cacheKey); vnpCache = getValueNamePairCache(m_info, cacheKey);
if (vnpCache != null) if (vnpCache.size() > 0)
{ {
for(ValueNamePair vnp : vnpCache) if (m_refreshCache)
{ {
m_lookup.put(vnp.getValue(), vnp); vnpCache.clear();
}
else
{
for(ValueNamePair vnp : vnpCache)
{
m_lookup.put(vnp.getValue(), vnp);
}
return;
} }
return;
} }
else
{
vnpCache = new ArrayList<ValueNamePair>();
}
} }
m_hasInactive = false; m_hasInactive = false;
@ -1132,18 +1177,7 @@ public final class MLookup extends Lookup implements Serializable
vnpCache.add(p); vnpCache.add(p);
} }
// if (log.isLoggable(Level.FINE)) log.fine( m_info.KeyColumn + ": " + name); // if (log.isLoggable(Level.FINE)) log.fine( m_info.KeyColumn + ": " + name);
} }
if (isNumber)
{
if (knpCache.size() <= MAX_NAMEPAIR_CACHE_SIZE)
s_keyNamePairCache.put(cacheKey, knpCache);
}
else
{
if (vnpCache.size() <= MAX_NAMEPAIR_CACHE_SIZE)
s_valueNamePairCache.put(cacheKey, vnpCache);
}
} }
catch (SQLException e) catch (SQLException e)
{ {

View File

@ -26,6 +26,7 @@ import java.util.logging.Level;
import org.compiere.util.CLogger; import org.compiere.util.CLogger;
import org.compiere.util.DB; import org.compiere.util.DB;
import org.compiere.util.Util;
/** /**
* Info Class for Lookup SQL (ValueObject) * Info Class for Lookup SQL (ValueObject)
@ -234,7 +235,7 @@ public class MLookupInfo implements Serializable, Cloneable
{ {
MLookupInfo clone = (MLookupInfo)super.clone(); MLookupInfo clone = (MLookupInfo)super.clone();
clone.parsedValidationCode = ""; clone.parsedValidationCode = "";
clone.IsValidated = false; clone.IsValidated = Util.isEmpty(ValidationCode);
clone.ctx = null; clone.ctx = null;
if (ZoomQuery != null) if (ZoomQuery != null)
clone.ZoomQuery = ZoomQuery.clone(); clone.ZoomQuery = ZoomQuery.clone();

View File

@ -31,6 +31,7 @@ import org.adempiere.webui.factory.InfoManager;
import org.adempiere.webui.panel.InfoPanel; import org.adempiere.webui.panel.InfoPanel;
import org.compiere.model.GridField; import org.compiere.model.GridField;
import org.compiere.model.Lookup; import org.compiere.model.Lookup;
import org.compiere.model.MLookup;
import org.compiere.util.NamePair; import org.compiere.util.NamePair;
import org.compiere.util.Util; import org.compiere.util.Util;
import org.compiere.util.ValueNamePair; import org.compiere.util.ValueNamePair;
@ -51,6 +52,8 @@ public class InfoListSubModel implements ListSubModel<ValueNamePair> {
private String keyColumnName; private String keyColumnName;
private String whereClause; private String whereClause;
private static final int AUTO_COMPLETE_QUERY_TIMEOUT = 1; //1 second
/** /**
* *
* @param lookup * @param lookup
@ -86,6 +89,24 @@ public class InfoListSubModel implements ListSubModel<ValueNamePair> {
ListModelList<ValueNamePair> model = new ListModelList<>(); ListModelList<ValueNamePair> model = new ListModelList<>();
if (value != null && !Util.isEmpty(value.toString(), true)) { if (value != null && !Util.isEmpty(value.toString(), true)) {
String queryText = value.toString().trim(); String queryText = value.toString().trim();
StringBuilder queryBuilder = new StringBuilder(queryText);
queryBuilder.append("?autocomplete={");
queryBuilder.append("timeout:")
.append(AUTO_COMPLETE_QUERY_TIMEOUT)
.append(",")
.append("pagesize:")
.append(nRows);
if (lookup instanceof MLookup) {
MLookup mlookup = (MLookup) lookup;
List<String> displayColumns = mlookup.getLookupInfo().lookupDisplayColumns;
if (displayColumns != null && displayColumns.size() > 0) {
queryBuilder.append(",")
.append("searchcolumn:")
.append(displayColumns.get(0));
}
}
queryBuilder.append("}");
queryText = queryBuilder.toString();
final InfoPanel ip = InfoManager.create(lookup, gridField, tableName, keyColumnName, queryText, false, getWhereClause()); final InfoPanel ip = InfoManager.create(lookup, gridField, tableName, keyColumnName, queryText, false, getWhereClause());
if (ip != null && ip.loadedOK()) { if (ip != null && ip.loadedOK()) {

View File

@ -21,7 +21,6 @@ import static org.compiere.model.SystemIDs.COLUMN_C_INVOICELINE_M_PRODUCT_ID;
import static org.compiere.model.SystemIDs.COLUMN_C_INVOICE_C_BPARTNER_ID; import static org.compiere.model.SystemIDs.COLUMN_C_INVOICE_C_BPARTNER_ID;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.logging.Level; import java.util.logging.Level;
@ -56,7 +55,6 @@ import org.compiere.model.X_AD_CtxHelp;
import org.compiere.util.CLogger; import org.compiere.util.CLogger;
import org.compiere.util.DisplayType; import org.compiere.util.DisplayType;
import org.compiere.util.Env; import org.compiere.util.Env;
import org.compiere.util.Util;
import org.zkoss.zk.au.out.AuScript; import org.zkoss.zk.au.out.AuScript;
import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions; import org.zkoss.zk.ui.Executions;
@ -76,8 +74,7 @@ import org.zkoss.zk.ui.util.Clients;
*/ */
public class WSearchEditor extends WEditor implements ContextMenuListener, ValueChangeListener, IZoomableEditor public class WSearchEditor extends WEditor implements ContextMenuListener, ValueChangeListener, IZoomableEditor
{ {
private static final int MAX_AUTO_COMPLETE_ROWS = 50; private static final int MAX_AUTO_COMPLETE_ROWS = 50;
private static final int AUTO_COMPLETE_QUERY_TIMEOUT = 1; //1 second
private static final String[] LISTENER_EVENTS = {Events.ON_CLICK, Events.ON_CHANGE, Events.ON_OK}; private static final String[] LISTENER_EVENTS = {Events.ON_CLICK, Events.ON_CHANGE, Events.ON_OK};
public static final String ATTRIBUTE_IS_INFO_PANEL_OPEN = "ATTRIBUTE_IS_INFO_PANEL_OPEN"; public static final String ATTRIBUTE_IS_INFO_PANEL_OPEN = "ATTRIBUTE_IS_INFO_PANEL_OPEN";
private Lookup lookup; private Lookup lookup;
@ -217,27 +214,7 @@ public class WSearchEditor extends WEditor implements ContextMenuListener, Value
getComponent().getCombobox().addEventListener(Events.ON_CHANGING, (EventListener<InputEvent>)(e) -> { getComponent().getCombobox().addEventListener(Events.ON_CHANGING, (EventListener<InputEvent>)(e) -> {
if (!e.isChangingBySelectBack()) { if (!e.isChangingBySelectBack()) {
listModel.setWhereClause(getWhereClause()); listModel.setWhereClause(getWhereClause());
String s = e.getValue(); String s = e.getValue();
if (!Util.isEmpty(s, true)) {
StringBuilder query = new StringBuilder(s);
query.append("?autocomplete={");
query.append("timeout:")
.append(AUTO_COMPLETE_QUERY_TIMEOUT)
.append(",")
.append("pagesize:")
.append(MAX_AUTO_COMPLETE_ROWS);
if (lookup instanceof MLookup) {
MLookup mlookup = (MLookup) lookup;
List<String> displayColumns = mlookup.getLookupInfo().lookupDisplayColumns;
if (displayColumns != null && displayColumns.size() > 0) {
query.append(",")
.append("searchcolumn:")
.append(displayColumns.get(0));
}
}
query.append("}");
s = query.toString();
}
getComponent().getCombobox().setModel(listModel.getSubModel(s, MAX_AUTO_COMPLETE_ROWS)); getComponent().getCombobox().setModel(listModel.getSubModel(s, MAX_AUTO_COMPLETE_ROWS));
} }
}); });

View File

@ -599,7 +599,12 @@ ContextMenuListener, IZoomableEditor
Object curValue = getValue(); Object curValue = getValue();
if (isReadWrite()) if (isReadWrite())
lookup.refresh(); {
if (lookup instanceof MLookup)
((MLookup) lookup).refreshItemsAndCache();
else
lookup.refresh();
}
else else
refreshList(); refreshList();
if (curValue != null) if (curValue != null)