From: <Se...@us...> - 2010-09-30 22:35:59
|
Revision: 4777 http://jaffa.svn.sourceforge.net/jaffa/?rev=4777&view=rev Author: SeanZ Date: 2010-09-30 22:35:53 +0000 (Thu, 30 Sep 2010) Log Message: ----------- Extend export to excel to an object class aggregating on single child object class. Modified Paths: -------------- trunk/JaffaRIA/source/html/js/extjs/jaffa/finder/exportToExcel.jsp trunk/JaffaRIA/source/html/js/extjs/ux/plugins/ExportToExcelPlugin.js Modified: trunk/JaffaRIA/source/html/js/extjs/jaffa/finder/exportToExcel.jsp =================================================================== --- trunk/JaffaRIA/source/html/js/extjs/jaffa/finder/exportToExcel.jsp 2010-09-30 00:42:56 UTC (rev 4776) +++ trunk/JaffaRIA/source/html/js/extjs/jaffa/finder/exportToExcel.jsp 2010-09-30 22:35:53 UTC (rev 4777) @@ -6,12 +6,23 @@ - criteriaObject: criteria as a JSON structure - serviceClassName: the name of the service class that should contain the method 'public AGraphDataObject[] query(AGraphCriteria criteria)' - columnModel: JSON structure containing an array of column elements, each element having 'header' and 'mapping' for providing the column-title and data respectively + - masterKeyFieldNames: JSON structure of an array of key field names in the master object + - detailCriteriaClassName: the name of the criteria class + - detailCriteria: criteria as a JSON structure (should contain {#} for the key field values to be replaced) + - detailServiceClassName: the name of the service class that should contain the method 'public AGraphDataObject[] query(AGraphCriteria criteria)' + - detailColumnModel: JSON structure containing an array of column elements, each element having 'header' and 'mapping' for providing the column-title and data respectively + +Note: The implementation is generic that applies to a master object class aggregating a child object class. + +@author Sean Zhou, Sep, 2010. extends to handle aggregating with single child object. + --%> <%@ page import = "java.lang.reflect.InvocationTargetException" %> <%@ page import = "java.lang.reflect.Method" %> <%@ page import = "java.lang.reflect.Modifier" %> <%@ page import = "java.util.Arrays" %> +<%@ page import = "java.util.LinkedList" %> <%@ page import = "net.sf.ezmorph.Morpher" %> <%@ page import = "net.sf.ezmorph.ObjectMorpher" %> <%@ page import = "net.sf.ezmorph.object.IdentityObjectMorpher" %> @@ -31,6 +42,98 @@ <%! private static final Logger log = Logger.getLogger("js.extjs.jaffa.exportToExcel"); +/** + * A class to encapsulate the query service configuration information. + * @author SeanZ + */ +public class QueryServiceConfig { + private String serviceClassName; + private String criteriaClassName; + private String criteriaObject; + private String[] masterKeyFieldNames; + private Object[] columnModel; + + /** + * @return the serviceClassName + */ + public String getServiceClassName() { + return serviceClassName; + } + + /** + * @param serviceClassName the serviceClassName to set + */ + public void setServiceClassName(String serviceClassName) { + this.serviceClassName = serviceClassName; + } + + /** + * @return the criteriaClassName + */ + public String getCriteriaClassName() { + return criteriaClassName; + } + + /** + * @param criteriaClassName the criteriaClassName to set + */ + public void setCriteriaClassName(String criteriaClassName) { + this.criteriaClassName = criteriaClassName; + } + + /** + * @return the criteriaObject + */ + public String getCriteriaObject() { + return criteriaObject; + } + + /** + * @param criteriaObject the criteriaObject to set + */ + public void setCriteriaObject(String criteriaObject) { + this.criteriaObject = criteriaObject; + } + + /** + * @return the masterKeyFieldNames + */ + public String[] getMasterKeyFieldNames() { + return masterKeyFieldNames; + } + + /** + * @param masterKeyFieldNames the masterKeyFieldNames to set + */ + public void setMasterKeyFieldNames(Object[] masterKeyFieldNames) { + this.masterKeyFieldNames = new String[masterKeyFieldNames.length]; + for (int i=0; i<masterKeyFieldNames.length; i++) + this.masterKeyFieldNames[i] = (String) masterKeyFieldNames[i]; + } + + /** + * @return the columnModel + */ + public Object[] getColumnModel() { + return columnModel; + } + + /** + * @param columnModel the columnModel to set + */ + public void setColumnModel(String columnModel) { + this.setColumnModel(columnModel); + } + + /** + * @param columnModel the columnModel to set + */ + public void setColumnModel(Object[] columnModel) { + this.columnModel = columnModel; + } + +} + /** Converts the input JSON structure into an instance of the beanClassName. * If the beanClassName is null, then an instance of org.apache.commons.beanutils.DynaBean will be returned. * @param input input as a JSON structure. @@ -170,32 +273,38 @@ * @throws IllegalArgumentException if the input is invalid. * @throws InvocationTargetException if an error occurs during invocation of the query method. */ -private static String generateExcel(String criteriaClassName, String criteriaObject, String serviceClassName, String columnModel) +private static String generateExcel(QueryServiceConfig master, QueryServiceConfig child) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { - // Invoke the query and obtain an array of Graph objects - Object[] queryOutput = invokeQueryService(criteriaClassName, criteriaObject, serviceClassName); - - // Convert the column model into an array of DynaBean instances; each instance having 'header' and 'mapping' values - Object[] columnModelElements = jsonArrayToBeanArray(columnModel, null); - // Generate the Excel output by creating a simple HTML table StringBuilder buf = new StringBuilder("<table border='1'>\n"); // Add a row with column titles buf.append(" <tr>\n"); - for (Object o : columnModelElements) { + for (Object o : master.getColumnModel()) { String columnTitle = (String) ((DynaBean) o).get("header"); if (columnTitle == null || columnTitle.length() == 0) columnTitle = (String) ((DynaBean) o).get("mapping"); buf.append(" <th style='background-color: Silver;border : groove;'>").append(columnTitle).append("</th>\n"); } + if (child != null) { + for (Object o : child.getColumnModel()) { + String columnTitle = (String) ((DynaBean) o).get("header"); + if (columnTitle == null || columnTitle.length() == 0) + columnTitle = (String) ((DynaBean) o).get("mapping"); + buf.append(" <th style='background-color: Silver;border : groove;'>").append(columnTitle).append("</th>\n"); + } + } buf.append(" </tr>\n"); + // Invoke the query and obtain an array of Graph objects + Object[] queryOutput = invokeQueryService(master.getCriteriaClassName(), master.getCriteriaObject(), master.getServiceClassName()); + // Add the data rows if (queryOutput != null) { for (Object row : queryOutput) { - buf.append(" <tr>\n"); - for (Object o : columnModelElements) { + // extract the columns from master object + StringBuilder buf1 = new StringBuilder(); + for (Object o : master.getColumnModel()) { String mapping = (String) ((DynaBean) o).get("mapping"); Object value = null; try { @@ -204,10 +313,52 @@ if (log.isDebugEnabled()) log.debug("Property not found: " + mapping, e); } + buf1.append(" <td").append(value != null && value instanceof String ? " x:str" : "").append('>').append(value != null ? value : "").append("</td>\n"); + } + String masterRowString = buf1.toString(); + + Object[] detailQueryOutput = new Object[0]; + + if (child != null) { + // load the child rows + String detailCriteriaObject = child.getCriteriaObject(); + for (int i=0; i<child.getMasterKeyFieldNames().length; i++) { + String kfn = child.getMasterKeyFieldNames()[i]; + try { + String keyValue = (String) PropertyUtils.getProperty(row, kfn); + detailCriteriaObject = detailCriteriaObject.replace("{"+i+"}", keyValue); + } catch (Exception e) { + if (log.isDebugEnabled()) + log.debug("Key property not found: " + kfn, e); + } + } + detailQueryOutput = invokeQueryService(child.getCriteriaClassName(), detailCriteriaObject, child.getServiceClassName()); + } + + // add the child columns + if(detailQueryOutput != null && detailQueryOutput.length > 0) { + for (Object detailRow : detailQueryOutput) { + buf.append(" <tr>\n"); + buf.append(masterRowString); + for (Object o : child.getColumnModel()) { + String mapping = (String) ((DynaBean) o).get("mapping"); + Object value = null; + try { + value = PropertyUtils.getProperty(detailRow, mapping); + } catch (Exception e) { + if (log.isDebugEnabled()) + log.debug("Property not found in child result: " + mapping, e); + } buf.append(" <td").append(value != null && value instanceof String ? " x:str" : "").append('>').append(value != null ? value : "").append("</td>\n"); } buf.append(" </tr>\n"); } + } else { + buf.append(" <tr>\n"); + buf.append(masterRowString); + buf.append(" </tr>\n"); + } + } } buf.append("</table>\n"); @@ -221,17 +372,38 @@ <% -String criteriaClassName = request.getParameter("criteriaClassName"); -String criteriaObject = request.getParameter("criteriaObject"); -String serviceClassName = request.getParameter("serviceClassName"); -String columnModel = request.getParameter("columnModel"); + +QueryServiceConfig master = new QueryServiceConfig(); +QueryServiceConfig child = null; + +master.setCriteriaClassName(request.getParameter("criteriaClassName")); +master.setCriteriaObject(request.getParameter("criteriaObject")); +master.setServiceClassName(request.getParameter("serviceClassName")); +master.setColumnModel(jsonArrayToBeanArray(request.getParameter("columnModel"), null)); + +String mkfns = request.getParameter("masterKeyFieldNames"); +if (mkfns != null) { + child = new QueryServiceConfig(); + child.setMasterKeyFieldNames(jsonArrayToBeanArray(mkfns, null)); + child.setCriteriaClassName(request.getParameter("detailCriteriaClassName")); + child.setCriteriaObject(request.getParameter("detailCriteria")); + child.setServiceClassName(request.getParameter("detailServiceClassName")); + child.setColumnModel(jsonArrayToBeanArray(request.getParameter("detailColumnModel"), null)); +} if(log.isDebugEnabled()) { - log.debug("criteriaClassName = " + criteriaClassName); - log.debug("serviceClassName = " + serviceClassName); - log.debug("criteriaObject = " + criteriaObject); - log.debug("columnModel = " + columnModel); + log.debug("criteriaClassName = " + master.getCriteriaClassName()); + log.debug("serviceClassName = " + master.getServiceClassName()); + log.debug("criteriaObject = " + master.getCriteriaObject()); + log.debug("columnModel = " + master.getColumnModel().toString()); + if (child != null) { + log.debug("masterKeyFieldNames = " + child.getMasterKeyFieldNames().toString()); + log.debug("detailCriteriaClassName = " + child.getCriteriaClassName()); + log.debug("detailServiceClassName = " + child.getServiceClassName()); + log.debug("detailCriteriaObject = " + child.getCriteriaObject()); + log.debug("detailColumnModel = " + child.getColumnModel().toString()); + } } -String excelTable = generateExcel(criteriaClassName, criteriaObject, serviceClassName, columnModel); +String excelTable = generateExcel(master, child); response.setContentType("application/vnd.ms-excel"); response.setHeader("Content-Disposition", "attachment; filename=\"exportToExcel.xls\""); %> Modified: trunk/JaffaRIA/source/html/js/extjs/ux/plugins/ExportToExcelPlugin.js =================================================================== --- trunk/JaffaRIA/source/html/js/extjs/ux/plugins/ExportToExcelPlugin.js 2010-09-30 00:42:56 UTC (rev 4776) +++ trunk/JaffaRIA/source/html/js/extjs/ux/plugins/ExportToExcelPlugin.js 2010-09-30 22:35:53 UTC (rev 4777) @@ -1,6 +1,6 @@ /** * @class Ext.ux.plugins.ExportToExcelPlugin - * @author BobbyC + * @author BobbyC, SeanZ * @param {Object} config Options object * @constructor Example @@ -8,8 +8,17 @@ new Ext.grid.GridPanel({ ... plugins: new Ext.ux.plugins.ExportToExcelPlugin({ - serviceClassName:'com.mirotechnologies.workrecording.workorder.apis.WorkOrderService', - criteriaClassName:'com.mirotechnologies.workrecording.workorder.apis.data.WorkOrderCriteria' + serviceClassName:'com.mirotechnologies.material.transaction.apis.PickListService' + ,criteriaClassName:'com.mirotechnologies.material.transaction.apis.data.PickListCriteria' + ,detailServiceClassName:'com.mirotechnologies.material.transaction.apis.PickListRequestService' + ,detailCriteriaClassName:'com.mirotechnologies.material.transaction.apis.data.PickListRequestCriteria' + ,masterKeyFieldNames: '(key field name)' or [(key field names)] + // need to place some dummy key values in child grid store.baseParams to tell wehter the key fields + // in String or StringCriteria field format in the child criteria object. + // need one of the following three thins + // 1) need to add child grid as childGrid to the results panel before the button is clicked. + // 2) ,getChildGrid: function() {} + // 3) ,childColumnModel: [] } ... }); @@ -26,22 +35,24 @@ var plg = { init: function(grid) { var excelButton = { + xtype: 'button', text: "Export To Excel"//Labels.get("label.Jaffa.Finder.Button.Excel"), ,iconCls: "excel" ,scope:grid ,handler: function(item) { if(this.disabled) return; - var cm = []; + var cm = [], chldcm = config.childColumnModel || []; + // constructing the object class to export var cfg = this.getColumnConfig(); for(var i=0;i<cfg.length;i++) { var col = cfg[i]; var meta = this.store.reader.recordType.getField(col.dataIndex); //Add column to export list if not hidden or in the group by list if(meta && (col.hidden!=true || (this.store.groupField&&this.store.groupField.indexOf(col.dataIndex)>=0))) - cm[cm.length] = {header:col.header,mapping:meta.mapping||meta.name}; + cm.push({header:col.header,mapping:meta.mapping||meta.name}); } var criteria = Ext.apply({}, this.store.baseParams); - criteria.objectLimit=5000; // Limit to max of 5000 rows, prevents large query from breaking the system. override only if required + criteria.objectLimit=5000; // Limit to max of 5000 rows var form = Ext.DomHelper.append(document.body, { tag:'form', target:'_blank', @@ -54,6 +65,53 @@ fld.value = Ext.util.JSON.encode(criteria); fld = Ext.DomHelper.append(form, {tag:'input', type:'hidden', name:'columnModel'}); fld.value = Ext.util.JSON.encode(cm); + + // constructing the children object class to export + if (!grid.childGrid && Ext.isFunction(grid.getChildGrid)) grid.childGrid = grid.getChildGrid(); + if (chldcm.length==0 && grid.childGrid) { + if (Ext.isFunction(this.getChildColumnConfig)) { + cfg = this.getChildColumnConfig(); + } else { + cfg = grid.childGrid.getColumnModel().config; + } + for(var i=0;i<cfg.length;i++) { + var col = cfg[i]; + var meta = grid.childGrid.store.reader.recordType.getField(col.dataIndex); + if(meta && (col.hidden!=true || (grid.childGrid.store.groupField&&grid.childGrid.store.groupField.indexOf(col.dataIndex)>=0))) + chldcm.push({header:col.header,mapping:meta.mapping||meta.name}); + } + } + if (Ext.isArray(chldcm) && chldcm.length > 0) { + var detailCriteria = Ext.apply({}, grid.childGrid.getStore().baseParams); + detailCriteria.objectLimit = 500; + delete detailCriteria.groupBy; + // handling master key fields + if (Ext.isString(config.masterKeyFieldNames)) config.masterKeyFieldNames = [config.masterKeyFieldNames]; + for (var i=0; i<config.masterKeyFieldNames.length; i++) { + if (Ext.isString(detailCriteria[config.masterKeyFieldNames[i]])) { + detailCriteria[config.masterKeyFieldNames[i]] = '{'+i+'}'; + } else if (Ext.isArray(detailCriteria[config.masterKeyFieldNames[i]])){ + detailCriteria[config.masterKeyFieldNames[i]] = {values:['{'+i+'}']}; + } else { + Ext.MessageBox.alert('Error:', 'Key field: '+config.masterKeyFieldNames[i]+' needs to be defined in baseParams of the child store.'); + return; + } + } + + Ext.DomHelper.append(form, {tag:'input', type:'hidden', name:'detailServiceClassName', value:config.detailServiceClassName}); + Ext.DomHelper.append(form, {tag:'input', type:'hidden', name:'detailCriteriaClassName', value:config.detailCriteriaClassName}); + fld = Ext.DomHelper.append(form, {tag:'input', type:'hidden', name:'detailCriteria'}); + fld.value = Ext.util.JSON.encode(detailCriteria); + fld = Ext.DomHelper.append(form, { + tag: 'input', + type: 'hidden', + name: 'masterKeyFieldNames' + }); + fld.value = Ext.util.JSON.encode(config.masterKeyFieldNames); + fld = Ext.DomHelper.append(form, {tag:'input', type:'hidden', name:'detailColumnModel'}); + fld.value = Ext.util.JSON.encode(chldcm); + } + form.submit(); setTimeout(function(){Ext.removeNode(form);}, 100); } @@ -71,6 +129,8 @@ if (!grid.getColumnConfig) { grid.getColumnConfig = this.getColumnConfig; } + if (config.childGrid) grid.childGrid = config.childGrid; + if (Ext.isFunction(config.getChildGrid)) grid.getChildGrid = config.getChildGrid; } ,getColumnConfig: function() { return this.getColumnModel().config; @@ -78,4 +138,6 @@ }; if (Ext.isFunction(config.getColumnConfig)) plg.getColumnConfig = config.getColumnConfig; return plg; -}; \ No newline at end of file +}; + +Ext.preg('exporttoexcel', Ext.ux.plugins.ExportToExcelPlugin); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |