package com.fr.design.data; import com.fr.base.StoreProcedureParameter; import com.fr.base.TableData; import com.fr.concurrent.NamedThreadFactory; import com.fr.data.MultiResultTableData; import com.fr.data.TableDataSource; import com.fr.data.TableDataSourceTailor; import com.fr.data.core.DataCoreXmlUtils; import com.fr.data.impl.EmbeddedTableData; import com.fr.data.impl.NameDataModel; import com.fr.data.impl.storeproc.ProcedureDataModel; import com.fr.data.impl.storeproc.StoreProcedure; import com.fr.data.impl.storeproc.StoreProcedureConstants; import com.fr.data.impl.storeproc.StoreProcedureHelper; import com.fr.data.operator.DataOperator; import com.fr.design.DesignModelAdapter; import com.fr.design.data.datapane.preview.PreviewTablePane; import com.fr.design.data.tabledata.wrapper.MultiResultTableDataNameWrapper; import com.fr.design.data.tabledata.wrapper.MultiResultTableDataWrapper; import com.fr.design.data.tabledata.wrapper.ServerTableDataWrapper; import com.fr.design.data.tabledata.wrapper.TableDataFactory; import com.fr.design.data.tabledata.wrapper.TableDataWrapper; import com.fr.design.data.tabledata.wrapper.TemplateTableDataWrapper; import com.fr.design.dialog.DialogActionAdapter; import com.fr.design.file.HistoryTemplateListCache; import com.fr.design.gui.iprogressbar.AutoProgressBar; import com.fr.design.mainframe.JTemplate; import com.fr.design.parameter.ParameterInputPane; import com.fr.file.ProcedureConfig; import com.fr.file.TableDataConfig; import com.fr.general.ComparatorUtils; import com.fr.general.data.DataModel; import com.fr.general.data.TableDataException; import com.fr.log.FineLoggerFactory; import com.fr.module.ModuleContext; import com.fr.script.Calculator; import com.fr.stable.ArrayUtils; import com.fr.stable.ParameterProvider; import com.fr.stable.StringUtils; import com.fr.stable.xml.XMLPrintWriter; import javax.swing.JFrame; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.io.ByteArrayOutputStream; import java.text.Collator; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * 设计器管理操作数据集的类: * 1.对于每个TableData,会生成相应的TableDataWrapper.TableDataWrapper里面有TableData的数据列缓存 * 2.TableDataWrapper是不支持对TableData的修改查询语句 重命名等修改删除操作 * 如果TableData变化了,那么TableDataWrapper 也会重新生成,然后存进缓存.这样保证缓存是正确的,不会取到错误的数据列。 * 3.对于模板数据集,关键词应该保证各个模板数据集之间不同,所以默认有个加上模板名的操作。 * 4.个人觉得完全没有必要做成那种一个SQL语句对应一个数据列的情况,那样子太复杂了。而且每次比较关键词都很慢 *

* !!!Notice: 除了预览数据集的操作,其他所有涉及到数据集的界面操作,都要经过这个类(因为它有数据集的缓存,不需要重复计算) * * @author zhou */ public abstract class DesignTableDataManager { /** * 其实globalDsCache没有绝对的必要,只是为了操作方便。如果没有它,那么每次清空服务器数据集或者存储过程的时候,还要去遍历找一下, * 这个操作可能比较复杂 。 从减少代码复杂度的角度看,还是很有必要的 */ private static Map globalDsCache = new java.util.HashMap(); private static Set dsNameChangedSet = new LinkedHashSet<>(); private static List globalDsListeners = new ArrayList<>(); private static Map> dsListenersMap = new ConcurrentHashMap<>(); public static String NO_PARAMETER = "no_paramater_pane"; //用于记录是否要弹出参数框 private static ThreadLocal threadLocal = new ThreadLocal(); private static Map> columnCache = new HashMap<>(); /** * 清除全局 数据集缓存. */ public static void clearGlobalDs() { globalDsCache.clear(); } /** * 响应数据集改变. */ private static void fireDsChanged() { fireDsChanged(globalDsListeners); for (Iterator>> entryIterator = dsListenersMap.entrySet().iterator(); entryIterator.hasNext(); ) { List dsListeners = entryIterator.next().getValue(); fireDsChanged(dsListeners); } } private static void fireDsChanged(List dsListeners) { for (int i = 0; i < dsListeners.size(); i++) { //增强for循环用的iterator实现的, 如果中间哪个listener修改或删除了(如ChartEditPane.dsChangeListener), // 由于dsListeners是arraylist, 此时会ConcurrentModifyException ChangeEvent e = null; dsListeners.get(i).stateChanged(e); } } public static void closeTemplate(JTemplate template) { if (template != null) { template.whenClose(); columnCache.remove(getEditingTableDataSource()); dsListenersMap.remove(template.getPath()); } } public static void envChange() { columnCache.clear(); dsListenersMap.clear(); dsNameChangedSet.clear(); clearGlobalDs(); } /** * 响应数据集改变 * * @param dsNameChangedMap 改变名字的数据集 */ public static void fireDSChanged(Map dsNameChangedMap) { clearGlobalDs(); if (!dsNameChangedMap.isEmpty()) { setDsNameChangedSet(dsNameChangedMap); } fireDsChanged(); dsNameChangedMap.clear(); } private static void setDsNameChangedSet(Map map) { Iterator iterator = map.keySet().iterator(); while (iterator.hasNext()) { String key = (String) iterator.next(); dsNameChangedSet.add(new NameChangeBean(key, map.get(key))); } } /** * 数据库是否改变 * * @param oldDsName 旧名字 * @return 是则返回true */ public static boolean isDsNameChanged(String oldDsName) { for (NameChangeBean bean : dsNameChangedSet) { if (ComparatorUtils.equals(oldDsName, bean.getOldName())) { return true; } } return false; } public static String getChangedDsNameByOldDsName(String dsName) { for (NameChangeBean bean : dsNameChangedSet) { if (ComparatorUtils.equals(dsName, bean.getOldName())) { dsName = bean.getChangedName(); } } return dsName; } public static void addGlobalDsChangeListener(ChangeListener l) { globalDsListeners.add(l); } /** * 添加模板数据集改变 监听事件. * * @param l ChangeListener监听器 */ public static void addDsChangeListener(ChangeListener l) { JTemplate template = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); String key = StringUtils.EMPTY; if (JTemplate.isValid(template)) { key = template.getPath(); } List dsListeners = dsListenersMap.get(key); if (dsListeners == null) { dsListeners = new ArrayList(); dsListenersMap.put(key, dsListeners); } dsListeners.add(l); } /** * 获取数据源source中dsName的所有字段 * * @param source 数据源 * @param dsName 数据集名字 * @return */ public static String[] getSelectedColumnNames(TableDataSource source, String dsName) { java.util.Map resMap = getAllEditingDataSet(source); java.util.Map dsMap = getAllDataSetIncludingProcedure(resMap); TableDataWrapper tabledataWrapper = dsMap.get(dsName); if (tabledataWrapper == null) { return new String[0]; } else { return getSelectedColumnNamesFromCache(source, dsName, tabledataWrapper); } } private static String[] getSelectedColumnNamesFromCache(TableDataSource dataSource, String dsName, TableDataWrapper tableDataWrapper) { Map map = columnCache.get(dataSource); if (map == null) { map = new HashMap<>(); String[] columnNames = tableDataWrapper.calculateColumnNameList().toArray(new String[0]); map.put(dsName, columnNames); columnCache.put(dataSource, map); return columnNames; } else { String[] columnNames = map.get(dsName); if (columnNames == null) { columnNames = tableDataWrapper.calculateColumnNameList().toArray(new String[0]); map.put(dsName, columnNames); return columnNames; } else { return columnNames; } } } public static void removeSelectedColumnNames(String dsName) { Map map = columnCache.get(getEditingTableDataSource()); if (map == null) { return; } map.remove(dsName); } public static void addDsColumnNames(String dsName, String[] columnNames) { TableDataSource dataSource = getEditingTableDataSource(); Map map = columnCache.get(dataSource); if (map == null) { map = new HashMap<>(); map.put(dsName, columnNames); columnCache.put(dataSource, map); } else { map.put(dsName, columnNames); } } public static String[] getDsColumnNames(String dsName) { TableDataSource dataSource = getEditingTableDataSource(); Map map = columnCache.get(dataSource); if (map == null) { return new String[0]; } return map.get(dsName); } /** * august:返回当前正在编辑的具有报表数据源的模板(基本报表、聚合报表) 包括 : 图表模板 * * @return TableDataSource * attention:与这个方法有关系的静态组件(不随着切换模板tab而变化的),应该重新执行该方法,再刷新组件 */ public static TableDataSource getEditingTableDataSource() { return DesignModelAdapter.getCurrentModelAdapter() == null ? null : DesignModelAdapter.getCurrentModelAdapter().getBook(); } /** * 返回当前模板(source)数据集、服务器数据集和存储过程中所有的数据集名字 * * @param source * @return */ public static String[] getAllDSNames(TableDataSource source) { Iterator> entryIt = getAllEditingDataSet(source).entrySet().iterator(); List list = new ArrayList(); while (entryIt.hasNext()) { Entry entry = entryIt.next(); list.add(entry.getKey()); } return list.toArray(new String[0]); } /** * 获取所有的数据集名称,无论模板是不是有数据集的权限 */ public static Set getAllDSNamesWithoutPermissions(TableDataSource source) { Set names = new HashSet<>(); Map resMap = new HashMap<>(); // 模板数据集 addTemplateData(resMap, source); // 存储过程 addStoreProcedureData(resMap); for (Map.Entry entry : resMap.entrySet()) { names.add(entry.getKey()); } //服务器数据集 Map tableDatas = TableDataConfig.getInstance().getTableDatas(); for (Map.Entry entry : tableDatas.entrySet()) { names.add(entry.getKey()); } return names; } /** * 不根据过滤设置,返回当前模板数据集、服务器数据集、存储过程本身,是有顺序的 */ public static java.util.Map getAllEditingDataSet(TableDataSource source) { java.util.Map resMap = new java.util.LinkedHashMap(); // 模板数据集 addTemplateData(resMap, source); // 服务器数据集 addServerData(resMap); // 存储过程 addStoreProcedureData(resMap); return resMap; } /** * 不根据过滤设置,返回当前模板数据集,是有顺序的 */ public static java.util.Map getTemplateDataSet(TableDataSource source) { java.util.Map resMap = new java.util.LinkedHashMap(); // 模板数据集 addTemplateData(resMap, source); return resMap; } public static java.util.Map getAllDataSetIncludingProcedure(java.util.Map resMap) { java.util.LinkedHashMap dsMap = new java.util.LinkedHashMap(); for (Entry entry : resMap.entrySet()) { String key = entry.getKey(); TableDataWrapper tableDataWrapper = resMap.get(key); if (tableDataWrapper.getTableData() instanceof MultiResultTableData) { MultiResultTableData tableData = (MultiResultTableData) tableDataWrapper.getTableData(); String name = tableDataWrapper.getTableDataName(); List resultNames = tableData.getResultNames(); TableDataWrapper tdw = new MultiResultTableDataNameWrapper(name + "_Table", tableData); boolean hasSchemaOrResult = false; // 存储过程的特殊处理,还有其它名称 if (tableData instanceof StoreProcedure) { StoreProcedure storeProcedure = (StoreProcedure) tableData; StoreProcedureParameter[] parameters = StoreProcedureHelper.getSortPara(storeProcedure.getParameters()); for (StoreProcedureParameter parameter : parameters) { if (parameter.getSchema() != StoreProcedureConstants.IN) { String parameterName = name + "_" + parameter.getName(); TableDataWrapper newTwd = new MultiResultTableDataWrapper(storeProcedure, name, parameterName, false); dsMap.put(parameterName, newTwd); hasSchemaOrResult = true; } } } /*else { // TODO getDataModelList是空的 for (String n : tableData.getResultNames()) { String dmName = name + "_" + n; dsMap.put(n, new MultiResultTableDataWrapper(tableData, name, dmName, false)); } }*/ if (!resultNames.isEmpty()) { hasSchemaOrResult = true; for (String resultName : resultNames) { String dmName = name + "_" + resultName; TableDataWrapper newTwd = new MultiResultTableDataWrapper(tableData, name, dmName, false); dsMap.put(dmName, newTwd); } } if (!hasSchemaOrResult) { dsMap.put(name + "_Table", tdw); } } else { dsMap.put(key, tableDataWrapper); } } return dsMap; } /** * 不根据过滤设置,返回当前服务器数据集、存储过程所有的数据集,是有顺序的 */ public static java.util.Map getGlobalDataSet() { java.util.Map resMap = new java.util.LinkedHashMap(); // 服务器数据集 addServerData(resMap); // 存储过程 addStoreProcedureData(resMap); return resMap; } /** * 根据过滤设置,返回当前模板数据集、服务器数据集、存储过程所有的数据集,是有顺序的 */ public static List> getEditingDataSet(TableDataSource source) { Map templateDataMap = new LinkedHashMap(); Map serverDataMap = new LinkedHashMap(); Map storeProcedureMap = new LinkedHashMap(); addTemplateData(templateDataMap, source); addServerData(serverDataMap); addStoreProcedureData(storeProcedureMap); List> list = new ArrayList>(); list.add(templateDataMap); list.add(serverDataMap); list.add(storeProcedureMap); return list; } private static void addTemplateData(java.util.Map resMap, TableDataSource source) { if (source != null) { String[] namearray = TableDataFactory.getSortOfChineseNameOfTemplateData(source); for (String tabledataname : namearray) { TableData td = source.getTableData(tabledataname); TableDataWrapper tdw = new TemplateTableDataWrapper(td, tabledataname); resMap.put(tabledataname, tdw); } } } private static void addServerData(java.util.Map resMap) { TableDataConfig tableDataConfig = TableDataConfig.getInstance(); String[] namearray = TableDataFactory.getSortOfChineseNameOfServerData(tableDataConfig); for (String name : namearray) { if (globalDsCache.containsKey(name)) { resMap.put(name, globalDsCache.get(name)); } else { TableDataWrapper tdw = new ServerTableDataWrapper(tableDataConfig.getTableData(name), name); resMap.put(name, tdw); globalDsCache.put(name, tdw); } } } private static void addStoreProcedureData(java.util.Map resMap) { ProcedureConfig procedureConfig = ProcedureConfig.getInstance(); List names = new ArrayList<>(procedureConfig.getProcedures().keySet()); Collections.sort(names, Collator.getInstance(java.util.Locale.CHINA)); for (String name : names) { StoreProcedure storeProcedure = procedureConfig.getProcedure(name); if (globalDsCache.containsKey(name)) { resMap.put(name, globalDsCache.get(name)); } else { TableDataWrapper tdw = new MultiResultTableDataNameWrapper(name, storeProcedure); resMap.put(name, tdw); globalDsCache.put(name, tdw); } } } /** * 预览需要参数的数据集 * * @param tabledata 数据集 * @param rowCount 需要预览的行数 * @param needLoadingBar 是否需要加载进度条 * @return 数据集 * @throws Exception 异常 */ public static EmbeddedTableData previewTableDataNeedInputParameters(TableData tabledata, int rowCount, boolean needLoadingBar) throws Exception { return previewTableData(null, tabledata, rowCount, true, needLoadingBar); } /** * 预览需要参数的数据集 * * @param tabledata 数据集 * @param rowCount 需要预览的行数 * @param needLoadingBar 是否需要加载进度条 * @return 数据集 * @throws Exception 异常 */ public static EmbeddedTableData previewTableDataNeedInputParameters(TableDataSource tableDataSource, TableData tabledata, int rowCount, boolean needLoadingBar) throws Exception { return previewTableDataNeedInputParameters(tableDataSource, tabledata, rowCount, needLoadingBar, null); } public static EmbeddedTableData previewTableDataNeedInputParameters(TableDataSource tableDataSource, TableData tabledata, int rowCount, boolean needLoadingBar, AutoProgressBar progressBar) throws Exception { return previewTableData(tableDataSource, tabledata, rowCount, true, needLoadingBar, progressBar); } /** * 预览不需要参数的数据集 * * @param tabledata 数据集 * @param rowCount 需要预览的行数 * @param needLoadingBar 是否需要加载进度条 * @return 数据集 * @throws Exception 异常 */ public static EmbeddedTableData previewTableDataNotNeedInputParameters(TableData tabledata, int rowCount, boolean needLoadingBar) throws Exception { return previewTableData(null, tabledata, rowCount, false, needLoadingBar); } /** * 预览不需要参数的数据集 * * @param tabledata 数据集 * @param rowCount 需要预览的行数 * @param needLoadingBar 是否需要加载进度条 * @return 数据集 * @throws Exception 异常 */ public static EmbeddedTableData previewTableDataNotNeedInputParameters(TableDataSource tableDataSource, TableData tabledata, int rowCount, boolean needLoadingBar) throws Exception { return previewTableData(tableDataSource, tabledata, rowCount, false, needLoadingBar); } /** * 获取预览后的EmbeddedTableData,考虑到Env * * @param tabledata * @param rowCount * @param isMustInputParameters 是否必须输入参数值(不管参数有没有默认值)。一般预览时这个值为true,因为一般预览是要看不同的参数的结果的。 * 而获取数据集的字段名字时,则没必要 * @return */ private static EmbeddedTableData previewTableData(TableDataSource tableDataSource, TableData tabledata, int rowCount, boolean isMustInputParameters, boolean needLoadingBar) throws Exception { return previewTableData(tableDataSource, tabledata, rowCount, isMustInputParameters, needLoadingBar, null); } private static EmbeddedTableData previewTableData(TableDataSource tableDataSource, TableData tabledata, int rowCount, boolean isMustInputParameters, boolean needLoadingBar, AutoProgressBar progressBar) throws Exception { final AutoProgressBar loadingBar; if (progressBar == null) { loadingBar = PreviewTablePane.getInstance().getProgressBar(); } else { loadingBar = progressBar; } ParameterProvider[] parameters = DataOperator.getInstance().getTableDataParameters(tabledata); if (ArrayUtils.isEmpty(parameters)) { parameters = tabledata.getParameters(Calculator.createCalculator()); } Map parameterMap = new HashMap<>(); if (needInputParams(isMustInputParameters, parameters)) { showParaWindow(parameterMap, parameters); } else { for (ParameterProvider parameter : parameters) { parameterMap.put(parameter.getName(), parameter.getValue()); } } if (loadingBar != null && needLoadingBar) { loadingBar.start(); } try { for (ParameterProvider parameter : DataOperator.getInstance().getTableDataParameters(tabledata)) { if (parameterMap.containsKey(parameter.getName())) { parameter.setValue(parameterMap.get(parameter.getName())); } } return DataOperator.getInstance().previewTableData(TableDataSourceTailor.extractTableData(tableDataSource), tabledata, parameterMap, rowCount); } catch (Exception e) { throw new TableDataException(e.getMessage(), e); } finally { ScheduledExecutorService scheduledExecutorService = ModuleContext .getExecutor() .newSingleThreadScheduledExecutor(new NamedThreadFactory("")); scheduledExecutorService.schedule(new Runnable() { @Override public void run() { if (loadingBar != null) { loadingBar.close(); } } }, 100, TimeUnit.MILLISECONDS); scheduledExecutorService.shutdown(); } } private static boolean needInputParams(boolean mustInputParameters, ParameterProvider[] parameters) { if (mustInputParameters && ArrayUtils.isNotEmpty(parameters)) { return true; } for (ParameterProvider parameter : parameters) { if (parameter.getValue() == null || StringUtils.EMPTY.equals(parameter.getValue())) { return true; } } return false; } /** * 返回TableData的数据列,注意TableData * 是没有考虑参数的。用于简单的查询语句生成的TableData, 或者 * EmbeddedTableData. 比如说:数据字典-数据库表生成的TableData。 * 使用该方法是没有数据集缓存的功能的 * * @return List */ public static List getColumnNamesByTableData(TableData tableData) { List columnNames = new ArrayList(); if (tableData != null) { DataModel rs = tableData.createDataModel(Calculator.createCalculator()); int value; try { value = rs.getColumnCount(); for (int i = 0; i < value; i++) { columnNames.add(rs.getColumnName(i)); } rs.release(); } catch (Exception e) { FineLoggerFactory.getLogger().error(e.getMessage(), e); } } return columnNames; } /** * 该方法主要利用了StoreProcedure里面的“dataModelList有缓存作用”的机制 * 所以用该方法,不会对一个已经计算了的存储过程重复计算.和分页预览时处理机制一样,这样对有多个返回数据集的存储过程来说很有必要 * * @param needLoadingBar 是否需要进度条 * @param tableData 存储过程 * @return 数据 */ public static NameDataModel[] createLazyDataModel(MultiResultTableData tableData, boolean needLoadingBar) throws Exception { Map parameterMap = new HashMap<>(); if (tableData instanceof StoreProcedure) { StoreProcedure storeProcedure = (StoreProcedure) tableData; ByteArrayOutputStream out = new ByteArrayOutputStream(); XMLPrintWriter writer = XMLPrintWriter.create(out); // 把storeProcedure写成xml文件到out DataCoreXmlUtils.writeXMLStoreProcedure(writer, storeProcedure, null); if (storeProcedure.getDataModelList().size() > 0 && !storeProcedure.isFirstExpand()) { // 存储过程有些特殊处理 // 这个就简单直接获取暂存列表吧 return storeProcedure.getDataModelList().toArray(new ProcedureDataModel[0]); } ParameterProvider[] inParameters = DataOperator.getInstance().getStoreProcedureParameters(storeProcedure); if (inParameters.length > 0 && !ComparatorUtils.equals(threadLocal.get(), NO_PARAMETER)) {// 检查Parameter. showParaWindow(parameterMap, inParameters); } storeProcedure.setFirstExpand(false); } else { ParameterProvider[] parameters = DataOperator.getInstance().getTableDataParameters(tableData); if (parameters.length > 0) { showParaWindow(parameterMap, parameters); } } if (needLoadingBar) { MultiResultTableDataWrapper.loadingBar.start(); } return DataOperator.getInstance().previewMultiResultTableData(tableData, parameterMap, 0); } private static void showParaWindow(final Map parameterMap, ParameterProvider[] inParameters) { final ParameterInputPane pPane = new ParameterInputPane(inParameters); pPane.showSmallWindow(new JFrame(), new DialogActionAdapter() { @Override public void doOk() { parameterMap.putAll(pPane.update()); } }).setVisible(true); } public static void setThreadLocal(String value) { threadLocal.set(value); } /** * 根据数据集名称判断是否为服务器数据集或服务器存储过程 * * @param tableDataName 数据集名称 * @return */ public static boolean isGlobalTableData(String tableDataName) { return globalDsCache.containsKey(tableDataName); } }