/******************************************************************************
* Product: ADempiere ERP & CRM Smart Business Solution *
* Copyright (C) 2009 www.metas.de *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program is distributed in the hope *
* that it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
*****************************************************************************/
package org.adempiere.model;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import org.adempiere.exceptions.AdempiereException;
import org.compiere.model.MAccount;
import org.compiere.model.MAttributeSetInstance;
import org.compiere.model.MChart;
import org.compiere.model.MColumn;
import org.compiere.model.MImage;
import org.compiere.model.MLocation;
import org.compiere.model.MLocator;
import org.compiere.model.MQuery;
import org.compiere.model.MResourceAssignment;
import org.compiere.model.MRole;
import org.compiere.model.MSysConfig;
import org.compiere.model.MTab;
import org.compiere.model.MTable;
import org.compiere.model.PO;
import org.compiere.util.CLogger;
import org.compiere.util.DB;
import org.compiere.util.DisplayType;
import org.compiere.util.Env;
import org.compiere.util.Util;
/**
* Generic provider of zoom targets. Contains pieces of {@link org.adempiere.webui.WZoomAcross}
* methods getZoomTargets and addTarget.
*
* @author Tobias Schoeneberg, www.metas.de - FR [ 2897194 ] Advanced Zoom and RelationTypes
*
*/
public class GenericZoomProvider implements IZoomProvider {
private static final CLogger logger = CLogger.getCLogger(GenericZoomProvider.class);
private Map queries;
@Override
public List retrieveZoomInfos(PO po) {
// User preference
boolean detailedZoom = "Y".equals(Env.getContext(Env.getCtx(), "P|IsDetailedZoomAcross"));
StringBuilder sqlb = new StringBuilder().append(
"SELECT w.AD_Window_ID, w.Name, tt.Name, f.Name, t.TableName, c.ColumnName, tt.AD_Tab_ID, ");
boolean baseLanguage = Env.isBaseLanguage(Env.getCtx(), "AD_Window");
String tabAlias;
if (baseLanguage) {
tabAlias = "tt";
} else {
tabAlias = "tt0";
}
String justFirstTab = "";
if (! detailedZoom) {
justFirstTab = " AND " + tabAlias + ".SeqNo=10 ";
}
if (baseLanguage) {
sqlb.append(
"tt.SeqNo "
+ "FROM AD_Table t "
+ "JOIN AD_Tab tt ON (tt.AD_Table_ID=t.AD_Table_ID AND tt.IsActive='Y' AND tt.Name NOT LIKE 'Used in%' AND tt.IsReadOnly='N' AND tt.IsSortTab='N'")
.append(justFirstTab)
.append(") "
+ "JOIN AD_Window w ON (tt.AD_Window_ID=w.AD_Window_ID AND w.IsActive='Y') "
+ "JOIN AD_Column c ON (t.AD_Table_ID=c.AD_Table_ID AND c.IsActive='Y' AND c.IsKey='N' AND c.IsParent='N' AND c.ColumnSQL IS NULL) "
+ "JOIN AD_Field f ON (f.AD_Column_ID=c.AD_Column_ID AND f.AD_Tab_ID=tt.AD_Tab_ID AND f.IsActive='Y' AND f.IsDisplayed='Y') ");
} else {
sqlb.append(
"tt0.SeqNo "
+ "FROM AD_Table t "
+ "JOIN AD_Tab tt0 ON (tt0.AD_Table_ID=t.AD_Table_ID AND tt0.IsActive='Y' AND tt0.Name NOT LIKE 'Used in%' AND tt0.IsReadOnly='N' AND tt0.IsSortTab='N'")
.append(justFirstTab)
.append(") "
+ "JOIN AD_Tab_Trl tt ON (tt.AD_Tab_ID=tt0.AD_Tab_ID AND tt.AD_Language=?) "
+ "JOIN AD_Window w0 ON (tt0.AD_Window_ID=w0.AD_Window_ID AND w0.IsActive='Y') "
+ "JOIN AD_Window_Trl w ON (w.AD_Window_ID=w0.AD_Window_ID AND w.AD_Language=?) "
+ "JOIN AD_Column c ON (t.AD_Table_ID=c.AD_Table_ID AND c.IsActive='Y' AND c.IsKey='N' AND c.IsParent='N' AND c.ColumnSQL IS NULL) "
+ "JOIN AD_Field f0 ON (f0.AD_Column_ID=c.AD_Column_ID AND f0.AD_Tab_ID=tt0.AD_Tab_ID AND f0.IsActive='Y' AND f0.IsDisplayed='Y') "
+ "JOIN AD_Field_Trl f ON (f.AD_Field_ID=f0.AD_Field_ID AND f.AD_Language=?) ");
}
sqlb.append(
"LEFT JOIN AD_Ref_Table r ON (c.AD_Reference_Value_ID=r.AD_Reference_ID) "
+ "LEFT JOIN AD_Table tr ON (r.AD_Table_ID=tr.AD_Table_ID) "
+ "WHERE t.IsActive='Y' "
+ " AND t.TableName NOT LIKE 'I|_%' ESCAPE '|' " // not import tables
+ " AND t.TableName NOT LIKE 'T|_%' ESCAPE '|' " // not temp tables
+ " AND t.IsView='N' "); // not views
if (detailedZoom) {
sqlb.append(
" AND ( ( c.ColumnName=? AND c.AD_Reference_ID IN (?,?)) "); // TableDir/TableDirUU
if (MLocation.COLUMNNAME_C_Location_ID.equals(po.get_KeyColumns()[0]))
sqlb.append(" OR c.AD_Reference_ID=").append(DisplayType.Location);
else if (MAccount.COLUMNNAME_C_ValidCombination_ID.equals(po.get_KeyColumns()[0]))
sqlb.append(" OR c.AD_Reference_ID=").append(DisplayType.Account);
else if (MLocator.COLUMNNAME_M_Locator_ID.equals(po.get_KeyColumns()[0]))
sqlb.append(" OR c.AD_Reference_ID=").append(DisplayType.Locator);
else if (MImage.COLUMNNAME_AD_Image_ID.equals(po.get_KeyColumns()[0]))
sqlb.append(" OR c.AD_Reference_ID=").append(DisplayType.Image);
else if (MResourceAssignment.COLUMNNAME_S_ResourceAssignment_ID.equals(po.get_KeyColumns()[0]))
sqlb.append(" OR c.AD_Reference_ID=").append(DisplayType.Assignment);
else if (MAttributeSetInstance.COLUMNNAME_M_AttributeSetInstance_ID.equals(po.get_KeyColumns()[0]))
sqlb.append(" OR c.AD_Reference_ID=").append(DisplayType.PAttribute);
else if (MChart.COLUMNNAME_AD_Chart_ID.equals(po.get_KeyColumns()[0]))
sqlb.append(" OR c.AD_Reference_ID=").append(DisplayType.Chart);
sqlb.append(" OR ( c.ColumnName=? AND c.AD_Reference_ID IN (?,?) AND c.AD_Reference_Value_ID IS NULL ) " // Search/SearchUU
+ " OR ( c.AD_Reference_ID IN (?,?,?,?) AND c.AD_Reference_Value_ID=r.AD_Reference_ID AND tr.TableName=? ) ) "); // Table/Search/Table/SearchUU
} else {
sqlb.append(" AND c.ColumnName=? ");
}
sqlb.append(" ORDER BY 2, 8");
final PreparedStatement pstmt = DB.prepareStatement(sqlb.toString(), null);
ResultSet rs = null;
try {
int index = 1;
if (!baseLanguage) {
pstmt.setString(index++, Env.getAD_Language(Env.getCtx()));
pstmt.setString(index++, Env.getAD_Language(Env.getCtx()));
pstmt.setString(index++, Env.getAD_Language(Env.getCtx()));
}
pstmt.setString(index++, po.get_KeyColumns()[0]);
if (detailedZoom) {
pstmt.setInt(index++, DisplayType.TableDir);
pstmt.setInt(index++, DisplayType.TableDirUU);
pstmt.setString(index++, po.get_KeyColumns()[0]);
pstmt.setInt(index++, DisplayType.Search);
pstmt.setInt(index++, DisplayType.SearchUU);
pstmt.setInt(index++, DisplayType.Table);
pstmt.setInt(index++, DisplayType.TableUU);
pstmt.setInt(index++, DisplayType.Search);
pstmt.setInt(index++, DisplayType.SearchUU);
pstmt.setString(index++, po.get_TableName());
}
rs = pstmt.executeQuery();
final List result = new ArrayList();
queries = new HashMap();
while (rs.next()) {
int AD_Window_ID = rs.getInt(1);
String winName = rs.getString(2);
String tabName = rs.getString(3);
String fldName = rs.getString(4);
String targetTableName = rs.getString(5);
String targetColumnName = rs.getString(6);
int AD_Tab_ID = rs.getInt(7);
Boolean access = MRole.getDefault().getWindowAccess(AD_Window_ID);
if (access == null)
continue;
final MQuery query = evaluateQuery(targetTableName, targetColumnName, AD_Tab_ID, po);
if (query != null && query.getRecordCount() > 0) {
if (detailedZoom) {
result.add(new ZoomInfoFactory.ZoomInfo(AD_Window_ID, query, winName + " / " + tabName + " / " + fldName));
} else {
result.add(new ZoomInfoFactory.ZoomInfo(AD_Window_ID, query, winName));
}
}
}
queries = null;
return result;
} catch (SQLException e) {
logger.log(Level.SEVERE, sqlb.toString(), e);
throw new AdempiereException(e);
} finally {
DB.close(rs, pstmt);
}
}
/**
*
* @param targetTableName
* @param targetColumnName
* @param AD_Tab_ID
* @param po
* @return MQuery
*/
private MQuery evaluateQuery(String targetTableName, String targetColumnName, int AD_Tab_ID, final PO po) {
Properties ctx = Env.getCtx();
int clientID = Env.getAD_Client_ID(ctx);
final MQuery query = new MQuery();
MTable table = MTable.get(ctx, targetTableName);
if (! table.columnExistsInDB("AD_Client_ID")) // table doesn't have AD_Client_ID
return null;
int tabIDLoop = AD_Tab_ID;
int levelUp = 0;
while (true) {
MTab tab = MTab.get(tabIDLoop);
String whereCtx = tab.getWhereClause();
if (!Util.isEmpty(whereCtx, true)) {
if (whereCtx.indexOf("@") != -1)
whereCtx = Env.parseVariable(whereCtx, po, null, true);
if (whereCtx.indexOf("@") != -1) // could not parse - probably window context variable in where tab
return null;
if (levelUp == 0) {
query.addRestriction("(" + whereCtx + ")");
} else if (levelUp == 1) {
MTable parentTable = MTable.get(ctx, tab.getAD_Table_ID());
String parentTableName = parentTable.getTableName();
StringBuilder subquery = new StringBuilder()
.append(parentTableName)
.append("_ID IN (SELECT ")
.append(parentTableName)
.append("_ID FROM ")
.append(parentTableName)
.append(" WHERE ")
.append(whereCtx)
.append(")");
query.addRestriction("(" + subquery + ")");
} else {
// Cannot add where beyond the first parent - need to implement recursion
return null;
}
}
levelUp++;
tabIDLoop = tab.getParentTabID();
if (tabIDLoop < 0)
break;
}
MColumn column = table.getColumn(targetColumnName);
String refTableName = column.getReferenceTableName();
MTable refTable = MTable.get(ctx, refTableName);
StringBuilder restriction = new StringBuilder(targetTableName)
.append(".")
.append(targetColumnName)
.append("=");
if (refTable.isUUIDKeyTable()) {
restriction.append(DB.TO_STRING(po.get_UUID()));
query.setZoomValue(po.get_UUID());
} else {
restriction.append(po.get_ID());
query.setZoomValue(po.get_ID());
}
query.addRestriction(restriction.toString());
query.setZoomTableName(targetTableName);
query.setZoomColumnName(targetColumnName);
String accessLevel = table.getAccessLevel();
if ( clientID != 0
&& MTable.ACCESSLEVEL_SystemOnly.equals(accessLevel)) {
return null;
}
if ( clientID != 0
&& ( MTable.ACCESSLEVEL_All.equals(accessLevel)
|| MTable.ACCESSLEVEL_SystemPlusClient.equals(accessLevel))) {
query.addRestriction(targetTableName+".AD_Client_ID IN (0, " + clientID + ")");
} else {
query.addRestriction(targetTableName+".AD_Client_ID=" + clientID);
}
StringBuilder sqlb = new StringBuilder("SELECT COUNT(*) FROM ")
.append(targetTableName)
.append(" WHERE ")
.append(query.getWhereClause(true));
String sql = sqlb.toString();
int count = -1;
if (queries.containsKey(sql)) {
count = queries.get(sql);
} else {
int timeout = MSysConfig.getIntValue(MSysConfig.ZOOM_ACROSS_QUERY_TIMEOUT, 5, Env.getAD_Client_ID(Env.getCtx())); // default 5 seconds
count = getSQLValueTimeout(null, sql, timeout);
queries.put(sql, count);
}
query.setRecordCount(count);
return query;
}
/**
* @param object
* @param sql
* @param timeOut
* @return sql value from DB
*/
private int getSQLValueTimeout(Object object, String sql, int timeOut) {
int retValue = -1;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
pstmt = DB.prepareStatement(sql, null);
if (timeOut > 0)
pstmt.setQueryTimeout(timeOut);
rs = pstmt.executeQuery();
if (rs.next())
retValue = rs.getInt(1);
} catch (SQLException e) {
logger.warning(e.getMessage() + " -> " + sql);
} finally {
DB.close(rs, pstmt);
rs = null; pstmt = null;
}
return retValue;
}
}