Browse Source

Merge pull request #8563 in DESIGN/design from final/11.0 to persist/11.0

* commit 'a88acb64898b2310575a0d0f75e57860f98a7bff': (103 commits)
  REPORT-69776 切换在线组件的排序,会加载很久
  REPORT-69575 设计器在线组件刷不出来
  CHART-22994 图表背景色控件需要更换为支持透明色的控件
  REPORT-68726 【迭代】【数据集管理优化】多张模板切换触发搜索,设计器概率卡住 【问题原因】之前没修改好,可能出现搜索框文字不清空的情况 【改动思路】修改为每次离开搜索模式时,情况搜索框 【review建议】无
  无jira任务,屏蔽部分功能入口
  无jira任务,屏蔽部分功能入口
  REPORT-69422 产品调整部分UI && 删除数据集没反应 【问题原因】1.搜索匹配文字高亮调整为匹配文字加粗;2.UIList获取选中对象的方法返回为空 【改动思路】1.改UI;2.自己写一下获取选中对象的逻辑 【review建议】无
  REPORT-68987 把高级编辑当作可选的
  REPORT-69422 产品调整部分UI 【问题原因】有些小细节还需要调整下 【改动思路】调整 【review建议】无
  Update designer-base/src/main/java/com/fr/design/record/analyzer/advice/MonitorAdvice.java
  无jira任务,屏蔽部分功能入口
  REPORT-69324 插件-新插件管理-设计器内插件管理版本号显示异常
  REPORT-69422 && REPORT-69426 产品调整部分UI && 复制单元格会影响数据集的展开/收起状态 【问题原因】1.产品验收功能的时候改了一些UI;2.因为粘贴单元格的时候会获取当前数据集面板,getInstance方法里会refresh一次整个数据集树,然后将所有节点重置到收起状态 【改动思路】1.调整UI;2.提供一个获取数据集树面板单例不刷新自己的方法,粘贴时使用 【review建议】无
  BI-103979 【6.0回归】fr连接bi闪退
  REPORT-69213 函数在公式编辑器内不标蓝显示
  REPORT-69213 函数在公式编辑器内不标蓝显示
  REPORT-68212 表头排序功能-国际化-交互问题 && REPORT-68931 表头排序图标配置颜色后大小发生变化
  REPORT-67790;修改开启新引擎的逻辑(原来的以第一个sheet设置为准,改为只要有一个sheet设置了新引擎,就走新引擎逻辑)
  REPORT-69289 【设计器】大屏编辑器-繁体设计器-远程服务器,打开本地设计器工作目录下的模板,提示我备份
  REPORT-68455 && REPORT-69108 【问题原因】 1.工具栏面板与其他面板(设计器菜单栏-模板或远程设计-切换工作目录)重叠时会显示异常是因为之前的这个工具栏面板手误写成了继承Panel 2.搜索结果显示不正确是因为之前改卡死问题没改好,把搜索计数放到了处理UI的外面,会优先于数据集树的刷新而变化,导致搜索计数完成了,但是结果没更新的问题 【改动思路】1.改成继承JPanel;2.搜索计数放到处理UI中,确定处理UI完成才算是一次计数完成;3.添加部分debug日志,方便以后排查问题 【review建议】无
  ...
fix-lag
superman 2 years ago
parent
commit
ddeda7db01
  1. 8
      build.gradle
  2. 13
      designer-base/src/main/java/com/fr/design/DesignModelAdapter.java
  3. 35
      designer-base/src/main/java/com/fr/design/DesignerEnvManager.java
  4. 4
      designer-base/src/main/java/com/fr/design/RestartHelper.java
  5. 4
      designer-base/src/main/java/com/fr/design/data/BasicTableDataTreePane.java
  6. 11
      designer-base/src/main/java/com/fr/design/data/DesignTableDataManager.java
  7. 53
      designer-base/src/main/java/com/fr/design/data/MapCompareUtils.java
  8. 243
      designer-base/src/main/java/com/fr/design/data/datapane/TableDataTree.java
  9. 397
      designer-base/src/main/java/com/fr/design/data/datapane/TableDataTreePane.java
  10. 75
      designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionListPane.java
  11. 12
      designer-base/src/main/java/com/fr/design/data/datapane/connect/SshPane.java
  12. 5
      designer-base/src/main/java/com/fr/design/data/datapane/connect/SslPane.java
  13. 68
      designer-base/src/main/java/com/fr/design/data/datapane/management/clip/TableDataTreeClipboard.java
  14. 242
      designer-base/src/main/java/com/fr/design/data/datapane/management/search/TableDataTreeSearchManager.java
  15. 12
      designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/TreeSearchCallback.java
  16. 37
      designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/TreeSearchResult.java
  17. 10
      designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/TreeSearchTask.java
  18. 54
      designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/common/TableDataSearchCallBack.java
  19. 105
      designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/common/TableDataSearchResult.java
  20. 147
      designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/common/TableDataSearchTask.java
  21. 23
      designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/pre/TableDataPreSearchCallBack.java
  22. 40
      designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/pre/TableDataPreSearchResult.java
  23. 45
      designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/pre/TableDataPreSearchTask.java
  24. 22
      designer-base/src/main/java/com/fr/design/data/datapane/management/search/event/TreeSearchStatusChangeEvent.java
  25. 11
      designer-base/src/main/java/com/fr/design/data/datapane/management/search/event/TreeSearchStatusChangeListener.java
  26. 215
      designer-base/src/main/java/com/fr/design/data/datapane/management/search/pane/TableDataSearchRemindPane.java
  27. 208
      designer-base/src/main/java/com/fr/design/data/datapane/management/search/pane/TreeSearchToolbarPane.java
  28. 38
      designer-base/src/main/java/com/fr/design/data/datapane/management/search/searcher/TableDataSearchMode.java
  29. 148
      designer-base/src/main/java/com/fr/design/data/datapane/management/search/searcher/TableDataTreeSearcher.java
  30. 28
      designer-base/src/main/java/com/fr/design/data/datapane/management/search/searcher/TreeSearchStatus.java
  31. 27
      designer-base/src/main/java/com/fr/design/data/datapane/management/search/searcher/TreeSearcher.java
  32. 82
      designer-base/src/main/java/com/fr/design/data/datapane/management/search/view/TreeSearchRendererHelper.java
  33. 340
      designer-base/src/main/java/com/fr/design/data/tabledata/paste/TableDataFollowingPasteUtils.java
  34. 109
      designer-base/src/main/java/com/fr/design/data/tabledata/wrapper/StoreProcedureDataWrapper.java
  35. 4
      designer-base/src/main/java/com/fr/design/env/LocalDesignerWorkspaceInfo.java
  36. 21
      designer-base/src/main/java/com/fr/design/extra/WebViewDlgHelper.java
  37. 24
      designer-base/src/main/java/com/fr/design/file/HistoryTemplateListCache.java
  38. 22
      designer-base/src/main/java/com/fr/design/formula/FormulaPane.java
  39. 34
      designer-base/src/main/java/com/fr/design/formula/FunctionConstants.java
  40. 2
      designer-base/src/main/java/com/fr/design/gui/controlpane/UIControlPane.java
  41. 12
      designer-base/src/main/java/com/fr/design/gui/controlpane/UIListControlPane.java
  42. 26
      designer-base/src/main/java/com/fr/design/gui/frpane/JTreeControlPane.java
  43. 158
      designer-base/src/main/java/com/fr/design/gui/frpane/TreeSettingPane.java
  44. 40
      designer-base/src/main/java/com/fr/design/gui/frpane/tree/layer/config/LayerDataControlPane.java
  45. 6
      designer-base/src/main/java/com/fr/design/gui/ifilechooser/SwingFileChooser.java
  46. 177
      designer-base/src/main/java/com/fr/design/gui/ilist/CheckBoxListWithPartialSelect.java
  47. 40
      designer-base/src/main/java/com/fr/design/gui/iprogressbar/ProgressDialog.java
  48. 7
      designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/RefreshableJTree.java
  49. 5
      designer-base/src/main/java/com/fr/design/gui/style/TextFormatPane.java
  50. 5858
      designer-base/src/main/java/com/fr/design/gui/syntax/ui/rsyntaxtextarea/modes/FormulaTokenMaker.java
  51. 8
      designer-base/src/main/java/com/fr/design/javascript/JSContentPane.java
  52. 26
      designer-base/src/main/java/com/fr/design/login/socketio/LoginAuthServer.java
  53. 7
      designer-base/src/main/java/com/fr/design/mainframe/BaseJForm.java
  54. 26
      designer-base/src/main/java/com/fr/design/mainframe/CenterRegionContainerPane.java
  55. 21
      designer-base/src/main/java/com/fr/design/mainframe/DefaultToolKitConfig.java
  56. 19
      designer-base/src/main/java/com/fr/design/mainframe/DesignerFrame.java
  57. 4
      designer-base/src/main/java/com/fr/design/mainframe/DesignerFrameFileDealerPane.java
  58. 10
      designer-base/src/main/java/com/fr/design/mainframe/JDashboard.java
  59. 19
      designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java
  60. 27
      designer-base/src/main/java/com/fr/design/mainframe/ToolKitConfigStrategy.java
  61. 2
      designer-base/src/main/java/com/fr/design/mainframe/theme/edit/chart/ChartTitleAndBackgroundStylePane.java
  62. 2
      designer-base/src/main/java/com/fr/design/mainframe/widget/accessibles/AccessibleTreeModelEditor.java
  63. 5
      designer-base/src/main/java/com/fr/design/mainframe/widget/wrappers/TreeModelWrapper.java
  64. 45
      designer-base/src/main/java/com/fr/design/record/analyzer/DesignerAnalyzer.java
  65. 131
      designer-base/src/main/java/com/fr/design/record/analyzer/DesignerAnalyzerActivator.java
  66. 12
      designer-base/src/main/java/com/fr/design/record/analyzer/DesignerAnalyzerAdvice.java
  67. 23
      designer-base/src/main/java/com/fr/design/record/analyzer/DesignerAnalyzerListener.java
  68. 91
      designer-base/src/main/java/com/fr/design/record/analyzer/DesignerAssemblyFactory.java
  69. 23
      designer-base/src/main/java/com/fr/design/record/analyzer/advice/DBMonitorAdvice.java
  70. 46
      designer-base/src/main/java/com/fr/design/record/analyzer/advice/FaultToleranceAdvice.java
  71. 31
      designer-base/src/main/java/com/fr/design/record/analyzer/advice/FocusAdvice.java
  72. 155
      designer-base/src/main/java/com/fr/design/record/analyzer/advice/MonitorAdvice.java
  73. 49
      designer-base/src/main/java/com/fr/design/record/analyzer/advice/PerformancePointAdvice.java
  74. 36
      designer-base/src/main/java/com/fr/design/record/analyzer/advice/TimeAdvice.java
  75. 25
      designer-base/src/main/java/com/fr/design/record/analyzer/advice/TrackAdvice.java
  76. 31
      designer-base/src/main/java/com/fr/design/upm/UpmFinder.java
  77. 19
      designer-base/src/main/java/com/fr/design/upm/UpmShowPane.java
  78. 12
      designer-base/src/main/java/com/fr/design/utils/TemplateUtils.java
  79. 27
      designer-base/src/main/java/com/fr/env/utils/WorkspaceUtils.java
  80. 111
      designer-base/src/main/java/com/fr/exit/ConfigToPropMigrator.java
  81. 34
      designer-base/src/main/java/com/fr/file/FILEChooserPane.java
  82. 29
      designer-base/src/main/java/com/fr/nx/app/designer/utils/CptAndCptxCompatibilityUtil.java
  83. 28
      designer-base/src/main/java/com/fr/start/BaseDesigner.java
  84. 14
      designer-base/src/main/java/com/fr/start/event/LazyStartupEvent.java
  85. 26
      designer-base/src/main/resources/com/fr/design/gui/syntax/ui/rsyntaxtextarea/modes/FormulaTokenMaker.flex
  86. 6
      designer-base/src/main/resources/com/fr/design/i18n/dimension_en.properties
  87. 6
      designer-base/src/main/resources/com/fr/design/i18n/dimension_ja_JP.properties
  88. 6
      designer-base/src/main/resources/com/fr/design/i18n/dimension_ko_KR.properties
  89. 6
      designer-base/src/main/resources/com/fr/design/i18n/dimension_zh.properties
  90. 6
      designer-base/src/main/resources/com/fr/design/i18n/dimension_zh_TW.properties
  91. 5
      designer-base/src/main/resources/com/fr/design/images/control/batch_esd_off_disabled.svg
  92. 3
      designer-base/src/main/resources/com/fr/design/images/control/batch_esd_off_normal.svg
  93. 5
      designer-base/src/main/resources/com/fr/design/images/control/batch_esd_on_disabled.svg
  94. 3
      designer-base/src/main/resources/com/fr/design/images/control/batch_esd_on_normal.svg
  95. 9
      designer-base/src/main/resources/com/fr/design/images/data/back_normal.svg
  96. 10
      designer-base/src/main/resources/com/fr/design/images/data/clear_normal.svg
  97. 9
      designer-base/src/main/resources/com/fr/design/images/data/search_normal.svg
  98. 57
      designer-base/src/test/java/com/fr/design/data/MapCompareUtilsTest.java
  99. 43
      designer-base/src/test/java/com/fr/design/data/datapane/management/clip/TableDataTreeClipboardTest.java
  100. 201
      designer-base/src/test/java/com/fr/design/data/tabledata/paste/TableDataFollowingPasteUtilsTest.java
  101. Some files were not shown because too many files have changed in this diff Show More

8
build.gradle

@ -67,10 +67,10 @@ allprojects {
implementation 'com.fr.third:jxbrowser:6.23'
implementation 'com.fr.third:jxbrowser-mac:6.23'
implementation 'com.fr.third:jxbrowser-win64:6.23'
implementation 'com.fr.third:jxbrowser-v7:7.15'
implementation 'com.fr.third:jxbrowser-mac-v7:7.15'
implementation 'com.fr.third:jxbrowser-win64-v7:7.15'
implementation 'com.fr.third:jxbrowser-swing-v7:7.15'
implementation 'com.fr.third:jxbrowser-v7:7.22'
implementation 'com.fr.third:jxbrowser-mac-v7:7.22'
implementation 'com.fr.third:jxbrowser-win64-v7:7.22'
implementation 'com.fr.third:jxbrowser-swing-v7:7.22'
implementation 'com.fr.third.server:servlet-api:3.0'
implementation 'org.swingexplorer:swexpl:2.0.1'
implementation 'org.swingexplorer:swag:1.0'

13
designer-base/src/main/java/com/fr/design/DesignModelAdapter.java

@ -4,6 +4,7 @@ import com.fr.base.Parameter;
import com.fr.base.ParameterConfig;
import com.fr.base.TableData;
import com.fr.base.io.BaseBook;
import com.fr.base.param.ParameterSource;
import com.fr.data.TableDataSource;
import com.fr.data.operator.DataOperator;
import com.fr.design.file.HistoryTemplateListCache;
@ -19,8 +20,8 @@ import com.fr.stable.Filter;
import com.fr.stable.ParameterProvider;
import com.fr.stable.StringUtils;
import com.fr.stable.js.WidgetName;
import com.fr.util.ParameterApplyHelper;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@ -283,9 +284,9 @@ public abstract class DesignModelAdapter<T extends BaseBook, S extends JTemplate
TableData tableData = this.getBook().getTableData(name);
ParameterProvider[] parameterProviders = DataOperator.getInstance().getTableDataParameters(tableData);
if (filter != null) {
ParameterApplyHelper.addPara2Map(map, parameterProviders, filter);
ParameterApplyHelper.addPara2Map(map, parameterProviders, filter, null, ParameterSource.DEFAULT_SOURCE);
} else {
ParameterApplyHelper.addPara2Map(map, parameterProviders);
ParameterApplyHelper.addPara2Map(map, parameterProviders, null, ParameterSource.TEMPLATE_SOURCE);
}
tableDataParametersMap.put(name, parameterProviders);
}
@ -353,9 +354,9 @@ public abstract class DesignModelAdapter<T extends BaseBook, S extends JTemplate
*/
private void updateParaMap(Map<String, ParameterProvider> map, ParameterProvider[] parameterProviders, Filter<ParameterProvider> filter) {
if (filter != null) {
ParameterApplyHelper.addPara2Map(map, parameterProviders, filter);
ParameterApplyHelper.addPara2Map(map, parameterProviders, filter, null, ParameterSource.DEFAULT_SOURCE);
} else {
ParameterApplyHelper.addPara2Map(map, parameterProviders);
ParameterApplyHelper.addPara2Map(map, parameterProviders, null, ParameterSource.DEFAULT_SOURCE);
}
}
@ -378,7 +379,7 @@ public abstract class DesignModelAdapter<T extends BaseBook, S extends JTemplate
protected void addGlobalParameters(Map<String, ParameterProvider> map) {
// 添加全局参数
Parameter[] glbParas = ParameterConfig.getInstance().getGlobalParameters();
ParameterApplyHelper.addPara2Map(map, glbParas);
ParameterApplyHelper.addPara2Map(map, glbParas, null, ParameterSource.GLOBAL_SOURCE);
}

35
designer-base/src/main/java/com/fr/design/DesignerEnvManager.java

@ -214,6 +214,8 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter {
private boolean useOptimizedUPM4Adapter;
private boolean propertiesUsable;
/**
* DesignerEnvManager.
*/
@ -442,12 +444,35 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter {
private static File envFile = null;
private File getEnvFile() {
checkDebugStart();
if (envFile == null) {
envFile = new File(ProductConstants.getEnvHome() + File.separator + ProductConstants.APP_NAME + "Env.xml");
}
return envFile;
}
/**
* 在VM options里加入-Ddebug=true激活
*/
private static void checkDebugStart() {
if (ComparatorUtils.equals("true", System.getProperty("debug"))) {
setDebugEnv();
}
}
/**
* 端口改一下环境配置文件改一下便于启动两个设计器进行对比调试
*/
private static void setDebugEnv() {
DesignUtils.setPort(DesignerPort.getInstance().getDebugMessagePort());
DesignerEnvManager.setEnvFile(new File(StableUtils.pathJoin(
ProductConstants.getEnvHome(),
ProductConstants.APP_NAME + "Env_debug.xml"
)));
}
/**
* 是否启用了https
*
@ -676,6 +701,14 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter {
this.useOptimizedUPM4Adapter = useOptimizedUPM4Adapter;
}
public boolean isPropertiesUsable() {
return this.propertiesUsable;
}
public void setPropertiesUsable(boolean propertiesUsable) {
this.propertiesUsable = propertiesUsable;
}
/**
* 知否自动备份
*
@ -1900,6 +1933,7 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter {
this.setEmbedServerLazyStartup(reader.getAttrAsBoolean("embedServerLazyStartup", false));
this.setShowTemplateMissingPlugin(reader.getAttrAsBoolean("showTemplateMissingPlugin", true));
this.setUseOptimizedUPM4Adapter(reader.getAttrAsBoolean("useOptimizedUPM4Adapter", SupportOSImpl.MACOS_12_VERSION_ADAPTER.support()));
this.setPropertiesUsable(reader.getAttrAsBoolean("propertiesUsable", false));
this.setShowServerDatasetAuthTip(reader.getAttrAsBoolean("showServerDatasetAuthTip", true));
this.setLayoutTemplateStyle(reader.getAttrAsInt("layoutTemplateStyle", LAYOUT_TEMPLATE_SIMPLE_STYLE));
}
@ -2178,6 +2212,7 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter {
writer.attr("layoutTemplateStyle", this.getLayoutTemplateStyle());
writer.attr("showServerDatasetAuthTip", this.isShowServerDatasetAuthTip());
writer.attr("useOptimizedUPM4Adapter", this.isUseOptimizedUPM4Adapter());
writer.attr("propertiesUsable", this.isPropertiesUsable());
writer.end();
}

4
designer-base/src/main/java/com/fr/design/RestartHelper.java

@ -57,10 +57,8 @@ public class RestartHelper {
properties.setProperty((i + size) + "", files[i]);
}
}
try {
FileOutputStream file2DeleteOutputStream = new FileOutputStream(file);
try (FileOutputStream file2DeleteOutputStream = new FileOutputStream(file)) {
properties.store(file2DeleteOutputStream, "save");
file2DeleteOutputStream.close();
} catch (IOException e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}

4
designer-base/src/main/java/com/fr/design/data/BasicTableDataTreePane.java

@ -378,9 +378,7 @@ public abstract class BasicTableDataTreePane extends DockingView implements Resp
}
protected boolean isDsNameRepeaded(String name) {
if (allDSNames == null) {
allDSNames = DesignTableDataManager.getAllDSNames(tc.getBook());
}
allDSNames = DesignTableDataManager.getAllDSNames(tc.getBook());
for (int i = 0; i < allDSNames.length; i++) {
if (ComparatorUtils.equals(name, allDSNames[i])) {
return true;

11
designer-base/src/main/java/com/fr/design/data/DesignTableDataManager.java

@ -308,6 +308,17 @@ public abstract class DesignTableDataManager {
return resMap;
}
/**
* 不根据过滤设置返回当前模板数据集是有顺序的
*/
public static java.util.Map<String, TableDataWrapper> getTemplateDataSet(TableDataSource source) {
java.util.Map<String, TableDataWrapper> resMap = new java.util.LinkedHashMap<String, TableDataWrapper>();
// 模板数据集
addTemplateData(resMap, source);
return resMap;
}
public static java.util.Map<String, TableDataWrapper> getAllDataSetIncludingProcedure(java.util.Map<String, TableDataWrapper> resMap) {
java.util.LinkedHashMap<String, TableDataWrapper> dsMap = new java.util.LinkedHashMap<String, TableDataWrapper>();
Iterator<Entry<String, TableDataWrapper>> entryIt = resMap.entrySet().iterator();

53
designer-base/src/main/java/com/fr/design/data/MapCompareUtils.java

@ -0,0 +1,53 @@
package com.fr.design.data;
import org.jetbrains.annotations.NotNull;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author rinoux
* @version 10.0
* Created by rinoux on 2022/3/28
*/
public final class MapCompareUtils {
/**
* 对比两个map 查找出相比origother中有哪些是新增的删除的或者被修改的并分别进行处理
*
* @param orig 原始map
* @param other 参考的新map
* @param eventHandler 有区别时的事件处理器
* @param <K> K
* @param <V> V
*/
public static <K, V> void contrastMapEntries(@NotNull Map<K, V> orig, @NotNull Map<K, V> other, @NotNull EventHandler<K, V> eventHandler) {
Map<K, V> copiedOrig = new LinkedHashMap<>(orig);
other.forEach((k, v) -> {
V existedV = copiedOrig.remove(k);
if (existedV != null) {
if (!v.equals(existedV)) {
eventHandler.on(EntryEventKind.UPDATED, k, v);
}
} else {
eventHandler.on(EntryEventKind.ADDED, k, v);
}
});
copiedOrig.forEach((k, v) -> eventHandler.on(EntryEventKind.REMOVED, k, v));
}
public interface EventHandler<K, V> {
void on(EntryEventKind entryEventKind, K k, V v);
}
public enum EntryEventKind {
ADDED,
REMOVED,
UPDATED;
}
}

243
designer-base/src/main/java/com/fr/design/data/datapane/TableDataTree.java

@ -1,7 +1,10 @@
package com.fr.design.data.datapane;
import com.fr.base.BaseUtils;
import com.fr.data.impl.storeproc.StoreProcedure;
import com.fr.design.constants.UIConstants;
import com.fr.design.data.datapane.management.search.TableDataTreeSearchManager;
import com.fr.design.data.tabledata.wrapper.AbstractTableDataWrapper;
import com.fr.design.data.tabledata.wrapper.TableDataWrapper;
import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode;
import com.fr.design.gui.itree.refreshabletree.UserObjectRefreshJTree;
@ -17,6 +20,10 @@ import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import java.awt.Color;
import java.awt.Component;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* TableData Tree
@ -24,6 +31,10 @@ import java.awt.Component;
public class TableDataTree extends UserObjectRefreshJTree<TableDataSourceOP> {
private static final long serialVersionUID = 1L;
private static final String TABLE_DATA_NODE = "tableData";
private static final String COLUMN_NODE = "column";
/**
* Constructor.
*/
@ -84,12 +95,13 @@ public class TableDataTree extends UserObjectRefreshJTree<TableDataSourceOP> {
this.tableDataTreeCellRenderer = tableDataTreeCellRenderer;
}
@Override
protected void refreshTreeNode(ExpandMutableTreeNode eTreeNode, String childName) {
if (interceptRefresh(eTreeNode)) {
return;
}
boolean refreshall = childName.isEmpty();
ExpandMutableTreeNode[] new_nodes = loadChildTreeNodes(eTreeNode);
ExpandMutableTreeNode[] newNodes = loadChildTreeNodes(eTreeNode);
java.util.List<DefaultMutableTreeNode> childTreeNodeList = new java.util.ArrayList<DefaultMutableTreeNode>();
for (int i = 0, len = eTreeNode.getChildCount(); i < len; i++) {
@ -102,30 +114,30 @@ public class TableDataTree extends UserObjectRefreshJTree<TableDataSourceOP> {
eTreeNode.removeAllChildren();
for (int ci = 0; ci < new_nodes.length; ci++) {
Object cUserObject = new_nodes[ci].getUserObject();
for (int ci = 0; ci < newNodes.length; ci++) {
Object cUserObject = newNodes[ci].getUserObject();
ExpandMutableTreeNode cTreeNode = null;
for (int ni = 0, nlen = childTreeNodeList.size(); ni < nlen; ni++) {
cTreeNode = (ExpandMutableTreeNode) childTreeNodeList.get(ni);
if (ComparatorUtils.equals(cTreeNode.getUserObject(), cUserObject)) {
if (!refreshall && !ComparatorUtils.equals(childName, ((NameObject) cUserObject).getName())) {
new_nodes[ci] = cTreeNode;
newNodes[ci] = cTreeNode;
break;
}
new_nodes[ci].setExpanded(cTreeNode.isExpanded());
newNodes[ci].setExpanded(cTreeNode.isExpanded());
// REPORT-41299 如果建立的是错误的数据集(没有Child的情况)且这个错误数据集处于isExpanded状态,会在后面的if语句中调用getFirstChild()产生异常,因此这里判断一下
if (cTreeNode.isExpanded() && cTreeNode.getChildCount() == 0) {
new_nodes[ci].setExpanded(false);
if (cTreeNode.getChildCount() == 0) {
newNodes[ci].setExpanded(false);
break;
}
if (cTreeNode.getFirstChild() instanceof ExpandMutableTreeNode && cTreeNode.isExpanded()) {
checkChildNodes(cTreeNode, new_nodes[ci]);
checkChildNodes(cTreeNode, newNodes[ci]);
}
break;
}
}
eTreeNode.add(new_nodes[ci]);
eTreeNode.add(newNodes[ci]);
}
}
@ -140,9 +152,7 @@ public class TableDataTree extends UserObjectRefreshJTree<TableDataSourceOP> {
for (int k = 0; k < nodes.length; k++) {
newChild.add(nodes[k]);
}
if (newChild.getChildCount() > 1 && ((ExpandMutableTreeNode) newChild.getFirstChild()).getUserObject() == PENDING) {
newChild.remove(0);
}
removePending(newChild);
if (ComparatorUtils.equals(oldChild.getUserObject(), newChild.getUserObject())) {
newChild.setExpanded(oldChild.isExpanded());
}
@ -150,9 +160,100 @@ public class TableDataTree extends UserObjectRefreshJTree<TableDataSourceOP> {
}
}
private void removePending(ExpandMutableTreeNode treeNode) {
if (treeNode.getChildCount() > 1 && ((ExpandMutableTreeNode) treeNode.getFirstChild()).getUserObject() == PENDING) {
treeNode.remove(0);
}
}
@Override
public void refresh4TreeSearch() {
ExpandMutableTreeNode root = (ExpandMutableTreeNode) this.getModel().getRoot();
refreshTreeNode4TreeSearch(root);
((DefaultTreeModel) this.getModel()).reload(root);
root.expandCurrentTreeNode(this);
}
/**
* 主要是处理节点是否应该添加为搜索结果以及节点是否需要展开
*
* @param root
*/
private void refreshTreeNode4TreeSearch(ExpandMutableTreeNode root) {
if (interceptRefresh(root)) {
return;
}
// 获取数据集子节点
ExpandMutableTreeNode[] dsTreeNodes = loadChildTreeNodes(root);
root.removeAllChildren();
for (ExpandMutableTreeNode dsTreeNode : dsTreeNodes) {
if (TableDataTreeSearchManager.getInstance().nodeNameMatches(dsTreeNode.getUserObject().toString())) {
// 加载数据列节点
loadAndAddChildTreeChild(dsTreeNode);
// 处理子节点的展开
dealWithNodeExpand(dsTreeNode);
// 添加数据集子节点
root.add(dsTreeNode);
}
}
}
/**
* 加载所有子节点并添加到父节点中
*
* @param treeNode
* @return
*/
private ExpandMutableTreeNode loadAndAddChildTreeChild(ExpandMutableTreeNode treeNode) {
if (isTreeNodeStoreProcedure(treeNode)) {
// 如果是存储过程,则再加载一次其子表节点,这里比较坑的就是存储过程不能使用loadChildTreeNodes
int tableChildCounts = treeNode.getChildCount();
ExpandMutableTreeNode[] childs = new ExpandMutableTreeNode[tableChildCounts];
for (int i = 0; i < tableChildCounts; i++) {
ExpandMutableTreeNode tableChild = (ExpandMutableTreeNode) treeNode.getChildAt(i);
loadAndAddChildTreeChild(tableChild);
childs[i] = tableChild;
removePending(tableChild);
}
treeNode.addChildTreeNodes(childs);
} else {
ExpandMutableTreeNode[] expandMutableTreeNodes = loadChildTreeNodes(treeNode);
treeNode.addChildTreeNodes(expandMutableTreeNodes);
}
removePending(treeNode);
return treeNode;
}
/**
* 处理节点的展开如果此节点是存储过程还会处理其子表节点的展开
* 只针对数据集节点
*
* @param treeNode
* @return
*/
public ExpandMutableTreeNode dealWithNodeExpand(ExpandMutableTreeNode treeNode) {
String tableDataName = treeNode.getUserObject().toString();
// 主要还是处理存储过程
if (isTreeNodeStoreProcedure(treeNode)) {
int childCount = treeNode.getChildCount();
for (int i = 0; i < childCount; i++) {
ExpandMutableTreeNode child = (ExpandMutableTreeNode) treeNode.getChildAt(i);
String nodeName = tableDataName + "_" + child.getUserObject().toString();
if (TableDataTreeSearchManager.getInstance().nodeCanExpand(nodeName)) {
child.setExpanded(true);
}
}
}
if (TableDataTreeSearchManager.getInstance().nodeCanExpand(treeNode.getUserObject().toString())) {
treeNode.setExpanded(true);
}
return treeNode;
}
/*
* p:获得选中的NameObject = name + tabledata.
*/
@Override
public NameObject getSelectedNameObject() {
TreePath selectedTreePath = this.getSelectionPath();
if (selectedTreePath == null) {
@ -182,6 +283,108 @@ public class TableDataTree extends UserObjectRefreshJTree<TableDataSourceOP> {
}
/**
* 获得选中的数据集节点的NameObject的数组只会返回数据集节点的NameObject
* 当多选了数据集或数据列时也只返回选中的数据集
*/
public NameObject[] getSelectedTableDataNameObjects() {
Map<String, List<ExpandMutableTreeNode>> tableDataNodesAndColumnNodes = getSelectedTableDataNodesAndColumnNodes();
List<ExpandMutableTreeNode> tableDataNodes = tableDataNodesAndColumnNodes.get(TABLE_DATA_NODE);
if (tableDataNodes == null) {
return new NameObject[0];
}
return tableDataNodes.stream().map(node -> (NameObject) node.getUserObject()).toArray(NameObject[]::new);
}
/**
* 获取选中的数据集节点和列名节点
* 其中存储过程的子表节点不计入数据集节点中仅存储过程节点本身计入数据集节点
* @return
*/
private Map<String, List<ExpandMutableTreeNode>> getSelectedTableDataNodesAndColumnNodes() {
TreePath[] selectedTreePaths = this.getSelectionPaths();
if (selectedTreePaths == null) {
return new HashMap<>();
}
Map<String, List<ExpandMutableTreeNode>> resultMap = new HashMap<>();
List<ExpandMutableTreeNode> tableDataNodes = new ArrayList<>();
List<ExpandMutableTreeNode> columnNodes = new ArrayList<>();
resultMap.put(TABLE_DATA_NODE, tableDataNodes);
resultMap.put(COLUMN_NODE, columnNodes);
for (TreePath selectedTreePath : selectedTreePaths) {
if (selectedTreePath == null) {
continue;
}
ExpandMutableTreeNode selectedTreeNode = (ExpandMutableTreeNode) selectedTreePath.getLastPathComponent();
if (isTableDataNodes(selectedTreeNode)) {
// 数据集节点
tableDataNodes.add(selectedTreeNode);
} else {
// 列名节点
columnNodes.add(selectedTreeNode);
}
}
return resultMap;
}
/**
* 获取选中的数据集数量选中数据列则不计入
*
* @return
*/
public int getSelectedTableDataCounts() {
return getSelectedTableDataNameObjects().length;
}
/**
* 是否存在单独选了数据列节点但没选其对应数据集的情况
* @return
*/
public boolean hasSelectedIndependentColumns() {
Map<String, List<ExpandMutableTreeNode>> tableDataNodesAndColumnNodes = getSelectedTableDataNodesAndColumnNodes();
List<ExpandMutableTreeNode> tableDataNodes = tableDataNodesAndColumnNodes.get(TABLE_DATA_NODE);
List<ExpandMutableTreeNode> columnNodes = tableDataNodesAndColumnNodes.get(COLUMN_NODE);
if (columnNodes == null || columnNodes.size() == 0) {
// 未选中数据列
return false;
}
if (tableDataNodes == null || tableDataNodes.size() == 0) {
// 选中数据列而未选中数据集
return true;
}
boolean result = false;
for (ExpandMutableTreeNode columnNode : columnNodes) {
ExpandMutableTreeNode tableDataNode = getBelongedTableDataNodes(columnNode);
if (!tableDataNodes.contains(tableDataNode)) {
result = true;
}
}
return result;
}
/**
* 获取一个节点归属的数据集层级父节点
* @param treeNode
* @return
*/
private ExpandMutableTreeNode getBelongedTableDataNodes(ExpandMutableTreeNode treeNode) {
if (isTableDataNodes(treeNode)) {
return treeNode;
}
return getBelongedTableDataNodes((ExpandMutableTreeNode) treeNode.getParent());
}
private boolean isTableDataNodes(ExpandMutableTreeNode treeNode) {
if (treeNode == null) {
return false;
}
Object userObject = treeNode.getUserObject();
if (userObject instanceof NameObject && ((NameObject) userObject).getObject() instanceof AbstractTableDataWrapper) {
return true;
}
return false;
}
public TableDataWrapper[] getSelectedDatas() {
TreePath[] selectedTreePaths = this.getSelectionPaths();
if (selectedTreePaths == null || selectedTreePaths.length == 0) {
@ -247,4 +450,20 @@ public class TableDataTree extends UserObjectRefreshJTree<TableDataSourceOP> {
treeModel.reload(root);
}
/**
* 判断此节点是否为存储过程
*
* @param treeNode
* @return
*/
public boolean isTreeNodeStoreProcedure(ExpandMutableTreeNode treeNode) {
Object userObject = treeNode.getUserObject();
if (userObject instanceof NameObject) {
NameObject nameObject = (NameObject) userObject;
TableDataWrapper tableDataWrapper = (TableDataWrapper) nameObject.getObject();
return tableDataWrapper.getTableData() instanceof StoreProcedure;
}
return false;
}
}

397
designer-base/src/main/java/com/fr/design/data/datapane/TableDataTreePane.java

@ -13,6 +13,11 @@ import com.fr.design.data.BasicTableDataTreePane;
import com.fr.design.data.BasicTableDataUtils;
import com.fr.design.data.DesignTableDataManager;
import com.fr.design.data.StrategyConfigAttrUtils;
import com.fr.design.data.datapane.management.clip.TableDataTreeClipboard;
import com.fr.design.data.datapane.management.search.pane.TableDataSearchRemindPane;
import com.fr.design.data.datapane.management.search.pane.TreeSearchToolbarPane;
import com.fr.design.data.datapane.management.search.searcher.TableDataSearchMode;
import com.fr.design.data.datapane.management.search.TableDataTreeSearchManager;
import com.fr.design.data.tabledata.StoreProcedureWorkerListener;
import com.fr.design.data.tabledata.tabledatapane.AbstractTableDataPane;
import com.fr.design.data.tabledata.tabledatapane.DBTableDataPane;
@ -20,12 +25,15 @@ import com.fr.design.data.tabledata.wrapper.AbstractTableDataWrapper;
import com.fr.design.data.tabledata.wrapper.TableDataWrapper;
import com.fr.design.data.tabledata.wrapper.TemplateTableDataWrapper;
import com.fr.design.dialog.BasicDialog;
import com.fr.design.dialog.BasicPane;
import com.fr.design.dialog.DialogActionAdapter;
import com.fr.design.dialog.FineJOptionPane;
import com.fr.design.file.HistoryTemplateListCache;
import com.fr.design.fun.TableDataPaneProcessor;
import com.fr.design.gui.ibutton.UIHeadGroup;
import com.fr.design.gui.icontainer.UIScrollPane;
import com.fr.design.gui.ilable.UILabel;
import com.fr.design.gui.ilist.CheckBoxListWithPartialSelect;
import com.fr.design.gui.imenu.UIPopupMenu;
import com.fr.design.gui.itextfield.UITextField;
import com.fr.design.gui.itoolbar.UIToolbar;
import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode;
@ -37,6 +45,7 @@ import com.fr.design.menu.LineSeparator;
import com.fr.design.menu.MenuDef;
import com.fr.design.menu.SeparatorDef;
import com.fr.design.menu.ToolBarDef;
import com.fr.design.utils.gui.GUICoreUtils;
import com.fr.esd.core.strategy.config.StrategyConfig;
import com.fr.esd.core.strategy.config.StrategyConfigHelper;
import com.fr.esd.event.DSMapping;
@ -45,7 +54,6 @@ import com.fr.esd.event.StrategyEventsNotifier;
import com.fr.esd.query.StrategicTableData;
import com.fr.general.ComparatorUtils;
import com.fr.general.GeneralContext;
import com.fr.general.IOUtils;
import com.fr.general.NameObject;
import com.fr.log.FineLoggerFactory;
import com.fr.plugin.context.PluginContext;
@ -61,7 +69,6 @@ import org.jetbrains.annotations.NotNull;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
@ -69,9 +76,12 @@ import javax.swing.ToolTipManager;
import javax.swing.tree.TreePath;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.dnd.DnDConstants;
import java.awt.event.ActionEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
@ -105,15 +115,36 @@ public class TableDataTreePane extends BasicTableDataTreePane {
return singleton;
}
/**
* 获取不必每次都refreshDockingView的数据集树面板
* 不会主动替换DesignModelAdapter需要保证使用时没有跨模板动作谨慎使用
* @param tc
* @return
*/
public synchronized static BasicTableDataTreePane getInstanceWithoutRefreshEverytime(DesignModelAdapter<?, ?> tc) {
TableDataPaneProcessor treePaneProcessor = ExtraDesignClassManager.getInstance().getSingle(TableDataPaneProcessor.XML_TAG);
if (treePaneProcessor != null) {
return treePaneProcessor.createTableDataTreePane(tc);
}
return singleton.tc == null ? getInstance(tc) : singleton;
}
private TableDataSourceOP op;
private TableDataTree tableDataTree;
private UIPopupMenu popupMenu;
private EditAction editAction;
private RemoveAction removeAction;
private CopyAction copyAction;
private PasteAction pasteAction;
private EsdOnAction esdAction;
private EsdOffAction esdOffAction;
private SwitchAction switchAction;
private PreviewTableDataAction previewTableDataAction;
private JPanel serverDatasetAuthTipJPanel = new JPanel();
private TableDataSearchRemindPane remindPane;
private TreeSearchToolbarPane toolbarPane;
private TableDataTreePane() {
initPane();
}
@ -122,63 +153,131 @@ public class TableDataTreePane extends BasicTableDataTreePane {
this.setLayout(new BorderLayout(4, 0));
this.setBorder(null);
//TableDataTree
tableDataTree = new TableDataTree();
initTableDataTree();
toolbarPane = initToolBarPane();
JPanel treePane = initTreePane();
dealWithTableDataTree();
this.add(toolbarPane, BorderLayout.NORTH);
this.add(treePane, BorderLayout.CENTER);
checkButtonEnabled();
}
/**
* 处理TableDataTree的监听等
*/
private void dealWithTableDataTree() {
// tooltip
ToolTipManager.sharedInstance().registerComponent(tableDataTree);
ToolTipManager.sharedInstance().setDismissDelay(3000);
ToolTipManager.sharedInstance().setInitialDelay(0);
// 右键菜单
popupMenu = new UIPopupMenu();
popupMenu.add(editAction.createMenuItem());
popupMenu.add(previewTableDataAction.createMenuItem());
popupMenu.addSeparator();
popupMenu.add(copyAction.createMenuItem());
popupMenu.add(pasteAction.createMenuItem());
popupMenu.add(removeAction.createMenuItem());
popupMenu.addSeparator();
// 监听
tableDataTree.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
// 服务器暂不支持右键菜单
if (SwingUtilities.isRightMouseButton(e) && op.getDataMode() != TableDataSourceOP.SERVER_TABLE_DATA) {
GUICoreUtils.showPopupMenu(popupMenu, e.getComponent(), e.getX(), e.getY());
}
checkButtonEnabled();
}
});
tableDataTree.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
//F2重命名先屏蔽了, 有bug没时间弄
if (e.getKeyCode() == KeyEvent.VK_F2) {
return;
}
super.keyPressed(e);
checkButtonEnabled();
}
@Override
public void keyReleased(KeyEvent e) {
super.keyReleased(e);
checkButtonEnabled();
}
});
// TreeCellEditor
tableDataTree.setEditable(true);
TableDataTreeCellEditor treeCellEditor = new TableDataTreeCellEditor(new UITextField(), tableDataTree, this);
treeCellEditor.addCellEditorListener(treeCellEditor);
tableDataTree.setCellEditor(treeCellEditor);
new TableDataTreeDragSource(tableDataTree, DnDConstants.ACTION_COPY);
}
/**
* 工具栏面板
*
* @return
*/
private TreeSearchToolbarPane initToolBarPane() {
// toolbar
addMenuDef = new MenuDef(Toolkit.i18nText("Fine-Design_Basic_Action_Add"));
addMenuDef.setIconPath(IconPathConstants.ADD_POPMENU_ICON_PATH);
addMenuDef.setIconPath("/com/fr/design/images/control/addPopup");
createAddMenuDef();
// 创建插件监听
createPluginListener();
editAction = new EditAction();
copyAction = new CopyAction();
pasteAction = new PasteAction();
removeAction = new RemoveAction();
previewTableDataAction = new PreviewTableDataAction(tableDataTree);
connectionTableAction = new ConnectionTableAction();
esdAction = new EsdOnAction();
esdOffAction = new EsdOffAction();
switchAction = new SwitchAction();
toolbarDef = new ToolBarDef();
toolbarDef.addShortCut(addMenuDef, SeparatorDef.DEFAULT, editAction, removeAction, SeparatorDef.DEFAULT, previewTableDataAction, connectionTableAction, esdAction, esdOffAction);
toolbarDef.addShortCut(addMenuDef, SeparatorDef.DEFAULT, editAction, removeAction, SeparatorDef.DEFAULT, previewTableDataAction, connectionTableAction, esdAction, esdOffAction, switchAction);
UIToolbar toolBar = ToolBarDef.createJToolBar();
toolBar.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, UIConstants.TOOLBAR_BORDER_COLOR));
toolBar.setBorderPainted(true);
toolbarDef.updateToolBar(toolBar);
JPanel toolbarPane = FRGUIPaneFactory.createBorderLayout_S_Pane();
toolbarPane.add(toolBar, BorderLayout.CENTER);
this.add(toolbarPane, BorderLayout.NORTH);
TreeSearchToolbarPane searchLayerdPane = new TreeSearchToolbarPane(toolBar);
searchLayerdPane.setPreferredSize(new Dimension(this.getWidth(), 23));
UIScrollPane scrollPane = new UIScrollPane(tableDataTree);
scrollPane.setBorder(null);
return searchLayerdPane;
}
/**
* 数据集树面板
*
* @return
*/
private JPanel initTreePane() {
JPanel treePane = new JPanel(new BorderLayout(0, 6));
// north
JPanel northPane = new JPanel(FRGUIPaneFactory.createBorderLayout());
initServerDatasetAuthTipJPanel();
initButtonGroup();
JPanel jPanel = new JPanel(new BorderLayout(0, 0));
JPanel buttonPane = new JPanel(FRGUIPaneFactory.createBorderLayout());
buttonPane.add(buttonGroup, BorderLayout.CENTER);
buttonPane.add(serverDatasetAuthTipJPanel, BorderLayout.SOUTH);
jPanel.add(buttonPane, BorderLayout.NORTH);
jPanel.add(scrollPane, BorderLayout.CENTER);
this.add(jPanel, BorderLayout.CENTER);
tableDataTree.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
checkButtonEnabled();
}
});
tableDataTree.addKeyListener(getTableTreeNodeListener(editAction, previewTableDataAction, removeAction, op, tableDataTree));
// TreeCellEditor
tableDataTree.setEditable(true);
TableDataTreeCellEditor treeCellEditor = new TableDataTreeCellEditor(new UITextField(), tableDataTree, this);
treeCellEditor.addCellEditorListener(treeCellEditor);
tableDataTree.setCellEditor(treeCellEditor);
new TableDataTreeDragSource(tableDataTree, DnDConstants.ACTION_COPY);
checkButtonEnabled();
northPane.add(buttonGroup, BorderLayout.CENTER);
northPane.add(serverDatasetAuthTipJPanel, BorderLayout.SOUTH);
// center
remindPane = new TableDataSearchRemindPane(getDataTree());
treePane.add(northPane, BorderLayout.NORTH);
treePane.add(remindPane, BorderLayout.CENTER);
return treePane;
}
/**
* 初始化 TableDataTree
*/
private void initTableDataTree() {
tableDataTree = new TableDataTree();
}
private void initServerDatasetAuthTipJPanel() {
@ -300,6 +399,10 @@ public class TableDataTreePane extends BasicTableDataTreePane {
*/
@Override
public void dgEdit(final AbstractTableDataPane<?> tableDataPane, String originalName, boolean isUpdate) {
// 编辑时如果正在搜索,跳回原树
if (TableDataTreeSearchManager.getInstance().isInSearchMode()) {
TableDataTreeSearchManager.getInstance().outOfSearchMode();
}
tableDataPane.addStoreProcedureWorkerListener(new StoreProcedureWorkerListener() {
public void fireDoneAction() {
if (tableDataTree.getSelectionPath() == null) {
@ -471,6 +574,17 @@ public class TableDataTreePane extends BasicTableDataTreePane {
((TableDataSourceDependent) td).setTableDataSource(tds);
}
String tdName = nPanel.getObjectName();
//处理缓存策略配置
if (uPanel instanceof DBTableDataPane) {
StrategyConfig editingConfig = ((DBTableDataPane) uPanel).updateStrategyConfig();
if (editingConfig != null) {
editingConfig.setDsName(tdName);
StrategyConfigAttrUtils.addStrategyConfig(editingConfig);
}
((DBTableData) td).setDsName(tdName);
}
tds.putTableData(tdName, td);
Map<String, String> map = new HashMap<String, String>();
if (!ComparatorUtils.equals(paneName, tdName)) {
@ -482,6 +596,7 @@ public class TableDataTreePane extends BasicTableDataTreePane {
int[] rows = tableDataTree.getSelectionRows();
tableDataTree.refreshChildByName(tdName);
tableDataTree.setSelectionRows(rows);
FineLoggerFactory.getLogger().info("add table data succeeded for {}", tdName);
}
@Override
@ -501,11 +616,61 @@ public class TableDataTreePane extends BasicTableDataTreePane {
this.createAddMenuDef();
}
/**
* 感觉这里把一堆Action和Op之类的送到抽象类里去检查很奇怪抽象类本身定义的Action只有add和connection
* 另外因为改动了数据集树节点的选中逻辑所以这边改成自己类内部实现
* 不直接改抽象类是怕影响到部分插件兼容
*/
private void checkButtonEnabled() {
super.checkButtonEnabled(editAction, previewTableDataAction, removeAction, op, tableDataTree);
// 检查添加与定义数据连接操作
this.checkAddAndConnectionEnabled();
// 检查编辑、预览、复制、粘贴、删除等基本操作
this.checkBasicButtonEnabled();
// 检查esd相关操作
this.checkESDComponentsEnabled();
}
private void checkAddAndConnectionEnabled() {
connectionTableAction.setEnabled(WorkContext.getCurrent() != null && WorkContext.getCurrent().isRoot());
addMenuDef.setEnabled(!(op == null || op.interceptButtonEnabled() || op.getDataMode() == SERVER_TABLE_DATA));
}
private void checkBasicButtonEnabled() {
// 设置下各个button的基本状态,避免代码重复
editAction.setEnabled(false);
copyAction.setEnabled(false);
pasteAction.setEnabled(false);
removeAction.setEnabled(false);
previewTableDataAction.setEnabled(false);
if (op == null || op.interceptButtonEnabled()) {
// 保持false状态
return;
}
// 获取选中的数据集数量
int selectioncount = getDataTree().getSelectedTableDataCounts();
if (op.getDataMode() == SERVER_TABLE_DATA) {
// 服务器数据集下,选中数据集数量为1时,可以预览
if (selectioncount == 1 && !getDataTree().hasSelectedIndependentColumns()) {
previewTableDataAction.setEnabled(true);
}
// 其它保持false状态
return;
}
// 模板数据集时,粘贴可用
pasteAction.setEnabled(true);
if (selectioncount == 0 || getDataTree().hasSelectedIndependentColumns()) {
// 未选中数据集,或存在单独选中的数据列时,其它保持false状态
return;
}
if (selectioncount == 1) {
// 仅选中单个数据集时,才可以编译、预览
editAction.setEnabled(true);
previewTableDataAction.setEnabled(true);
}
removeAction.setEnabled(true);
copyAction.setEnabled(true);
}
private void checkESDComponentsEnabled() {
if (buttonGroup.getSelectedIndex() == 1) {
@ -594,6 +759,9 @@ public class TableDataTreePane extends BasicTableDataTreePane {
if (op != null) {
op.setDataMode(modeArray[buttonGroup.getSelectedIndex()]);
addMenuDef.setEnabled(modeArray[buttonGroup.getSelectedIndex()] == TEMPLATE_TABLE_DATA);
if (TableDataTreeSearchManager.getInstance().isInSearchMode()) {
TableDataTreeSearchManager.getInstance().outOfSearchMode();
}
refreshDockingView();
}
@ -689,8 +857,8 @@ public class TableDataTreePane extends BasicTableDataTreePane {
}
@Override
public Icon getIcon() {
return IOUtils.readIcon("/com/fr/design/images/control/batch_esd_on.png");
public String getIconResource() {
return "/com/fr/design/images/control/batch_esd_on";
}
@Override
@ -736,8 +904,8 @@ public class TableDataTreePane extends BasicTableDataTreePane {
}
@Override
public Icon getIcon() {
return IOUtils.readIcon("/com/fr/design/images/control/batch_esd_off.png");
public String getIconResource() {
return "/com/fr/design/images/control/batch_esd_off";
}
@Override
@ -758,14 +926,14 @@ public class TableDataTreePane extends BasicTableDataTreePane {
public abstract String getName();
public abstract Icon getIcon();
public abstract String getIconResource();
public abstract void doWithTableDataWrapper(TableDataWrapper tableDataWrapper);
public AbstractESDAction() {
this.setName(getName());
this.setMnemonic('R');
this.setSmallIcon(getIcon());
this.setSmallIcon(getIconResource());
}
@Override
@ -831,7 +999,7 @@ public class TableDataTreePane extends BasicTableDataTreePane {
public EditAction() {
this.setName(Toolkit.i18nText("Fine-Design_Basic_Edit"));
this.setMnemonic('E');
this.setSmallIcon(IOUtils.readIcon(IconPathConstants.TD_EDIT_ICON_PATH));
this.setSmallIcon("/com/fr/design/images/control/edit");
}
@Override
@ -858,39 +1026,138 @@ public class TableDataTreePane extends BasicTableDataTreePane {
public RemoveAction() {
this.setName(Toolkit.i18nText("Fine-Design_Basic_Remove"));
this.setMnemonic('R');
this.setSmallIcon(IOUtils.readIcon(IconPathConstants.TD_REMOVE_ICON_PATH));
this.setSmallIcon("/com/fr/design/images/control/remove");
}
@Override
public void actionPerformed(ActionEvent e) {
NameObject selectedNO = tableDataTree.getSelectedNameObject();
if (selectedNO == null) {
NameObject[] selectedNameObjects = tableDataTree.getSelectedTableDataNameObjects();
if (selectedNameObjects == null || selectedNameObjects.length == 0) {
FineLoggerFactory.getLogger().error("Table Data to remove is null or not selected");
return;
}
// 可以半选的CheckBoxList
CheckBoxListWithPartialSelect tableDataCheckBoxPane = new CheckBoxListWithPartialSelect(selectedNameObjects);
UIScrollPane scrollPane = new UIScrollPane(tableDataCheckBoxPane);
UILabel tips = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Select_Source_To_Remove"));
BasicPane basicPane = new BasicPane() {
@Override
protected String title4PopupWindow() {
return Toolkit.i18nText("Fine-Design_Basic_Remove");
}
};
basicPane.setLayout(new BorderLayout());
basicPane.add(tips, BorderLayout.NORTH);
basicPane.add(scrollPane, BorderLayout.CENTER);
BasicDialog basicDialog = basicPane.showSmallWindow(SwingUtilities.getWindowAncestor(TableDataTreePane.this), new DialogActionAdapter() {
@Override
public void doOk() {
List selectedValues = tableDataCheckBoxPane.getSelectedObjects();
// 删除时如果正在搜索,跳回原树
if (TableDataTreeSearchManager.getInstance().isInSearchMode()) {
TableDataTreeSearchManager.getInstance().outOfSearchMode();
}
for (Object toRemove : selectedValues) {
try {
doRemove((String) toRemove);
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e, "remove table data failed for {}", toRemove);
}
}
}
int returnVal = FineJOptionPane.showConfirmDialog(DesignerContext.getDesignerFrame(), Toolkit.i18nText("Fine-Design_Basic_Utils_Are_You_Sure_To_Remove_The_Selected_Item") + ":" + selectedNO.getName() + "?",
Toolkit.i18nText("Fine-Design_Basic_Remove"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
if (returnVal == JOptionPane.OK_OPTION) {
// richer:这个地方为什么要在DataSourceTree里面去remove呢?多此一举吧
op.removeAction(selectedNO.getName());
tableDataTree.refresh();
// Richie:默认最后一行获得焦点.
tableDataTree.requestFocus();
tableDataTree.setSelectionRow(tableDataTree.getRowCount() - 1);
fireDSChanged();
checkButtonEnabled();
@Override
public void doCancel() {
super.doCancel();
}
});
basicDialog.setVisible(true);
}
private void doRemove(String toRemove) throws Exception {
// richer:这个地方为什么要在DataSourceTree里面去remove呢?多此一举吧
op.removeAction(toRemove);
tableDataTree.refresh();
// Richie:默认最后一行获得焦点.
tableDataTree.requestFocus();
tableDataTree.setSelectionRow(tableDataTree.getRowCount() - 1);
fireDSChanged();
checkButtonEnabled();
//删掉缓存配置
StrategyConfigAttrUtils.removeStrategyConfig(toRemove);
// 如果一个模版是平台开启,这个数据集的配置不会存xml,预览模版时直接从全局配置copy,这样
// 导致删除的时候StrategyConfigsAttrSavedHook没有通过前后配置比较感知数据集被删除,因此不会发出事件让其失效
// 这里额外发出一次数据集修改事件
StrategyEventsNotifier.modifyDataSet(new DSMapping(getTplPath(), new DsNameTarget(toRemove)));
DesignTableDataManager.removeSelectedColumnNames(toRemove);
DesignModelAdapter.getCurrentModelAdapter().removeTableDataParameters(toRemove);
FineLoggerFactory.getLogger().info("remove table data succeeded for {}", toRemove);
}
}
private class CopyAction extends UpdateAction {
public CopyAction() {
this.setName(Toolkit.i18nText("Fine-Design_Basic_Copy"));
this.setMnemonic('C');
this.setSmallIcon("/com/fr/design/images/m_edit/copy");
}
@Override
public void actionPerformed(ActionEvent e) {
NameObject[] selectedNameObjects = tableDataTree.getSelectedTableDataNameObjects();
Map<String, AbstractTableDataWrapper> dataWrapperMap = TableDataTreeClipboard.getInstance().transferNameObjectArray2Map(selectedNameObjects);
TableDataTreeClipboard.getInstance().addToClip(dataWrapperMap);
}
}
//删掉缓存配置
StrategyConfigAttrUtils.removeStrategyConfig(selectedNO.getName());
private class PasteAction extends UpdateAction {
// 如果一个模版是平台开启,这个数据集的配置不会存xml,预览模版时直接从全局配置copy,这样
// 导致删除的时候StrategyConfigsAttrSavedHook没有通过前后配置比较感知数据集被删除,因此不会发出事件让其失效
// 这里额外发出一次数据集修改事件
StrategyEventsNotifier.modifyDataSet(new DSMapping(getTplPath(), new DsNameTarget(selectedNO.getName())));
DesignTableDataManager.removeSelectedColumnNames(selectedNO.getName());
DesignModelAdapter.getCurrentModelAdapter().removeTableDataParameters(selectedNO.getName());
public PasteAction() {
this.setName(Toolkit.i18nText("Fine-Design_Basic_Action_Paste_Name"));
this.setMnemonic('P');
this.setSmallIcon("/com/fr/design/images/m_edit/paste");
}
@Override
public void actionPerformed(ActionEvent e) {
// 粘贴时如果正在搜索,跳回原树
if (TableDataTreeSearchManager.getInstance().isInSearchMode()) {
TableDataTreeSearchManager.getInstance().outOfSearchMode();
}
Map<String, AbstractTableDataWrapper> dataWrapperMap = TableDataTreeClipboard.getInstance().takeFromClip();
for (Map.Entry<String, AbstractTableDataWrapper> dataWrapperEntry : dataWrapperMap.entrySet()) {
// 处理数据集名称
String dsName = getNoRepeatedDsName4Paste(dataWrapperEntry.getKey());
AbstractTableDataWrapper wrapper = dataWrapperEntry.getValue();
AbstractTableDataPane<?> tableDataPane = wrapper.creatTableDataPane();
addDataPane(tableDataPane, dsName);
}
}
}
public String getNoRepeatedDsName4Paste(String oldName) {
while (isDsNameRepeaded(oldName)) {
oldName = oldName + Toolkit.i18nText("Fine-Design_Table_Data_Copy_Of_Table_Data");
}
return oldName;
}
private class SwitchAction extends UpdateAction {
public SwitchAction() {
this.setName(Toolkit.i18nText("Fine-Design_Basic_Search"));
this.setMnemonic('S');
this.setSmallIcon("/com/fr/design/images/data/search");
}
@Override
public void actionPerformed(ActionEvent e) {
// 交换层级
toolbarPane.switchPane(TreeSearchToolbarPane.SEARCH_PANE);
TableDataTreeSearchManager.getInstance().switchToSearch(TableDataSearchMode.match(buttonGroup.getSelectedIndex()), DesignTableDataManager.getEditingTableDataSource());
}
}

75
designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionListPane.java

@ -6,6 +6,7 @@ import com.fr.data.impl.ConnectionBean;
import com.fr.data.impl.JDBCDatabaseConnection;
import com.fr.data.impl.JNDIDatabaseConnection;
import com.fr.design.ExtraDesignClassManager;
import com.fr.design.data.MapCompareUtils;
import com.fr.design.dialog.BasicDialog;
import com.fr.design.fun.ConnectionProvider;
import com.fr.design.gui.controlpane.JListControlPane;
@ -16,21 +17,21 @@ import com.fr.design.i18n.Toolkit;
import com.fr.event.EventDispatcher;
import com.fr.file.ConnectionConfig;
import com.fr.file.ConnectionOperator;
import com.fr.file.ConnectionOperatorImpl;
import com.fr.general.NameObject;
import com.fr.log.FineLoggerFactory;
import com.fr.rpc.ExceptionHandler;
import com.fr.rpc.RPCInvokerExceptionInfo;
import com.fr.stable.ArrayUtils;
import com.fr.stable.Nameable;
import com.fr.stable.StringUtils;
import com.fr.stable.core.PropertyChangeAdapter;
import com.fr.third.org.apache.commons.collections4.MapUtils;
import com.fr.transaction.Configurations;
import com.fr.transaction.WorkerFacade;
import com.fr.workspace.WorkContext;
import java.awt.Window;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -42,7 +43,8 @@ import java.util.UUID;
public class ConnectionListPane extends JListControlPane implements ConnectionShowPane {
public static final String TITLE_NAME = Toolkit.i18nText("Fine-Design_Basic_Server_Define_Data_Connection");
private boolean isNamePermitted = true;
private HashMap<String, String> renameMap = new HashMap<String, String>();
private final HashMap<String, String> renameMap = new HashMap<>();
private final Map<String, Connection> populatedConnectionsSnapshot = new LinkedHashMap<>();
public ConnectionListPane() {
renameMap.clear();
@ -149,11 +151,16 @@ public class ConnectionListPane extends JListControlPane implements ConnectionSh
*/
public void populate(ConnectionConfig connectionConfig) {
List<NameObject> nameObjectList = new ArrayList<NameObject>();
populatedConnectionsSnapshot.clear();
for (Map.Entry<String, Connection> entry : connectionConfig.getConnections().entrySet()) {
nameObjectList.add(new NameObject(entry.getKey(), entry.getValue()));
try {
populatedConnectionsSnapshot.put(entry.getKey(), (Connection) entry.getValue().clone());
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
}
this.populate(nameObjectList.toArray(new NameObject[nameObjectList.size()]));
}
/**
@ -162,23 +169,36 @@ public class ConnectionListPane extends JListControlPane implements ConnectionSh
public void update(ConnectionConfig connectionConfig) {
// Nameable[]居然不能强转成NameObject[],一定要这么写...
Nameable[] res = this.update();
List<ConnectionBean> connectionBeans = new ArrayList<>();
Map<String, String> map = MapUtils.invertMap(getRenameMap());
for (int i = 0; i < res.length; i++) {
NameObject nameObject = (NameObject) res[i];
String oldName = map.get(nameObject.getName());
if (oldName == null) {
oldName = StringUtils.EMPTY;
Map<String, Connection> updatedMap = new LinkedHashMap<>();
Arrays.stream(res).map(n -> (NameObject) n).forEach(no -> updatedMap.put(no.getName(), (Connection) no.getObject()));
List<String> removedConnNames = new ArrayList<>();
List<ConnectionBean> addedOrUpdatedConnections = new ArrayList<>();
MapCompareUtils.contrastMapEntries(populatedConnectionsSnapshot, updatedMap, (entryEventKind, s, connection) -> {
switch (entryEventKind) {
case REMOVED:
removedConnNames.add(s);
break;
case ADDED:
case UPDATED:
addedOrUpdatedConnections.add(new ConnectionBean(s, StringUtils.EMPTY, connection));
default:
break;
}
connectionBeans.add(new ConnectionBean(nameObject.getName(), oldName, (Connection) nameObject.getObject()));
}
});
this.alterConnections(removedConnNames, addedOrUpdatedConnections);
}
private void alterConnections(List<String> removedConnNames, List<ConnectionBean> addedOrUpdatedConnections) {
try {
WorkContext.getCurrent().get(ConnectionOperator.class, new ExceptionHandler() {
@Override
public Object callHandler(RPCInvokerExceptionInfo exceptionInfo) {
return saveByOldWay(connectionBeans);
}
}).saveConnection(new ArrayList<>(connectionConfig.getConnections().keySet()), connectionBeans);
WorkContext.getCurrent().get(ConnectionOperator.class, exceptionInfo -> saveByOldWay(removedConnNames, addedOrUpdatedConnections))
.saveConnection(removedConnNames, addedOrUpdatedConnections);
// hades:远程环境时,由于时直接RPC调用远程修改,因此设计器本地配置需要失效
if (!WorkContext.getCurrent().isLocal()) {
EventDispatcher.fire(RemoteConfigEvent.EDIT, ConnectionConfig.getInstance().getNameSpace());
}
@ -187,13 +207,20 @@ public class ConnectionListPane extends JListControlPane implements ConnectionSh
}
}
private boolean saveByOldWay(List<ConnectionBean> connectionBeans) {
private boolean saveByOldWay(List<String> removedConnNames, List<ConnectionBean> addedOrUpdatedConnections) {
try {
return ConnectionOperatorImpl.getInstance().saveConnection(connectionBeans);
return Configurations.modify(new WorkerFacade(ConnectionConfig.class) {
@Override
public void run() {
removedConnNames.forEach(n -> ConnectionConfig.getInstance().removeConnection(n));
addedOrUpdatedConnections.forEach(cb -> ConnectionConfig.getInstance().addConnection(cb.getName(), cb.getConnection()));
}
});
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
return false;
}
return false;
}
public static void showDialog(Window parent) {

12
designer-base/src/main/java/com/fr/design/data/datapane/connect/SshPane.java

@ -7,6 +7,7 @@ import com.fr.data.security.ssh.SshException;
import com.fr.data.security.ssh.SshType;
import com.fr.data.security.ssh.impl.KeyVerifySsh;
import com.fr.data.security.ssh.impl.NormalSsh;
import com.fr.data.security.ssl.SslUtils;
import com.fr.design.border.UITitledBorder;
import com.fr.design.constants.UIConstants;
import com.fr.design.dialog.BasicPane;
@ -25,7 +26,6 @@ import com.fr.file.FILE;
import com.fr.file.FILEChooserPane;
import com.fr.file.filter.ChooseFileFilter;
import com.fr.stable.StringUtils;
import com.fr.stable.project.ProjectConstants;
import com.fr.third.guava.collect.HashBiMap;
import javax.swing.ImageIcon;
@ -131,7 +131,7 @@ public class SshPane extends BasicPane {
fileChooserButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
FILEChooserPane fileChooser = FILEChooserPane.getInstanceWithDesignatePath(ProjectConstants.RESOURCES_NAME, new ChooseFileFilter(true));
FILEChooserPane fileChooser = FILEChooserPane.getInstanceWithDesignatePath(SslUtils.PREFIX, new ChooseFileFilter(true), SslUtils.CERTIFICATES);
int type = fileChooser.showOpenDialog(SshPane.this, StringUtils.EMPTY);
if (type == FILEChooserPane.OK_OPTION) {
final FILE file = fileChooser.getSelectedFILE();
@ -142,6 +142,7 @@ public class SshPane extends BasicPane {
}
}
fileChooser.removeAllFilter();
fileChooser.removeTopPath();
}
});
}
@ -241,7 +242,6 @@ public class SshPane extends BasicPane {
public static class KeyFileUITextField extends UITextField {
private static final Pattern ERROR_START = Pattern.compile("^([/\\\\.]+).*");
private static final Pattern MUTI_DOT = Pattern.compile("\\.+");
private static final String PREFIX = ProjectConstants.RESOURCES_NAME + "/";
private static final String UPPER = "..";
public KeyFileUITextField(int columns) {
@ -283,7 +283,7 @@ public class SshPane extends BasicPane {
public String getText() {
// 获取的时候,不为空,给他加上前缀就好了,否则还是空
if (!StringUtils.isEmpty(super.getText())) {
return PREFIX + super.getText();
return SslUtils.PREFIX + super.getText();
}
return StringUtils.EMPTY;
}
@ -291,8 +291,8 @@ public class SshPane extends BasicPane {
@Override
public void setText(String text) {
// 设置的时候,不为空,说明文件指定了(文件需要是resource下),替换掉前缀
if (!StringUtils.isEmpty(text) && text.startsWith(PREFIX)) {
super.setText(text.replaceFirst(PREFIX, ""));
if (!StringUtils.isEmpty(text) && text.startsWith(SslUtils.PREFIX)) {
super.setText(text.replaceFirst(SslUtils.PREFIX, ""));
} else {
super.setText(text);
}

5
designer-base/src/main/java/com/fr/design/data/datapane/connect/SslPane.java

@ -4,6 +4,7 @@ import com.fr.data.impl.JDBCDatabaseConnection;
import com.fr.data.security.ssl.Ssl;
import com.fr.data.security.ssl.SslException;
import com.fr.data.security.ssl.SslType;
import com.fr.data.security.ssl.SslUtils;
import com.fr.data.security.ssl.impl.NormalSsl;
import com.fr.design.border.UITitledBorder;
import com.fr.design.constants.UIConstants;
@ -21,7 +22,6 @@ import com.fr.file.FILE;
import com.fr.file.FILEChooserPane;
import com.fr.file.filter.ChooseFileFilter;
import com.fr.stable.StringUtils;
import com.fr.stable.project.ProjectConstants;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
@ -148,7 +148,7 @@ public class SslPane extends BasicPane {
@Override
public void actionPerformed(ActionEvent e) {
FILEChooserPane fileChooser = FILEChooserPane.getInstanceWithDesignatePath(ProjectConstants.RESOURCES_NAME, new ChooseFileFilter(true));
FILEChooserPane fileChooser = FILEChooserPane.getInstanceWithDesignatePath(SslUtils.PREFIX, new ChooseFileFilter(true), SslUtils.CERTIFICATES);
int type = fileChooser.showOpenDialog(SslPane.this, StringUtils.EMPTY);
if (type == FILEChooserPane.OK_OPTION) {
final FILE file = fileChooser.getSelectedFILE();
@ -159,6 +159,7 @@ public class SslPane extends BasicPane {
}
}
fileChooser.removeAllFilter();
fileChooser.removeTopPath();
}
}
}

68
designer-base/src/main/java/com/fr/design/data/datapane/management/clip/TableDataTreeClipboard.java

@ -0,0 +1,68 @@
package com.fr.design.data.datapane.management.clip;
import com.fr.design.data.tabledata.wrapper.AbstractTableDataWrapper;
import com.fr.general.NameObject;
import java.util.HashMap;
import java.util.Map;
/**
* 用于数据集的复制粘贴
*
* @author Yvan
*/
public class TableDataTreeClipboard {
/**
* 数据集名称 - 数据集Wrapper
*/
private Map<String, AbstractTableDataWrapper> clip = new HashMap<>();
private static class Holder {
private static final TableDataTreeClipboard INSTANCE = new TableDataTreeClipboard();
}
private TableDataTreeClipboard() {
}
public static TableDataTreeClipboard getInstance() {
return Holder.INSTANCE;
}
/**
* 添加选中的数据集数据到剪切板覆盖原本剪切板内数据
*
* @param copyMap
* @return
*/
public void addToClip(Map<String, AbstractTableDataWrapper> copyMap) {
this.clip = copyMap;
}
public Map<String, AbstractTableDataWrapper> transferNameObjectArray2Map(NameObject[] selectedNameObjects) {
Map<String, AbstractTableDataWrapper> resultMap = new HashMap<>();
if (selectedNameObjects == null) {
return resultMap;
}
for (NameObject selectedNameObject : selectedNameObjects) {
resultMap.put(selectedNameObject.getName(), (AbstractTableDataWrapper) selectedNameObject.getObject());
}
return resultMap;
}
/**
* 取出剪切板内的所有数据集数据剪切板不清空
*
* @return
*/
public Map<String, AbstractTableDataWrapper> takeFromClip() {
return clip;
}
/**
* 清空剪切板
*/
public void reset() {
clip.clear();
}
}

242
designer-base/src/main/java/com/fr/design/data/datapane/management/search/TableDataTreeSearchManager.java

@ -0,0 +1,242 @@
package com.fr.design.data.datapane.management.search;
import com.fr.data.TableDataSource;
import com.fr.design.DesignModelAdapter;
import com.fr.design.data.datapane.TableDataTree;
import com.fr.design.data.datapane.TableDataTreePane;
import com.fr.design.data.datapane.management.search.event.TreeSearchStatusChangeEvent;
import com.fr.design.data.datapane.management.search.event.TreeSearchStatusChangeListener;
import com.fr.design.data.datapane.management.search.searcher.TableDataSearchMode;
import com.fr.design.data.datapane.management.search.searcher.TableDataTreeSearcher;
import com.fr.design.data.datapane.management.search.searcher.TreeSearchStatus;
import com.fr.design.data.datapane.management.search.view.TreeSearchRendererHelper;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.StringUtils;
import javax.swing.SwingUtilities;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 数据集树搜索管理器
*
* @author Yvan
*/
public class TableDataTreeSearchManager {
/**
* 数据集树搜索器
*/
private TableDataTreeSearcher treeSearcher;
/**
* 搜索任务的状态
*/
private TreeSearchStatus treeSearchStatus;
/**
* 缓存上次搜索文本避免重复搜索
*/
private String lastSearchText;
/**
* 存储与复原 原本数据集树的UI
*/
private TreeSearchRendererHelper rendererHelper;
/**
* 取数计数器
*/
private AtomicInteger count;
/**
* 搜索状态变化监听
*/
private List<TreeSearchStatusChangeListener> listeners = new ArrayList<>();
private TableDataTreeSearchManager() {
init();
}
private void init() {
this.treeSearchStatus = TreeSearchStatus.NOT_IN_SEARCH_MODE;
}
private static class Holder {
private static final TableDataTreeSearchManager INSTANCE = new TableDataTreeSearchManager();
}
public static TableDataTreeSearchManager getInstance() {
return Holder.INSTANCE;
}
public TreeSearchStatus getTreeSearchStatus() {
return treeSearchStatus;
}
public void setTreeSearchStatus(TreeSearchStatus treeSearchStatus) {
this.treeSearchStatus = treeSearchStatus;
// 每次设置搜索状态,都触发下监听,让页面跟随变化
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
for (TreeSearchStatusChangeListener listener : listeners) {
listener.updateTreeSearchChange(new TreeSearchStatusChangeEvent(treeSearchStatus));
}
}
});
}
public void registerTreeSearchStatusChangeListener(TreeSearchStatusChangeListener listener) {
listeners.add(listener);
}
/**
* 工具栏处切换到搜索面板
*
* @param searchMode
* @param tableDataSource
*/
public void switchToSearch(TableDataSearchMode searchMode, TableDataSource tableDataSource) {
setTreeSearchStatus(TreeSearchStatus.SEARCH_NOT_BEGIN);
rendererHelper = new TreeSearchRendererHelper();
rendererHelper.save(getCurrentTableDataTree());
treeSearcher = new TableDataTreeSearcher();
FineLoggerFactory.getLogger().debug("switch to table data search for mode: {}", searchMode.name());
treeSearcher.beforeSearch(searchMode, tableDataSource);
}
/**
* 获取当前的tableDataTree
*
* @return
*/
private TableDataTree getCurrentTableDataTree() {
DesignModelAdapter<?, ?> currentModelAdapter = DesignModelAdapter.getCurrentModelAdapter();
TableDataTreePane tableDataTreePane = (TableDataTreePane) TableDataTreePane.getInstance(currentModelAdapter);
return tableDataTreePane.getDataTree();
}
public boolean isMatchSetsEmpty() {
return treeSearcher.isMatchSetsEmpty();
}
/**
* 开始搜索
*
* @param searchText
*/
public void startSearch(String searchText) {
if (isRepeatSearch(searchText) || StringUtils.isEmpty(searchText)) {
return;
}
setTreeSearchStatus(TreeSearchStatus.SEARCHING);
rendererHelper.replaceTreeRenderer(getCurrentTableDataTree(), searchText);
count = new AtomicInteger(treeSearcher.getAllWrappersSize());
FineLoggerFactory.getLogger().debug("start table data search for search text: {}", searchText);
treeSearcher.startSearch(searchText);
}
/**
* 计数-1
*/
public void decreaseCount() {
if (count == null) {
return;
}
int cunrrentCount = count.decrementAndGet();
// 减到0后判断状态
if (cunrrentCount == 0 && getTreeSearchStatus() == TreeSearchStatus.SEARCHING) {
completeSearch();
}
}
private boolean isRepeatSearch(String searchText) {
boolean repeat = StringUtils.equals(lastSearchText, searchText);
lastSearchText = searchText;
return repeat;
}
/**
* 刷新树更新搜索的结果
*/
public void updateTableDataTree() {
getCurrentTableDataTree().refresh4TreeSearch();
}
/**
* 中断搜索
*/
public void stopSearch() {
setTreeSearchStatus(TreeSearchStatus.SEARCH_STOPPED);
count = null;
FineLoggerFactory.getLogger().debug("stop table data search for search text: {}", lastSearchText);
treeSearcher.stopSearch();
}
/**
* 搜索完成
*/
public void completeSearch() {
setTreeSearchStatus(TreeSearchStatus.SEARCH_COMPLETED);
count = null;
FineLoggerFactory.getLogger().debug("complete table data search for search text: {}", lastSearchText);
treeSearcher.completeSearch();
}
/**
* 切换回工具栏恢复数据集树UI
*/
public void restoreToolBarAndTreePane() {
setTreeSearchStatus(TreeSearchStatus.NOT_IN_SEARCH_MODE);
FineLoggerFactory.getLogger().info("out of table data search");
if (treeSearcher != null) {
treeSearcher.afterSearch();
}
lastSearchText = null;
if (rendererHelper != null) {
rendererHelper.restore(getCurrentTableDataTree());
}
}
/**
* 恢复数据集树UI
*/
public void restoreTreePane() {
setTreeSearchStatus(TreeSearchStatus.SEARCH_NOT_BEGIN);
lastSearchText = null;
if (rendererHelper != null) {
rendererHelper.restore(getCurrentTableDataTree());
}
}
/**
* 节点是否应该添加到搜索结果树的根节点中
* 只针对数据集节点
*
* @param treeNodeName 数据集节点名称
* @return
*/
public boolean nodeNameMatches(String treeNodeName) {
return treeSearcher.nodeMatches(treeNodeName);
}
/**
* 节点是否应该展开
*
* @param treeNodeName 节点名称
* @return
*/
public boolean nodeCanExpand(String treeNodeName) {
return treeSearcher.nodeCanExpand(treeNodeName);
}
public boolean isInSearchMode() {
return getTreeSearchStatus() != TreeSearchStatus.NOT_IN_SEARCH_MODE;
}
public void outOfSearchMode() {
restoreToolBarAndTreePane();
}
}

12
designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/TreeSearchCallback.java

@ -0,0 +1,12 @@
package com.fr.design.data.datapane.management.search.control;
/**
* 搜索任务回调
*
* @author Yvan
*/
public interface TreeSearchCallback {
void done(TreeSearchResult treeSearchResult);
}

37
designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/TreeSearchResult.java

@ -0,0 +1,37 @@
package com.fr.design.data.datapane.management.search.control;
import java.util.List;
/**
* @author Yvan
*/
public interface TreeSearchResult {
/**
* 任务结果是否成功
*
* @return
*/
boolean isSuccess();
/**
* 数据集名称匹配或者列名匹配时需要将数据集名称添加到匹配结果集中
*
* @return
*/
List<String> getAddToMatch();
/**
* 数据集有列名匹配时需要添加到展开结果集中
*
* @return
*/
List<String> getAddToExpand();
/**
* 数据集完成计算后需要添加到完成结果集中
*
* @return
*/
List<String> getAddToCalculated();
}

10
designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/TreeSearchTask.java

@ -0,0 +1,10 @@
package com.fr.design.data.datapane.management.search.control;
/**
* @author Yvan
*/
public interface TreeSearchTask extends Runnable {
@Override
void run();
}

54
designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/common/TableDataSearchCallBack.java

@ -0,0 +1,54 @@
package com.fr.design.data.datapane.management.search.control.common;
import com.fr.design.data.datapane.management.search.TableDataTreeSearchManager;
import com.fr.design.data.datapane.management.search.control.TreeSearchCallback;
import com.fr.design.data.datapane.management.search.control.TreeSearchResult;
import com.fr.design.data.datapane.management.search.searcher.TableDataTreeSearcher;
import com.fr.design.data.datapane.management.search.searcher.TreeSearchStatus;
import javax.swing.SwingUtilities;
/**
* @author Yvan
*/
public class TableDataSearchCallBack implements TreeSearchCallback {
protected TableDataTreeSearcher treeSearcher;
public TableDataSearchCallBack(TableDataTreeSearcher treeSearcher) {
this.treeSearcher = treeSearcher;
}
@Override
public void done(TreeSearchResult treeSearchResult) {
if (TableDataTreeSearchManager.getInstance().getTreeSearchStatus() != TreeSearchStatus.SEARCHING) {
return;
}
if (treeSearchResult.isSuccess()) {
// 添加结果
addToTreeSearcher(treeSearchResult);
}
// 处理UI
updateTableDataTree();
}
protected void updateTableDataTree() {
SwingUtilities.invokeLater(() -> {
if (TableDataTreeSearchManager.getInstance().getTreeSearchStatus() != TreeSearchStatus.SEARCHING) {
return;
}
TableDataTreeSearchManager.getInstance().updateTableDataTree();
// 搜索计数
TableDataTreeSearchManager.getInstance().decreaseCount();
});
}
protected void addToTreeSearcher(TreeSearchResult treeSearchResult) {
// 添加到已计算结果集
treeSearcher.addToCalculatedSets(treeSearchResult.getAddToCalculated());
// 添加到匹配结果集
treeSearcher.addToMatchSets(treeSearchResult.getAddToMatch());
// 添加到展开结果集
treeSearcher.addToCanExpandSets(treeSearchResult.getAddToExpand());
}
}

105
designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/common/TableDataSearchResult.java

@ -0,0 +1,105 @@
package com.fr.design.data.datapane.management.search.control.common;
import com.fr.design.data.datapane.management.search.control.TreeSearchResult;
import java.util.ArrayList;
import java.util.List;
/**
* @author Yvan
*/
public class TableDataSearchResult implements TreeSearchResult {
private boolean success;
private List<String> addToMatch;
private List<String> addToExpand;
private List<String> addToCalculated;
protected TableDataSearchResult(Builder builder) {
this.success = builder.success;
this.addToMatch = builder.addToMatch;
this.addToExpand = builder.addToExpand;
this.addToCalculated = builder.addToCalculated;
}
public void setSuccess(boolean success) {
this.success = success;
}
public void setAddToMatch(List<String> addToMatch) {
this.addToMatch = addToMatch;
}
public void setAddToExpand(List<String> addToExpand) {
this.addToExpand = addToExpand;
}
public void setAddToCalculated(List<String> addToCalculated) {
this.addToCalculated = addToCalculated;
}
@Override
public boolean isSuccess() {
return this.success;
}
@Override
public List<String> getAddToMatch() {
return this.addToMatch;
}
@Override
public List<String> getAddToExpand() {
return this.addToExpand;
}
@Override
public List<String> getAddToCalculated() {
return this.addToCalculated;
}
public static class Builder {
private boolean success;
private List<String> addToMatch;
private List<String> addToExpand;
private List<String> addToCalculated;
public Builder() {
this.success = false;
this.addToMatch = new ArrayList<>();
this.addToExpand = new ArrayList<>();
this.addToCalculated = new ArrayList<>();
}
public Builder buildSuccess(boolean success) {
this.success = success;
return this;
}
public Builder buildAddToMatch(List<String> addToMatch) {
this.addToMatch = addToMatch;
return this;
}
public Builder buildAddToExpand(List<String> addToExpand) {
this.addToExpand = addToExpand;
return this;
}
public Builder buildAddToCalculated(List<String> addToCalculated) {
this.addToCalculated = addToCalculated;
return this;
}
public TableDataSearchResult build() {
return new TableDataSearchResult(this);
}
}
}

147
designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/common/TableDataSearchTask.java

@ -0,0 +1,147 @@
package com.fr.design.data.datapane.management.search.control.common;
import com.fr.design.data.datapane.management.search.control.TreeSearchCallback;
import com.fr.design.data.datapane.management.search.control.TreeSearchResult;
import com.fr.design.data.datapane.management.search.control.TreeSearchTask;
import com.fr.design.data.tabledata.wrapper.StoreProcedureDataWrapper;
import com.fr.design.data.tabledata.wrapper.TableDataWrapper;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.StringUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author Yvan
*/
public class TableDataSearchTask implements TreeSearchTask {
/**
* 用户搜索的文本
*/
private String searchText;
private TableDataWrapper tableDataWrapper;
private TreeSearchCallback callback;
public TableDataSearchTask(String searchText, TableDataWrapper tableDataWrapper, TreeSearchCallback callback) {
this.searchText = searchText;
this.tableDataWrapper = tableDataWrapper;
this.callback = callback;
}
@Override
public void run() {
TreeSearchResult result;
try {
if (isTableDataStoreProcedure(tableDataWrapper)) {
result = dealWithStoreProcedureTableDataWrapper((StoreProcedureDataWrapper) tableDataWrapper);
} else {
result = dealWithCommonTableDataWrapper(tableDataWrapper);
}
FineLoggerFactory.getLogger().debug("calculate {}'s columns succeeded", tableDataWrapper.getTableDataName());
} catch (Throwable e) {
FineLoggerFactory.getLogger().error(e, "calculate {}'s columns failed", tableDataWrapper.getTableDataName());
result = dealWithErrorTableDataWrapper(tableDataWrapper);
}
callback.done(result);
}
/**
* 处理错误情况
*
* @param tableDataWrapper
*/
private TreeSearchResult dealWithErrorTableDataWrapper(TableDataWrapper tableDataWrapper) {
return new TableDataSearchResult.Builder().buildSuccess(false).build();
}
/**
* 处理普通数据集的搜索与匹配
*
* @param tableDataWrapper
*/
private TreeSearchResult dealWithCommonTableDataWrapper(TableDataWrapper tableDataWrapper) {
String tableDataName = tableDataWrapper.getTableDataName();
boolean isTableDataNameMatch = isMatchSearch(tableDataName, searchText);
List<String> columnNameList = tableDataWrapper.calculateColumnNameList();
// 没取到列名的话,代表取数那边出错了,就不添加数据集了
if (columnNameList.size() == 0) {
return new TableDataSearchResult.Builder().buildSuccess(false).build();
}
boolean isColumnMatch = columnNameList.stream().anyMatch(columnName -> isMatchSearch(columnName, searchText));
return new TableDataSearchResult.Builder()
.buildSuccess(true)
.buildAddToMatch(isTableDataNameMatch || isColumnMatch ? Arrays.asList(tableDataName) : new ArrayList<>())
.buildAddToExpand(isColumnMatch ? Arrays.asList(tableDataName) : new ArrayList<>())
.buildAddToCalculated(Arrays.asList(tableDataName))
.build();
}
/**
* 处理存储过程的搜索与匹配
*
* @param procedureDataWrapper
*/
private TreeSearchResult dealWithStoreProcedureTableDataWrapper(StoreProcedureDataWrapper procedureDataWrapper) {
// 存储过程数据集名称,例如 Proc1_Table1
String tableDataName = procedureDataWrapper.getTableDataName();
// 存储过程名称,例如 Proc1
String storeProcedureName = procedureDataWrapper.getStoreprocedureName();
// 存储过程子表名称,例如 Table1
String tableName = tableDataName.replaceFirst(storeProcedureName, StringUtils.EMPTY).replaceFirst("_", StringUtils.EMPTY);
boolean isStoreProcedureNameMatch = isMatchSearch(storeProcedureName, searchText);
boolean isTableNameMatch = isMatchSearch(tableName, searchText);
// 再处理子表的columns
List<String> columnNameList = tableDataWrapper.calculateColumnNameList();
boolean isColumnMatch = columnNameList.stream().anyMatch(columnName -> isMatchSearch(columnName, searchText));
Set<String> addToMatch = new HashSet<>();
Set<String> addToExpand = new HashSet<>();
Set<String> addToCalculated = new HashSet<>();
if (isStoreProcedureNameMatch) {
addToMatch.add(storeProcedureName);
}
if (isTableNameMatch) {
addToMatch.add(storeProcedureName);
addToExpand.add(storeProcedureName);
}
if (isColumnMatch) {
addToMatch.add(storeProcedureName);
addToExpand.add(storeProcedureName);
// 这里有重名风险,所以要添加 “Proc1_Table1”,在结果树展示的时候再去处理
addToExpand.add(tableDataName);
}
addToCalculated.add(tableDataName);
return new TableDataSearchResult.Builder()
.buildSuccess(true)
.buildAddToMatch(new ArrayList<>(addToMatch))
.buildAddToExpand(new ArrayList<>(addToExpand))
.buildAddToCalculated(new ArrayList<>(addToCalculated))
.build();
}
/**
* 判断TableDataWrapper内的TableData是否为存储过程
*
* @param tableDataWrapper
* @return
*/
private boolean isTableDataStoreProcedure(TableDataWrapper tableDataWrapper) {
return tableDataWrapper instanceof StoreProcedureDataWrapper;
}
/**
* 判断是否匹配搜索文本不区分大小写
*
* @param str
* @return
*/
private boolean isMatchSearch(String str, String searchText) {
return str.toUpperCase().contains(searchText.toUpperCase());
}
}

23
designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/pre/TableDataPreSearchCallBack.java

@ -0,0 +1,23 @@
package com.fr.design.data.datapane.management.search.control.pre;
import com.fr.design.data.datapane.management.search.control.TreeSearchResult;
import com.fr.design.data.datapane.management.search.control.common.TableDataSearchCallBack;
import com.fr.design.data.datapane.management.search.searcher.TableDataTreeSearcher;
/**
* 预取数任务回调
*
* @author Yvan
*/
public class TableDataPreSearchCallBack extends TableDataSearchCallBack {
public TableDataPreSearchCallBack(TableDataTreeSearcher treeSearcher) {
super(treeSearcher);
}
@Override
public void done(TreeSearchResult treeSearchResult) {
// do nothing
}
}

40
designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/pre/TableDataPreSearchResult.java

@ -0,0 +1,40 @@
package com.fr.design.data.datapane.management.search.control.pre;
import com.fr.design.data.datapane.management.search.control.TreeSearchResult;
import java.util.ArrayList;
import java.util.List;
/**
* 预取数任务结果不需要回调因此空实现即可
*
* @author Yvan
*/
public class TableDataPreSearchResult implements TreeSearchResult {
private boolean success;
public TableDataPreSearchResult(boolean success) {
this.success = success;
}
@Override
public boolean isSuccess() {
return this.success;
}
@Override
public List<String> getAddToMatch() {
return new ArrayList<>();
}
@Override
public List<String> getAddToExpand() {
return new ArrayList<>();
}
@Override
public List<String> getAddToCalculated() {
return new ArrayList<>();
}
}

45
designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/pre/TableDataPreSearchTask.java

@ -0,0 +1,45 @@
package com.fr.design.data.datapane.management.search.control.pre;
import com.fr.design.data.datapane.management.search.TableDataTreeSearchManager;
import com.fr.design.data.datapane.management.search.control.TreeSearchTask;
import com.fr.design.data.datapane.management.search.control.common.TableDataSearchResult;
import com.fr.design.data.datapane.management.search.control.TreeSearchCallback;
import com.fr.design.data.datapane.management.search.control.TreeSearchResult;
import com.fr.design.data.datapane.management.search.searcher.TreeSearchStatus;
import com.fr.design.data.tabledata.wrapper.TableDataWrapper;
import com.fr.log.FineLoggerFactory;
/**
* 预取数任务
*
* @author Yvan
*/
public class TableDataPreSearchTask implements TreeSearchTask {
private TreeSearchCallback callback;
private TableDataWrapper tableDataWrapper;
public TableDataPreSearchTask(TreeSearchCallback callback, TableDataWrapper tableDataWrapper) {
this.callback = callback;
this.tableDataWrapper = tableDataWrapper;
}
@Override
public void run() {
TreeSearchResult result;
try {
tableDataWrapper.calculateColumnNameList();
result = new TableDataSearchResult.Builder()
.buildSuccess(true)
.build();
FineLoggerFactory.getLogger().debug("pre calculate {}'s columns succeeded", tableDataWrapper.getTableDataName());
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e, "pre calculate {}'s columns failed", tableDataWrapper.getTableDataName());
result = new TableDataSearchResult.Builder()
.buildSuccess(false)
.build();
}
callback.done(result);
}
}

22
designer-base/src/main/java/com/fr/design/data/datapane/management/search/event/TreeSearchStatusChangeEvent.java

@ -0,0 +1,22 @@
package com.fr.design.data.datapane.management.search.event;
import com.fr.design.data.datapane.management.search.searcher.TreeSearchStatus;
import java.util.EventObject;
/**
* @author Yvan
*/
public class TreeSearchStatusChangeEvent extends EventObject {
private TreeSearchStatus status;
public TreeSearchStatusChangeEvent(Object source) {
super(source);
this.status = (TreeSearchStatus) source;
}
public TreeSearchStatus getTreeSearchStatus() {
return status;
}
}

11
designer-base/src/main/java/com/fr/design/data/datapane/management/search/event/TreeSearchStatusChangeListener.java

@ -0,0 +1,11 @@
package com.fr.design.data.datapane.management.search.event;
import java.util.EventListener;
/**
* @author Yvan
*/
public interface TreeSearchStatusChangeListener extends EventListener {
void updateTreeSearchChange(TreeSearchStatusChangeEvent event);
}

215
designer-base/src/main/java/com/fr/design/data/datapane/management/search/pane/TableDataSearchRemindPane.java

@ -0,0 +1,215 @@
package com.fr.design.data.datapane.management.search.pane;
import com.fr.base.svg.IconUtils;
import com.fr.design.constants.UIConstants;
import com.fr.design.data.datapane.TableDataTree;
import com.fr.design.data.datapane.management.search.TableDataTreeSearchManager;
import com.fr.design.data.datapane.management.search.event.TreeSearchStatusChangeEvent;
import com.fr.design.data.datapane.management.search.event.TreeSearchStatusChangeListener;
import com.fr.design.data.datapane.management.search.searcher.TreeSearchStatus;
import com.fr.design.gui.icontainer.UIScrollPane;
import com.fr.design.gui.ilable.UILabel;
import com.fr.design.i18n.Toolkit;
import com.fr.design.layout.FRGUIPaneFactory;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
/**
* @author Yvan
*/
public class TableDataSearchRemindPane extends JPanel implements TreeSearchStatusChangeListener {
private RemindPane remindPane;
private TreePane treePane;
public TableDataSearchRemindPane(TableDataTree tableDataTree) {
this.setLayout(new BorderLayout());
remindPane = new RemindPane();
treePane = new TreePane(tableDataTree);
// 初始状态
this.add(remindPane, BorderLayout.NORTH);
this.add(treePane, BorderLayout.CENTER);
TableDataTreeSearchManager.getInstance().registerTreeSearchStatusChangeListener(this);
}
/**
* 根据搜索状态变化来调整自身面板的显示
*
* @param event
*/
@Override
public void updateTreeSearchChange(TreeSearchStatusChangeEvent event) {
TreeSearchStatus status = event.getTreeSearchStatus();
if (status == TreeSearchStatus.SEARCH_NOT_BEGIN || status == TreeSearchStatus.NOT_IN_SEARCH_MODE) {
remindPane.onNotBegin();
treePane.onNotBegin();
} else if (status == TreeSearchStatus.SEARCHING) {
remindPane.onInSearching();
treePane.onInSearching();
} else if (status == TreeSearchStatus.SEARCH_STOPPED) {
remindPane.onStoppedSearching();
treePane.onStoppedSearching();
} else {
boolean matchSetsEmpty = TableDataTreeSearchManager.getInstance().isMatchSetsEmpty();
// 代表是否搜索出结果
remindPane.onDoneSearching(matchSetsEmpty);
treePane.onDoneSearching(matchSetsEmpty);
}
this.revalidate();
}
private interface TreeSearchStatusChange {
void onNotBegin();
void onInSearching();
void onStoppedSearching();
void onDoneSearching(boolean matchSetsEmpty);
}
private class TreePane extends JPanel implements TreeSearchStatusChange {
private UIScrollPane scrollPane;
private JPanel notFoundPane;
private CardLayout cardLayout;
private static final String SCROLL_PANE = "scrollPane";
private static final String NOT_FOUND_PANE = "notFoundPane";
public TreePane(TableDataTree tableDataTree) {
init(tableDataTree);
}
private void init(TableDataTree tableDataTree) {
scrollPane = new UIScrollPane(tableDataTree);
scrollPane.setBorder(null);
notFoundPane = FRGUIPaneFactory.createVerticalFlowLayout_Pane(true, FlowLayout.LEADING, 0, 5);
UILabel emptyPicLabel = new UILabel();
emptyPicLabel.setIcon(IconUtils.readIcon("com/fr/base/images/share/no_match_icon.png"));
emptyPicLabel.setHorizontalAlignment(SwingConstants.CENTER);
emptyPicLabel.setPreferredSize(new Dimension(240, 100));
UILabel textLabel = new UILabel(Toolkit.i18nText("Fine-Design_Tree_Search_Not_Match"), SwingConstants.CENTER);
textLabel.setForeground(Color.gray);
textLabel.setHorizontalAlignment(SwingConstants.CENTER);
textLabel.setPreferredSize(new Dimension(240, 20));
notFoundPane.add(emptyPicLabel);
notFoundPane.add(textLabel);
notFoundPane.setBorder(BorderFactory.createEmptyBorder(80, 0, 0, 0));
cardLayout = new CardLayout();
this.setLayout(cardLayout);
this.add(scrollPane, SCROLL_PANE);
this.add(notFoundPane, NOT_FOUND_PANE);
cardLayout.show(this, SCROLL_PANE);
}
@Override
public void onNotBegin() {
switchPane(SCROLL_PANE);
}
@Override
public void onInSearching() {
switchPane(SCROLL_PANE);
}
@Override
public void onStoppedSearching() {
switchPane(SCROLL_PANE);
}
@Override
public void onDoneSearching(boolean matchSetsEmpty) {
if (matchSetsEmpty) {
switchPane(NOT_FOUND_PANE);
}
}
private void switchPane(String paneName) {
cardLayout.show(this, paneName);
}
}
private static class RemindPane extends JPanel implements TreeSearchStatusChange {
private static final String IN_SEARCHING = Toolkit.i18nText("Fine-Design_Tree_Search_In_Searching");
private static final String STOP_SEARCHING = Toolkit.i18nText("Fine-Design_Tree_Search_Stop_Search");
private static final String SEARCHING_STOPPED = Toolkit.i18nText("Fine-Design_Tree_Search_Search_Stopped");
private static final String DONE_SEARCHING = Toolkit.i18nText("Fine-Design_Tree_Search_Search_Completed");
private UILabel textLabel;
private UILabel stopLabel;
private MouseListener stopSearch;
public RemindPane() {
init();
}
private void init() {
this.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 0));
// 初始情况下为Not_Begin
textLabel = new UILabel();
textLabel.setForeground(Color.gray);
stopLabel = new UILabel();
stopLabel.setForeground(UIConstants.NORMAL_BLUE);
stopSearch = new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
TableDataTreeSearchManager.getInstance().stopSearch();
}
};
stopLabel.addMouseListener(stopSearch);
this.add(textLabel);
this.add(stopLabel);
onNotBegin();
}
@Override
public void onNotBegin() {
this.setVisible(false);
}
@Override
public void onInSearching() {
this.textLabel.setVisible(false);
this.stopLabel.setText(STOP_SEARCHING);
this.stopLabel.setVisible(true);
this.setVisible(true);
}
@Override
public void onStoppedSearching() {
this.textLabel.setText(SEARCHING_STOPPED);
this.textLabel.setVisible(true);
this.stopLabel.setVisible(false);
this.setVisible(true);
}
@Override
public void onDoneSearching(boolean matchSetsEmpty) {
this.textLabel.setText(DONE_SEARCHING);
this.textLabel.setVisible(true);
this.stopLabel.setVisible(false);
this.setVisible(true);
}
}
}

208
designer-base/src/main/java/com/fr/design/data/datapane/management/search/pane/TreeSearchToolbarPane.java

@ -0,0 +1,208 @@
package com.fr.design.data.datapane.management.search.pane;
import com.fr.base.svg.IconUtils;
import com.fr.design.DesignModelAdapter;
import com.fr.design.constants.UIConstants;
import com.fr.design.data.datapane.TableDataTreePane;
import com.fr.design.data.datapane.management.search.TableDataTreeSearchManager;
import com.fr.design.data.datapane.management.search.event.TreeSearchStatusChangeEvent;
import com.fr.design.data.datapane.management.search.event.TreeSearchStatusChangeListener;
import com.fr.design.data.datapane.management.search.searcher.TreeSearchStatus;
import com.fr.design.gui.ilable.UILabel;
import com.fr.design.gui.itextfield.UITextField;
import com.fr.design.gui.itoolbar.UIToolbar;
import com.fr.design.i18n.Toolkit;
import com.fr.design.layout.FRGUIPaneFactory;
import com.fr.stable.StringUtils;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
/**
* @author Yvan
*/
public class TreeSearchToolbarPane extends JPanel implements TreeSearchStatusChangeListener {
public static final String TOOLBAR_PANE = "toolbarPane";
public static final String SEARCH_PANE = "searchPane";
/**
* 工具栏
*/
private UIToolbar toolbar;
/**
* 工具栏面板
*/
private JPanel toolbarPane;
/**
* 搜索面板
*/
private JPanel searchPane;
/**
* 搜索输入框
*/
private UITextField searchTextField;
/**
* 内容面板
*/
private JPanel contentPane;
/**
* 卡片布局管理器
*/
private CardLayout cardLayout;
private final KeyAdapter enterPressed = new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
TableDataTreeSearchManager.getInstance().startSearch(searchTextField.getText());
}
}
};
public TreeSearchToolbarPane(UIToolbar toolbar) {
this.toolbar = toolbar;
this.setLayout(FRGUIPaneFactory.createBorderLayout());
initToolbarPane();
initSearchPane();
initContentPane();
add(contentPane, BorderLayout.CENTER);
setPreferredSize(new Dimension(240, 30));
TableDataTreeSearchManager.getInstance().registerTreeSearchStatusChangeListener(this);
}
private void initContentPane() {
cardLayout = new CardLayout();
contentPane = new JPanel(cardLayout);
contentPane.add(searchPane, SEARCH_PANE);
contentPane.add(toolbarPane, TOOLBAR_PANE);
cardLayout.show(contentPane, TOOLBAR_PANE);
}
private void initSearchPane() {
searchPane = new JPanel(FRGUIPaneFactory.createBorderLayout());
searchPane.setBorder(BorderFactory.createLineBorder(UIConstants.TOOLBAR_BORDER_COLOR));
searchPane.setBackground(Color.WHITE);
// 左侧搜索图标
UILabel searchLabel = new UILabel(IconUtils.readIcon("/com/fr/design/images/data/search"));
searchLabel.setBorder(BorderFactory.createEmptyBorder(0, 12, 0, 0));
searchLabel.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
// do nothing
}
});
// 中间输入框
searchTextField = new UITextField(){
@Override
public Insets getInsets() {
return new Insets(2, 4, 0, 4);
}
};
searchTextField.setBorderPainted(false);
searchTextField.setPlaceholder(Toolkit.i18nText("Fine-Design_Tree_Search_Press_Enter_For_Search"));
searchTextField.addFocusListener(new FocusListener() {
@Override
public void focusGained(FocusEvent e) {
searchPane.setBorder(BorderFactory.createLineBorder(UIConstants.NORMAL_BLUE));
searchPane.repaint();
}
@Override
public void focusLost(FocusEvent e) {
searchPane.setBorder(BorderFactory.createLineBorder(UIConstants.TOOLBAR_BORDER_COLOR));
searchPane.repaint();
}
});
this.searchTextField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
}
@Override
public void removeUpdate(DocumentEvent e) {
dealWithTextChange();
}
@Override
public void changedUpdate(DocumentEvent e) {
}
});
this.searchTextField.addKeyListener(enterPressed);
// 右侧返回图标
UILabel returnLabel = new UILabel(IconUtils.readIcon("/com/fr/design/images/data/clear"));
returnLabel.setToolTipText(Toolkit.i18nText("Fine-Design_Tree_Search_Return"));
returnLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 11));
returnLabel.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
TableDataTreeSearchManager.getInstance().outOfSearchMode();
}
});
searchPane.add(searchLabel, BorderLayout.WEST);
searchPane.add(searchTextField, BorderLayout.CENTER);
searchPane.add(returnLabel, BorderLayout.EAST);
}
private void dealWithTextChange() {
if (StringUtils.isEmpty(searchTextField.getText()) && TableDataTreeSearchManager.getInstance().isInSearchMode()) {
// 如果是搜索模式下,看作是用户删除输入框文字,仅复原TableDataTreePane
TableDataTreeSearchManager.getInstance().restoreTreePane();
TableDataTreePane.getInstance(DesignModelAdapter.getCurrentModelAdapter()).refreshDockingView();
}
}
private void initToolbarPane() {
toolbarPane = new JPanel();
toolbarPane.setLayout(FRGUIPaneFactory.createBorderLayout());
toolbarPane.add(toolbar, BorderLayout.CENTER);
}
/**
* 交换当前面板层级
*/
public void switchPane(String name) {
cardLayout.show(contentPane, name);
}
public void setPlaceHolder(String placeHolder) {
this.searchTextField.setPlaceholder(placeHolder);
}
/**
* 根据搜索状态变化来调整自身面板的显示
*
* @param event
*/
@Override
public void updateTreeSearchChange(TreeSearchStatusChangeEvent event) {
TreeSearchStatus treeSearchStatus = event.getTreeSearchStatus();
if (treeSearchStatus == TreeSearchStatus.NOT_IN_SEARCH_MODE) {
this.searchTextField.setText(StringUtils.EMPTY);
switchPane(TOOLBAR_PANE);
} else {
switchPane(SEARCH_PANE);
}
}
}

38
designer-base/src/main/java/com/fr/design/data/datapane/management/search/searcher/TableDataSearchMode.java

@ -0,0 +1,38 @@
package com.fr.design.data.datapane.management.search.searcher;
/**
* 搜索模式
*
* @author Yvan
*/
public enum TableDataSearchMode {
/**
* 搜索模板数据集
*/
TEMPLATE_TABLE_DATA(0),
/**
* 搜索服务器数据集
*/
SERVER_TABLE_DATA(1);
private final int mode;
TableDataSearchMode(int mode) {
this.mode = mode;
}
public int getMode() {
return mode;
}
public static TableDataSearchMode match(int mode) {
for (TableDataSearchMode searchMode : TableDataSearchMode.values()) {
if (searchMode.getMode() == mode) {
return searchMode;
}
}
return TEMPLATE_TABLE_DATA;
}
}

148
designer-base/src/main/java/com/fr/design/data/datapane/management/search/searcher/TableDataTreeSearcher.java

@ -0,0 +1,148 @@
package com.fr.design.data.datapane.management.search.searcher;
import com.fr.concurrent.NamedThreadFactory;
import com.fr.data.TableDataSource;
import com.fr.design.data.DesignTableDataManager;
import com.fr.design.data.datapane.management.search.TableDataTreeSearchManager;
import com.fr.design.data.datapane.management.search.control.common.TableDataSearchCallBack;
import com.fr.design.data.datapane.management.search.control.common.TableDataSearchTask;
import com.fr.design.data.datapane.management.search.control.pre.TableDataPreSearchCallBack;
import com.fr.design.data.datapane.management.search.control.pre.TableDataPreSearchTask;
import com.fr.design.data.tabledata.wrapper.TableDataWrapper;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author Yvan
*/
public class TableDataTreeSearcher implements TreeSearcher {
private ExecutorService executorService;
private Map<String, TableDataWrapper> allWrappers = new ConcurrentHashMap<>();
private final Set<String> calculatedSets = new HashSet<>();
private final Set<String> notCalculatedSets = new HashSet<>();
private final Set<String> matchSets = new HashSet<>();
private final Set<String> canExpandSets = new HashSet<>();
public TableDataTreeSearcher() {
}
public boolean isMatchSetsEmpty() {
return matchSets.isEmpty();
}
public int getAllWrappersSize() {
return allWrappers.size();
}
public synchronized void addToCalculatedSets(List<String> tableDataNames) {
for (String tableDataName : tableDataNames) {
TableDataWrapper tableDataWrapper = allWrappers.get(tableDataName);
if (tableDataWrapper == null) {
return;
}
calculatedSets.add(tableDataName);
}
}
public synchronized void addToMatchSets(List<String> matchNodeNames) {
matchSets.addAll(matchNodeNames);
}
public synchronized void addToCanExpandSets(List<String> canExpandNodeNames) {
canExpandSets.addAll(canExpandNodeNames);
}
/**
* 正式搜索前预加载一下数据集列名
*
* @param searchMode
* @param tableDataSource
*/
public void beforeSearch(TableDataSearchMode searchMode, TableDataSource tableDataSource) {
executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new NamedThreadFactory(TableDataTreeSearcher.class));
collectTableDataWrappers(searchMode, tableDataSource);
preCalculateColumns();
}
/**
* 预先对数据集进行取列名的操作提升用户搜索体验
*/
private void preCalculateColumns() {
for (String notCalculatedSet : notCalculatedSets) {
TableDataWrapper tableDataWrapper = allWrappers.get(notCalculatedSet);
if (TableDataTreeSearchManager.getInstance().getTreeSearchStatus() == TreeSearchStatus.SEARCH_NOT_BEGIN) {
executorService.execute(new TableDataPreSearchTask(new TableDataPreSearchCallBack(this), tableDataWrapper));
}
}
}
/**
* 收集下此次搜索需要进行取数的TableDataWrapper
*
* @param searchSubject
* @param tableDataSource
*/
private void collectTableDataWrappers(TableDataSearchMode searchSubject, TableDataSource tableDataSource) {
Map<String, TableDataWrapper> dataSet = searchSubject == TableDataSearchMode.TEMPLATE_TABLE_DATA ?
DesignTableDataManager.getTemplateDataSet(tableDataSource) :
DesignTableDataManager.getGlobalDataSet();
// 转化一下存储过程
Map<String, TableDataWrapper> setIncludingProcedure = DesignTableDataManager.getAllDataSetIncludingProcedure(dataSet);
notCalculatedSets.addAll(setIncludingProcedure.keySet());
allWrappers.putAll(setIncludingProcedure);
}
@Override
public void startSearch(String searchText) {
reset();
for (String notCalculatedSet : notCalculatedSets) {
TableDataWrapper tableDataWrapper = allWrappers.get(notCalculatedSet);
if (TableDataTreeSearchManager.getInstance().getTreeSearchStatus() == TreeSearchStatus.SEARCHING) {
executorService.execute(new TableDataSearchTask(searchText, tableDataWrapper, new TableDataSearchCallBack(this)));
}
}
}
@Override
public void stopSearch() {
}
@Override
public void completeSearch() {
}
private void reset() {
matchSets.clear();
canExpandSets.clear();
calculatedSets.clear();
notCalculatedSets.addAll(allWrappers.keySet());
}
public void afterSearch() {
allWrappers.clear();
executorService.shutdownNow();
}
public boolean nodeMatches(String dsName) {
return matchSets.contains(dsName);
}
public boolean nodeCanExpand(String dsName) {
return canExpandSets.contains(dsName);
}
}

28
designer-base/src/main/java/com/fr/design/data/datapane/management/search/searcher/TreeSearchStatus.java

@ -0,0 +1,28 @@
package com.fr.design.data.datapane.management.search.searcher;
/**
* @author Yvan
*/
public enum TreeSearchStatus {
/**
* 非搜索模式
*/
NOT_IN_SEARCH_MODE,
/**
* 搜索未开始
*/
SEARCH_NOT_BEGIN,
/**
* 搜索中
*/
SEARCHING,
/**
* 搜索已停止
*/
SEARCH_STOPPED,
/**
* 搜索已完成
*/
SEARCH_COMPLETED;
}

27
designer-base/src/main/java/com/fr/design/data/datapane/management/search/searcher/TreeSearcher.java

@ -0,0 +1,27 @@
package com.fr.design.data.datapane.management.search.searcher;
/**
* 用于搜索RefreshableJTree数据的搜索器
*
* @author Yvan
*/
public interface TreeSearcher {
/**
* 开始搜索
*
* @param text
*/
void startSearch(String text);
/**
* 停止搜索
*/
void stopSearch();
/**
* 搜索完成
*/
void completeSearch();
}

82
designer-base/src/main/java/com/fr/design/data/datapane/management/search/view/TreeSearchRendererHelper.java

@ -0,0 +1,82 @@
package com.fr.design.data.datapane.management.search.view;
import com.fr.design.data.datapane.TableDataTree;
import javax.swing.JTree;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeCellRenderer;
import java.awt.Component;
import java.util.regex.Pattern;
/**
* @author Yvan
*/
public class TreeSearchRendererHelper {
/**
* 缓存下原来的渲染器
*/
private TreeCellRenderer originTreeCellRenderer;
public TreeSearchRendererHelper() {
}
public TreeCellRenderer getOriginTreeCellRenderer() {
return originTreeCellRenderer;
}
public void setOriginTreeCellRenderer(TreeCellRenderer originTreeCellRenderer) {
this.originTreeCellRenderer = originTreeCellRenderer;
}
public void replaceTreeRenderer(TableDataTree tableDataTree, String searchText) {
tableDataTree.setCellRenderer(getNewTreeCellRenderer(searchText));
}
public void save(TableDataTree tableDataTree) {
if (getOriginTreeCellRenderer() == null) {
setOriginTreeCellRenderer(tableDataTree.getTableDataTreeCellRenderer());
}
}
public void restore(TableDataTree tableDataTree) {
if (getOriginTreeCellRenderer() != null) {
tableDataTree.setCellRenderer(getOriginTreeCellRenderer());
}
}
/**
* 获取新树渲染器也就是搜索结果树的TreeCellRenderer主要是为了文本高亮
*
* @param searchText
* @return
*/
private TreeCellRenderer getNewTreeCellRenderer(String searchText) {
return new DefaultTreeCellRenderer() {
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
Component treeCellRendererComponent = getOriginTreeCellRenderer().getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
if (treeCellRendererComponent instanceof DefaultTreeCellRenderer) {
DefaultTreeCellRenderer defaultTreeCellRenderer = (DefaultTreeCellRenderer) treeCellRendererComponent;
String text = defaultTreeCellRenderer.getText();
defaultTreeCellRenderer.setText(getHighlightText(text, searchText));
}
return treeCellRendererComponent;
}
};
}
private String getHighlightText(String text, String textToHighlight) {
String highLightTemplate = "<strong>$1</strong>";
if (textToHighlight.length() == 0) {
return text;
}
try {
text = text.replaceAll("(?i)(" + Pattern.quote(textToHighlight) + ")", highLightTemplate);
} catch (Exception e) {
return text;
}
text = "<html>" + text + "</html>";
return text;
}
}

340
designer-base/src/main/java/com/fr/design/data/tabledata/paste/TableDataFollowingPasteUtils.java

@ -0,0 +1,340 @@
package com.fr.design.data.tabledata.paste;
import com.fr.base.TableData;
import com.fr.base.chart.BaseChartCollection;
import com.fr.data.TableDataSource;
import com.fr.design.DesignModelAdapter;
import com.fr.design.data.DesignTableDataManager;
import com.fr.design.data.datapane.TableDataTreePane;
import com.fr.design.data.tabledata.tabledatapane.AbstractTableDataPane;
import com.fr.design.data.tabledata.wrapper.AbstractTableDataWrapper;
import com.fr.design.data.tabledata.wrapper.TableDataWrapper;
import com.fr.design.data.tabledata.wrapper.TemplateTableDataWrapper;
import com.fr.form.FormElementCaseProvider;
import com.fr.form.data.DataBinding;
import com.fr.form.data.DataTableConfig;
import com.fr.form.main.Form;
import com.fr.form.main.WidgetGather;
import com.fr.form.ui.DataControl;
import com.fr.form.ui.DictionaryContainer;
import com.fr.form.ui.ElementCaseEditor;
import com.fr.form.ui.Widget;
import com.fr.form.ui.concept.data.ValueInitializer;
import com.fr.general.ComparatorUtils;
import com.fr.log.FineLoggerFactory;
import com.fr.report.cell.FloatElement;
import com.fr.report.cell.tabledata.ElementUsedTableDataProvider;
import com.fr.report.worksheet.FormElementCase;
import com.fr.stable.StringUtils;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 数据集跟随复制粘贴的工具类
*
* @author Yvan
*/
public class TableDataFollowingPasteUtils {
private static final String UNDERLINE = "_";
/**
* 粘贴所有Map中的tabledata到当前模板
*
* @param tableDataWrapperMap
*/
public static void paste(Map<String, TableData> tableDataWrapperMap) {
if (tableDataWrapperMap == null) {
return;
}
// 获取当前的TableDataTreePane
DesignModelAdapter<?, ?> currentModelAdapter = DesignModelAdapter.getCurrentModelAdapter();
TableDataTreePane tableDataTreePane = (TableDataTreePane) TableDataTreePane.getInstanceWithoutRefreshEverytime(currentModelAdapter);
// 粘贴(添加)数据集
for (Map.Entry<String, TableData> dataWrapperEntry : tableDataWrapperMap.entrySet()) {
String dsName = dataWrapperEntry.getKey();
// 处理名称重复情况
if (isDsNameRepeated(dsName)) {
continue;
}
AbstractTableDataWrapper tableDataWrapper = new TemplateTableDataWrapper(dataWrapperEntry.getValue(), dsName);
AbstractTableDataPane<?> tableDataPane = tableDataWrapper.creatTableDataPane();
tableDataTreePane.addDataPane(tableDataPane, dsName);
}
}
private static boolean isDsNameRepeated(String dsName) {
DesignModelAdapter<?, ?> currentModelAdapter = DesignModelAdapter.getCurrentModelAdapter();
String[] allDSNames = DesignTableDataManager.getAllDSNames(currentModelAdapter.getBook());
return Arrays.stream(allDSNames).anyMatch(name -> StringUtils.equals(name, dsName));
}
/**
* 处理 ElementUsedTableDataProvider从中获取数据集名称 - 数据集Wrapper 的Map
*
* @param providers
* @return
*/
public static Map<String, TableData> transferProvider2TableDataMap(ElementUsedTableDataProvider... providers) {
if (providers == null) {
return new HashMap<>();
}
// 获取当前的所有模板数据集
Map<String, TableDataWrapper> templateTableData = getCurrentTemplateTableDataWrapperIncludingProcedure();
Map<String, TableData> tempMap = new HashMap<>();
try {
for (ElementUsedTableDataProvider tableDataProvider : providers) {
Set<String> usedTableDataNames = tableDataProvider.getElementUsedTableDataNames();
for (String usedTableDataName : usedTableDataNames) {
if (templateTableData.containsKey(usedTableDataName)) {
tempMap.put(usedTableDataName, templateTableData.get(usedTableDataName).getTableData());
}
}
}
// 处理存储过程名称问题
return dealWithStoreProcedure(tempMap);
} catch (Exception e) {
FineLoggerFactory.getLogger().error("transfer widget tabledata failed", e);
}
return new HashMap<>();
}
/**
* 处理结果集将结果集中的存储过程子表替换为原本的存储过程否则跟随粘贴过去的存储过程名称有问题
*
* @param tableDataMap
*/
public static Map<String, TableData> dealWithStoreProcedure(Map<String, TableData> tableDataMap) {
Map<String, TableData> resultMap = new HashMap<>();
if (tableDataMap == null) {
return resultMap;
}
for (Map.Entry<String, TableData> result : tableDataMap.entrySet()) {
String tableDataName = result.getKey();
TableData tableData = result.getValue();
// 判断名称中存在"_"的
if (tableDataName.contains(UNDERLINE)) {
String matchedName = matchTableDataName(tableDataName, tableData);
resultMap.put(matchedName, tableData);
} else {
resultMap.put(tableDataName, tableData);
}
}
return resultMap;
}
/**
* 存储过程子表名称匹配其存储过程数据集名称其余模板数据集名称不变
*
* @param tableDataName 待匹配的数据集名称
* @param tableData
* @return
*/
private static String matchTableDataName(String tableDataName, TableData tableData) {
if (tableDataName == null) {
return null;
}
// 获取不包括存储过程子表的所有TableDataMap
Map<String, TableDataWrapper> dataWrapperMap = getCurrentTemplateTableDataWrapper();
// 名称匹配时,直接返回
if (dataWrapperMap.containsKey(tableDataName)) {
return tableDataName;
}
// 名称不匹配时,判断TableData是否一致
for (Map.Entry<String, TableDataWrapper> dataWrapperEntry : dataWrapperMap.entrySet()) {
String tdName = dataWrapperEntry.getKey();
TableData td = dataWrapperEntry.getValue().getTableData();
if (ComparatorUtils.equals(td, tableData)) {
return tdName;
}
}
return tableDataName;
}
/**
* 提取控件内使用的数据集转化成Map返回
*
* @param widgets
* @return
*/
public static Map<String, TableData> transferWidgetArray2TableDataMap(Widget... widgets) {
if (widgets == null) {
return new HashMap<>();
}
// 获取当前的所有模板数据集
Map<String, TableDataWrapper> templateTableData = getCurrentTemplateTableDataWrapperIncludingProcedure();
Map<String, TableData> tempMap = new HashMap<>();
try {
for (Widget widget : widgets) {
// widget这个接口太大了,布局和子控件互相嵌套,所以只能分情况一个个收集
collectTableDataInDictionary(templateTableData, tempMap, widget);
collectTableDataInWidgetValue(templateTableData, tempMap, widget);
collectTableDataInChartCollection(templateTableData, tempMap, widget);
collectTableDataInElementCaseEditor(templateTableData, tempMap, widget);
}
// 处理存储过程名称问题
return dealWithStoreProcedure(tempMap);
} catch (Exception e) {
FineLoggerFactory.getLogger().error("transfer widget tabledata failed", e);
}
return new HashMap<>();
}
/**
* 收集控件-报表块中使用的数据集
*
* @param templateTableData
* @param tempMap
* @param widget
*/
private static void collectTableDataInElementCaseEditor(Map<String, TableDataWrapper> templateTableData, Map<String, TableData> tempMap, Widget widget) {
Form.traversalWidget(widget, new WidgetGather() {
@Override
public void dealWith(Widget widget) {
ElementCaseEditor elementCaseEditor = (ElementCaseEditor) widget;
FormElementCaseProvider elementCase = elementCaseEditor.getElementCase();
if (elementCase != null) {
// 普通单元格
Iterator cellIterator = elementCase.cellIterator();
while (cellIterator.hasNext()) {
ElementUsedTableDataProvider cellElement = (ElementUsedTableDataProvider) cellIterator.next();
collectElement(cellElement);
}
// 悬浮元素
Iterator<FloatElement> floatIterator = ((FormElementCase) elementCase).floatIterator();
while (floatIterator.hasNext()) {
ElementUsedTableDataProvider floatElement = floatIterator.next();
collectElement(floatElement);
}
}
}
private void collectElement(ElementUsedTableDataProvider provider) {
Set<String> usedTableDataNames = provider.getElementUsedTableDataNames();
for (String usedTableDataName : usedTableDataNames) {
if (templateTableData.containsKey(usedTableDataName)) {
tempMap.put(usedTableDataName, templateTableData.get(usedTableDataName).getTableData());
}
}
}
@Override
public boolean dealWithAllCards() {
return true;
}
}, ElementCaseEditor.class);
}
/**
* 收集控件-图表中的TableData
*
* @param templateTableData
* @param tempMap
* @param widget
*/
private static void collectTableDataInChartCollection(Map<String, TableDataWrapper> templateTableData, Map<String, TableData> tempMap, Widget widget) {
List<BaseChartCollection> chartCollections = widget.getChartCollections();
for (BaseChartCollection chartCollection : chartCollections) {
Set<String> dataSetNames = chartCollection.getDataSetNames();
for (String dataSetName : dataSetNames) {
if (templateTableData.containsKey(dataSetName)) {
tempMap.put(dataSetName, templateTableData.get(dataSetName).getTableData());
}
}
}
}
/**
* 收集控件值中的TableData
*
* @param templateTableData
* @param tempMap
* @param widget
*/
private static void collectTableDataInWidgetValue(Map<String, TableDataWrapper> templateTableData, Map<String, TableData> tempMap, Widget widget) {
Form.traversalWidget(widget, new WidgetGather() {
@Override
public void dealWith(Widget widget) {
if (((DataControl) widget).getWidgetValue() != null) {
ValueInitializer widgetValue = ((DataControl) widget).getWidgetValue();
Object value = widgetValue.getValue();
if (value instanceof DataBinding) {
String dataSourceName = ((DataBinding) value).getDataSourceName();
if (templateTableData.containsKey(dataSourceName)) {
tempMap.put(dataSourceName, templateTableData.get(dataSourceName).getTableData());
}
}
if (value instanceof DataTableConfig) {
String tableDataName = ((DataTableConfig) value).getTableDataName();
if (templateTableData.containsKey(tableDataName)) {
tempMap.put(tableDataName, templateTableData.get(tableDataName).getTableData());
}
}
}
}
@Override
public boolean dealWithAllCards() {
return true;
}
}, DataControl.class);
}
/**
* 收集控件-数据字典中的TableData
*
* @param templateTableData
* @param tempMap
* @param widget
*/
private static void collectTableDataInDictionary(Map<String, TableDataWrapper> templateTableData, Map<String, TableData> tempMap, Widget widget) {
Form.traversalWidget(widget, new WidgetGather() {
@Override
public void dealWith(Widget widget) {
Set<String> usedTableDataSets = ((DictionaryContainer) widget).getUsedTableDataSets();
for (String usedTableDataSet : usedTableDataSets) {
if (templateTableData.containsKey(usedTableDataSet)) {
tempMap.put(usedTableDataSet, templateTableData.get(usedTableDataSet).getTableData());
}
}
}
@Override
public boolean dealWithAllCards() {
return true;
}
}, DictionaryContainer.class);
}
/**
* 获取当前所有的模板数据集包括存储过程
*
* @return
*/
private static Map<String, TableDataWrapper> getCurrentTemplateTableDataWrapperIncludingProcedure() {
Map<String, TableDataWrapper> templateTableDataWrapper = getCurrentTemplateTableDataWrapper();
// 处理存储过程
Map<String, TableDataWrapper> dataWrapperMap = DesignTableDataManager.getAllDataSetIncludingProcedure(templateTableDataWrapper);
return dataWrapperMap;
}
/**
* 获取当前所有的模板数据集不包括存储过程
*
* @return
*/
private static Map<String, TableDataWrapper> getCurrentTemplateTableDataWrapper() {
TableDataSource tableDataSource = DesignTableDataManager.getEditingTableDataSource();
List<Map<String, TableDataWrapper>> editingDataSet = DesignTableDataManager.getEditingDataSet(tableDataSource);
Map<String, TableDataWrapper> templeteDataSet = editingDataSet.get(0);
return templeteDataSet;
}
}

109
designer-base/src/main/java/com/fr/design/data/tabledata/wrapper/StoreProcedureDataWrapper.java

@ -63,26 +63,27 @@ public final class StoreProcedureDataWrapper implements TableDataWrapper {
public StoreProcedureDataWrapper(Component component, StoreProcedure storeProcedure, String storeprocedureName, String dsName) {
this(component, storeProcedure, storeprocedureName, dsName, true);
}
/**
* @param: component loadingBar的父弹框如果不设置父弹框的话可能出现loadingBar隐藏在一个弹框后的情况
* @param: storeProcedure 存储过程
* @param: storeprocedureName 存储过程的名字(某些情况下可以为空)
* @param: dsName 存储过程一个返回数据集的名字
* @param: needLoad 是否要加载
* @param component loadingBar的父弹框如果不设置父弹框的话可能出现loadingBar隐藏在一个弹框后的情况
* @param storeProcedure 存储过程
* @param storeprocedureName 存储过程的名字(某些情况下可以为空)
* @param dsName 存储过程一个返回数据集的名字
* @param needLoad 是否要加载
**/
public StoreProcedureDataWrapper(Component component, StoreProcedure storeProcedure, String storeprocedureName, String dsName, boolean needLoad) {
this.dsName = dsName;
this.storeProcedure = storeProcedure;
this.storeProcedure.setCalculating(false);
this.storeprocedureName = storeprocedureName;
if (needLoad) {
setWorker();
}
if (component == null) {
component = new JFrame();
}
if (needLoad) {
setWorker(component);
}
loadingBar = new AutoProgressBar(component, Toolkit.i18nText("Fine-Design_Basic_Loading_Data"), "", 0, 100) {
@Override
public void doMonitorCanceled() {
getWorker().cancel(true);
}
@ -93,16 +94,17 @@ public final class StoreProcedureDataWrapper implements TableDataWrapper {
* 数据集执行结果返回的所有字段
*
* @return 数据集执行结果返回的所有字段
*
*
* @date 2014-12-3-下午7:43:17
*
*/
* @date 2014-12-3-下午7:43:17
*/
@Override
public List<String> calculateColumnNameList() {
if (columnNameList != null) {
return columnNameList;
}
if (!createStore(false)) {
try {
createStore(false);
} catch (Exception e) {
FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), Toolkit.i18nText("Fine-Design_Basic_Engine_No_TableData"));
return new ArrayList<String>();
}
@ -114,11 +116,9 @@ public final class StoreProcedureDataWrapper implements TableDataWrapper {
* 生成子节点
*
* @return 节点数组
*
*
* @date 2014-12-3-下午7:06:47
*
*/
* @date 2014-12-3-下午7:06:47
*/
@Override
public ExpandMutableTreeNode[] load() {
List<String> namelist;
if (storeProcedure.isCalculating()) {
@ -134,23 +134,16 @@ public final class StoreProcedureDataWrapper implements TableDataWrapper {
return res;
}
private boolean createStore(boolean needLoadingBar) {
try {
dataModels = DesignTableDataManager.createLazyDataModel(storeProcedure, needLoadingBar);
if (dataModels == null || dataModels.length == 0) {
return false;
}
for (int i = 0; i < dataModels.length; i++) {
if (ComparatorUtils.equals(this.dsName, storeprocedureName + "_" + dataModels[i].getName())) {
procedureDataModel = dataModels[i];
private void createStore(boolean needLoadingBar) throws Exception {
dataModels = DesignTableDataManager.createLazyDataModel(storeProcedure, needLoadingBar);
if (dataModels != null && dataModels.length != 0) {
for (ProcedureDataModel dataModel : dataModels) {
if (ComparatorUtils.equals(this.dsName, storeprocedureName + "_" + dataModel.getName())) {
procedureDataModel = dataModel;
break;
}
}
return true;
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
return false;
}
@Override
@ -159,17 +152,15 @@ public final class StoreProcedureDataWrapper implements TableDataWrapper {
}
/**
* 预览数据
*
* @param previewModel 预览模式, 全部还是一个
*
*
* @date 2014-12-3-下午7:05:50
*
*/
* 预览数据
*
* @param previewModel 预览模式, 全部还是一个
* @date 2014-12-3-下午7:05:50
*/
public void previewData(final int previewModel) {
this.previewModel = previewModel;
connectionBar = new AutoProgressBar(new JFrame(), Toolkit.i18nText("Fine-Design_Basic_Utils_Now_Create_Connection"), "", 0, 100) {
@Override
public void doMonitorCanceled() {
connectionBar.close();
worker.cancel(true);
@ -178,8 +169,9 @@ public final class StoreProcedureDataWrapper implements TableDataWrapper {
worker.execute();
}
private void setWorker() {
private void setWorker(final Component parent) {
worker = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
loadingBar.close();
PreviewTablePane.resetPreviewTable();
@ -195,6 +187,7 @@ public final class StoreProcedureDataWrapper implements TableDataWrapper {
return null;
}
@Override
public void done() {
try {
get();
@ -206,13 +199,15 @@ public final class StoreProcedureDataWrapper implements TableDataWrapper {
case StoreProcedureDataWrapper.PREVIEW_ONE:
previewData();
break;
default:
break;
}
} catch (Exception e) {
loadingBar.close();
if (!(e instanceof CancellationException)) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), e.getMessage());
FineJOptionPane.showMessageDialog(parent, e.getMessage());
}
loadingBar.close();
}
}
};
@ -227,10 +222,9 @@ public final class StoreProcedureDataWrapper implements TableDataWrapper {
/**
* 预览返回的一个数据集
*
*
* @date 2014-12-3-下午7:42:53
*
*/
* @date 2014-12-3-下午7:42:53
*/
@Override
public void previewData() {
previewData(-1, -1);
}
@ -240,13 +234,11 @@ public final class StoreProcedureDataWrapper implements TableDataWrapper {
/**
* 预览返回的一个数据集带有显示值和实际值的标记结果
*
* @param keyIndex 实际值
* @param valueIndex 显示值
*
*
* @date 2014-12-3-下午7:42:27
*
*/
* @param keyIndex 实际值
* @param valueIndex 显示值
* @date 2014-12-3-下午7:42:27
*/
@Override
public void previewData(final int keyIndex, final int valueIndex) {
PreviewTablePane.previewStoreData(procedureDataModel, keyIndex, valueIndex);
}
@ -257,7 +249,9 @@ public final class StoreProcedureDataWrapper implements TableDataWrapper {
*/
public void previewAllTable() {
if (procedureDataModel == null) {
if (!createStore(true)) {
try {
createStore(true);
} catch (Exception e) {
return;
}
}
@ -269,6 +263,7 @@ public final class StoreProcedureDataWrapper implements TableDataWrapper {
return dsName;
}
@Override
public TableData getTableData() {
return storeProcedure;
}
@ -282,10 +277,12 @@ public final class StoreProcedureDataWrapper implements TableDataWrapper {
*
* @return 是否异常
*/
@Override
public boolean isUnusual() {
return false;
}
@Override
public boolean equals(Object obj) {
return obj instanceof StoreProcedureDataWrapper
&& ComparatorUtils.equals(this.dsName, ((StoreProcedureDataWrapper) obj).getTableDataName())

4
designer-base/src/main/java/com/fr/design/env/LocalDesignerWorkspaceInfo.java vendored

@ -1,8 +1,6 @@
package com.fr.design.env;
import com.fr.general.ComparatorUtils;
import com.fr.general.GeneralUtils;
import com.fr.locale.InterProviderFactory;
import com.fr.stable.CoreConstants;
import com.fr.stable.StableUtils;
import com.fr.stable.StringUtils;
@ -10,8 +8,8 @@ import com.fr.stable.project.ProjectConstants;
import com.fr.stable.xml.XMLPrintWriter;
import com.fr.stable.xml.XMLableReader;
import com.fr.workspace.connect.WorkspaceConnectionInfo;
import com.fr.workspace.engine.exception.MainVersionNotMatchException;
import java.io.File;
import java.util.Properties;

21
designer-base/src/main/java/com/fr/design/extra/WebViewDlgHelper.java

@ -1,6 +1,5 @@
package com.fr.design.extra;
import com.fr.base.FRContext;
import com.fr.design.dialog.BasicPane;
import com.fr.design.dialog.FineJOptionPane;
import com.fr.design.dialog.UIDialog;
@ -15,12 +14,12 @@ import com.fr.general.IOUtils;
import com.fr.general.http.HttpToolbox;
import com.fr.json.JSONObject;
import com.fr.log.FineLoggerFactory;
import com.fr.plugin.PluginStoreConfig;
import com.fr.plugin.PluginStoreConstants;
import com.fr.stable.CommonUtils;
import com.fr.stable.EnvChangedListener;
import com.fr.stable.ProductConstants;
import com.fr.stable.StableUtils;
import com.fr.stable.StringUtils;
import javax.swing.JOptionPane;
import javax.swing.SwingWorker;
@ -44,15 +43,14 @@ public class WebViewDlgHelper {
private static final String LATEST = "latest";
private static final String SHOP_SCRIPTS = "shop_scripts";
private static final int VERSION_8 = 8;
private static String installHome = FRContext.getCommonOperator().getWebRootPath();
private static String installHome = PluginStoreConstants.getLocalInstallHome();
private static final String MAIN_JS_PATH = "/scripts/plugin.html";
private static final String ENV_VERSION = "ENV_VERSION";
static {
GeneralContext.addEnvChangedListener(new EnvChangedListener() {
@Override
public void envChanged() {
installHome = FRContext.getCommonOperator().getWebRootPath();
installHome = PluginStoreConstants.getLocalInstallHome();
}
});
}
@ -74,8 +72,11 @@ public class WebViewDlgHelper {
}
return;
}
String jar_version = PluginStoreConstants.getProps(ENV_VERSION, StringUtils.EMPTY);
if (ComparatorUtils.equals(jar_version, ProductConstants.VERSION)) {
// 检测更新前先刷新一下版本号
PluginStoreConstants.refreshProps();
String jarVersion = PluginStoreConfig.getInstance().getEnvVersion();
if (ComparatorUtils.equals(jarVersion, ProductConstants.VERSION)) {
updateShopScripts(SHOP_SCRIPTS);
showPluginDlg();
} else {
@ -133,7 +134,7 @@ public class WebViewDlgHelper {
*
* @param filePath 待删除文件路径
*/
private static void deleteExtraFile(String filePath){
private static void deleteExtraFile(String filePath) {
CommonIOUtils.deleteFile(new File(filePath));
}
@ -259,7 +260,7 @@ public class WebViewDlgHelper {
try {
if (get()) {
File scriptZip = new File(StableUtils.pathJoin(PluginConstants.DOWNLOAD_PATH, PluginConstants.TEMP_FILE));
if(scriptZip.exists()){
if (scriptZip.exists()) {
IOUtils.unzip(scriptZip, installHome);
CommonUtils.deleteFile(scriptZip);
}
@ -283,7 +284,7 @@ public class WebViewDlgHelper {
protected Void doInBackground() throws Exception {
String url = CloudCenter.getInstance().acquireUrlByKind("shop.plugin.update");
if (url != null) {
String text = HttpToolbox.get(url + "?" + PluginUtils.FR_VERSION + "=" + ProductConstants.VERSION + "&version=" + PluginStoreConstants.getProps("VERSION"));
String text = HttpToolbox.get(url + "?" + PluginUtils.FR_VERSION + "=" + ProductConstants.VERSION + "&version=" + PluginStoreConfig.getInstance().getVersion());
JSONObject resultJSONObject = new JSONObject(text);
String isLatest = resultJSONObject.optString("result");
if (!ComparatorUtils.equals(isLatest, LATEST)) {

24
designer-base/src/main/java/com/fr/design/file/HistoryTemplateListCache.java

@ -24,7 +24,6 @@ import com.fr.stable.CoreConstants;
import com.fr.stable.StringUtils;
import com.fr.third.org.apache.commons.io.FilenameUtils;
import javax.swing.SwingWorker;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@ -44,7 +43,6 @@ public class HistoryTemplateListCache implements CallbackEvent {
private static final int DEAD_LINE = DesignerEnvManager.getEnvManager().getCachingTemplateLimit();
private List<JTemplate<?, ?>> historyList;
private JTemplate<?, ?> editingTemplate;
private SwingWorker<Boolean, Void> stashWorker;
public static HistoryTemplateListCache getInstance() {
return Holder.INSTANCE;
@ -342,14 +340,7 @@ public class HistoryTemplateListCache implements CallbackEvent {
* @see HistoryTemplateListCache#load()
*/
public void stash() {
stashWorker = new SwingWorker<Boolean, Void>() {
@Override
protected Boolean doInBackground() throws Exception {
_stash();
return true;
}
};
stashWorker.execute();
_stash();
}
private void _stash() {
@ -370,16 +361,6 @@ public class HistoryTemplateListCache implements CallbackEvent {
FineLoggerFactory.getLogger().info("Env Change Template Stashed.");
}
private boolean checkStash() {
try {
return stashWorker.get();
} catch (Exception e) {
FineLoggerFactory.getLogger().debug(e.getMessage(), e);
return false;
}
}
/**
* 切换环境前将正在编辑的模板暂存起来后在新环境重新读取一遍
* <p>
@ -388,9 +369,6 @@ public class HistoryTemplateListCache implements CallbackEvent {
* @see HistoryTemplateListCache#stash()
*/
public void load() {
if (!checkStash()) {
return;
}
FineLoggerFactory.getLogger().info("Env Change Template Loading...");
if (stashFILEMap != null && stashFILEMap.size() != 0) {
int size = historyList.size();

22
designer-base/src/main/java/com/fr/design/formula/FormulaPane.java

@ -14,6 +14,7 @@ import com.fr.design.constants.UIConstants;
import com.fr.design.dialog.BasicDialog;
import com.fr.design.dialog.BasicPane;
import com.fr.design.dialog.DialogActionAdapter;
import com.fr.design.dialog.DialogActionListener;
import com.fr.design.dialog.FineJOptionPane;
import com.fr.design.file.HistoryTemplateListCache;
import com.fr.design.gui.autocomplete.AutoCompleteExtraRefreshComponent;
@ -63,6 +64,7 @@ import com.fr.stable.script.Node;
import com.fr.stable.script.Tiny;
import com.fr.stable.script.TinyHunter;
import java.awt.Window;
import javax.swing.BorderFactory;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
@ -736,6 +738,13 @@ public class FormulaPane extends BasicPane implements KeyListener, UIFormula {
return com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_FormulaD_Formula_Definition");
}
public BasicDialog showLargeWindow(Window window, DialogActionListener l) {
BasicDialog basicDialog = super.showWindowWithCustomSize(window, l, new Dimension(900, 600));
basicDialog.setMinimumSize(new Dimension(900, 600));
basicDialog.setResizable(true);
return basicDialog;
}
/**
* Populate
*/
@ -1102,6 +1111,7 @@ public class FormulaPane extends BasicPane implements KeyListener, UIFormula {
private void initGroupTypeModel() {
functionTypeListModel.addElement(FunctionConstants.COMMON);
functionTypeListModel.addElement(FunctionConstants.NEW);
for (int i = 0; i < FunctionConstants.EMBFUNCTIONS.length; i++) {
functionTypeListModel.addElement(FunctionConstants.EMBFUNCTIONS[i]);
}
@ -1186,9 +1196,6 @@ public class FormulaPane extends BasicPane implements KeyListener, UIFormula {
descriptionTextArea = new UITextArea();
descriptionTextArea.setPreferredSize(new Dimension(350, 200));
UIScrollPane desScrollPane = new UIScrollPane(descriptionTextArea);
desScrollPane.setBorder(null);
this.add(this.createNamePane(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_FormulaPane_Formula_Description") + ":", desScrollPane), BorderLayout.EAST);
descriptionTextArea.setBackground(Color.white);
descriptionTextArea.setLineWrap(true);
descriptionTextArea.setWrapStyleWord(true);
@ -1256,12 +1263,13 @@ public class FormulaPane extends BasicPane implements KeyListener, UIFormula {
private void initVariablesTree() {
JPanel panel = FRGUIPaneFactory.createBorderLayout_S_Pane();
// vairable.
variablesTree = new JTree();
UIScrollPane variablesTreePane = new UIScrollPane(variablesTree);
variablesTreePane.setBorder(new UIRoundedBorder(UIConstants.LINE_COLOR, 1, UIConstants.ARC));
this.add(this.createNamePane(
com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_FormulaPane_Variables") + ":", variablesTreePane), BorderLayout.CENTER);
panel.add(this.createNamePane(
com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_FormulaPane_Variables") + ":", variablesTreePane), BorderLayout.WEST);
variablesTree.setRootVisible(false);
variablesTree.setShowsRootHandles(true);
variablesTree.addMouseListener(applyTextMouseListener);
@ -1269,7 +1277,11 @@ public class FormulaPane extends BasicPane implements KeyListener, UIFormula {
initDescriptionTextArea();
UIScrollPane desScrollPane = new UIScrollPane(descriptionTextArea);
desScrollPane.setBorder(null);
panel.add(this.createNamePane(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_FormulaPane_Formula_Description") + ":", desScrollPane), BorderLayout.CENTER);
initVariablesTreeSelectionListener();
this.add(panel,BorderLayout.CENTER);
}
private void initComponents() {

34
designer-base/src/main/java/com/fr/design/formula/FunctionConstants.java

@ -5,10 +5,19 @@ import com.fr.function.AVERAGE;
import com.fr.function.CHAR;
import com.fr.function.COUNT;
import com.fr.function.DATE;
import com.fr.function.ENBYSTRNUM;
import com.fr.function.ENDOFMONTH;
import com.fr.function.GCD;
import com.fr.function.GETCHARNUM;
import com.fr.function.ISWORKDAY;
import com.fr.function.LCM;
import com.fr.function.MAX;
import com.fr.function.MIDCHAR;
import com.fr.function.MIN;
import com.fr.function.NUMTOZH;
import com.fr.function.RANGE;
import com.fr.function.SUM;
import com.fr.function.TEXTGETNUM;
import com.fr.function.TIME;
import com.fr.general.ComparatorUtils;
import com.fr.general.GeneralUtils;
@ -36,6 +45,7 @@ import java.util.Comparator;
import java.util.Enumeration;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@ -46,6 +56,9 @@ public final class FunctionConstants {
public static NameAndFunctionList COMMON = getCommonFunctionList();
public static NameAndTypeAndFunctionList[] EMBFUNCTIONS = getEmbededFunctionListArray();
public static FunctionGroup ALL = getAllFunctionGroup();
public static List<String> abandonFormulas = Arrays.asList("CIRCULAR", "CROSSLAYERTOTAL", "HIERARCHY", "LAYERTOTAL");
public static NameAndFunctionList NEW = getNewFunctionList();
private static List<String> shieldedFormulas = Arrays.asList("ENBYSTRNUM","TEXTGETNUM" ,"GETCHARNUM","GCD", "LCM");
static {
loadEmbededFunctions();
@ -54,7 +67,8 @@ public final class FunctionConstants {
/**
* Don't let anyone instantiate this class.
*/
private FunctionConstants() {}
private FunctionConstants() {
}
private static void loadEmbededFunctions() {
String pkgName = "com.fr.function";
@ -88,7 +102,10 @@ public final class FunctionConstants {
Class<?> cls = Class.forName(pkgName + "." + fileName.substring(0, fileName.length() - 6));
if (StableUtils.classInstanceOf(cls, iface)) {
Function inst;
inst = (Function)cls.newInstance();
inst = (Function) cls.newInstance();
if (abandonFormulas.contains(inst.getClass().getSimpleName()) || shieldedFormulas.contains(inst.getClass().getSimpleName())) {
continue;
}
for (NameAndTypeAndFunctionList EMBFUNCTION : EMBFUNCTIONS) {
if (EMBFUNCTION.test(inst)) {
break;
@ -270,6 +287,12 @@ public final class FunctionConstants {
});
}
private static NameAndFunctionList getNewFunctionList() {
return new NameAndFunctionList(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_FormulaD_New"), new Function[]{
new ENDOFMONTH(), new NUMTOZH(), new MIDCHAR(), new ISWORKDAY()
});
}
private static NameAndTypeAndFunctionList[] getEmbededFunctionListArray() {
return new NameAndTypeAndFunctionList[] {
new NameAndTypeAndFunctionList(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_FormulaD_Math_&_Trig"), Function.MATH),
@ -300,11 +323,12 @@ public final class FunctionConstants {
Collections.addAll(all, CUSTOM.getDescriptions());
//hugh:自定义函数分组
Set<Mutable> containers = ExtraClassManager.getInstance().getArray(FunctionDefContainer.MARK_STRING);
if(!containers.isEmpty()){
for(Mutable container : containers){
Collections.addAll(all,createFunctionGroup(((FunctionDefContainer)container)).getDescriptions());
if (!containers.isEmpty()) {
for (Mutable container : containers) {
Collections.addAll(all, createFunctionGroup(((FunctionDefContainer) container)).getDescriptions());
}
}
all = all.stream().filter(n -> !abandonFormulas.contains(n.getName())).collect(Collectors.toList());
Collections.sort(all, new Comparator<NameAndDescription>() {
@Override

2
designer-base/src/main/java/com/fr/design/gui/controlpane/UIControlPane.java

@ -212,7 +212,7 @@ public abstract class UIControlPane extends JControlPane {
}
// 点击"编辑"按钮,弹出面板
class PopupEditDialog extends JDialog {
protected class PopupEditDialog extends JDialog {
private JComponent editPane;
private PopupToolPane popupToolPane;
private static final int WIDTH = 570;

12
designer-base/src/main/java/com/fr/design/gui/controlpane/UIListControlPane.java

@ -34,6 +34,9 @@ public abstract class UIListControlPane extends UIControlPane implements ListCon
private CommonShortCutHandlers commonHandlers;
private ListControlPaneHelper helper;
// 目前被触发的事件
private ListDataEvent currentEvent;
public UIListControlPane() {
super();
@ -96,7 +99,9 @@ public abstract class UIListControlPane extends UIControlPane implements ListCon
nameableList.getModel().addListDataListener(new ListDataListener() {
@Override
public void intervalAdded(ListDataEvent e) {
setCurrentEvent(e);
saveSettings();
setCurrentEvent(null);
}
@Override
@ -321,4 +326,11 @@ public abstract class UIListControlPane extends UIControlPane implements ListCon
}
public ListDataEvent getCurrentEvent() {
return currentEvent;
}
public void setCurrentEvent(ListDataEvent currentEvent) {
this.currentEvent = currentEvent;
}
}

26
designer-base/src/main/java/com/fr/design/gui/frpane/JTreeControlPane.java

@ -42,14 +42,10 @@ public class JTreeControlPane extends ControlPane {
private JTree tree;
private DefaultTreeModel defaultTreeModel;
boolean isEditor = false;
private UICheckBox isPerformanceFirst;
public JTreeControlPane(NameableCreator[] creators, BasicBeanPane updatePane, boolean isEditor) {
public JTreeControlPane(NameableCreator[] creators, BasicBeanPane updatePane) {
this.initComponents(creators, updatePane);
this.isEditor = isEditor;
}
private void initComponents(NameableCreator[] creators, BasicBeanPane updatePane) {
@ -120,11 +116,7 @@ public class JTreeControlPane extends ControlPane {
if (obj instanceof TreeNodeAttr[]) {
treeNodeAttr = ((TreeNodeAttr[]) obj);
isPerformanceFirst.setSelected(false);
} else if (obj instanceof TreeEditor) {
TreeEditor treeEditor = (TreeEditor) obj;
treeNodeAttr = treeEditor.getTreeNodeAttr();
isPerformanceFirst.setSelected(treeEditor.isPerformanceFirst());
} else if (obj instanceof TreeNodeWrapper) {
}else if (obj instanceof TreeNodeWrapper) {
treeNodeAttr = ((TreeNodeWrapper) obj).getTreeNodeAttrs();
isPerformanceFirst.setSelected(((TreeNodeWrapper) obj).isPerformanceFirst());
}
@ -146,18 +138,8 @@ public class JTreeControlPane extends ControlPane {
}
public NameObject update() {
if (isEditor) {
TreeEditor treeEditor = new TreeEditor();
treeEditor.setTreeNodeAttr(updateTreeNodeAttr());
treeEditor.setPerformanceFirst(isPerformanceFirst.isSelected());
return new NameObject("tree", treeEditor);
} else {
TreeNodeWrapper treeNodeWrapper = new TreeNodeWrapper(isPerformanceFirst.isSelected(), updateTreeNodeAttr());
return new NameObject("tree", treeNodeWrapper);
}
TreeNodeWrapper treeNodeWrapper = new TreeNodeWrapper(isPerformanceFirst.isSelected(), updateTreeNodeAttr());
return new NameObject("tree", treeNodeWrapper);
}
public TreeNodeAttr[] updateTreeNodeAttr() {

158
designer-base/src/main/java/com/fr/design/gui/frpane/TreeSettingPane.java

@ -26,12 +26,18 @@ import java.awt.event.ItemListener;
import java.util.Arrays;
public class TreeSettingPane extends BasicPane implements DataCreatorUI {
/**
* 普通分层构建方式
*/
private JTreeControlPane controlPane;
/**
* 自动构建方式
*/
private JTreeAutoBuildPane autoBuildPane;
/**
* 新的分层构建方式
* 急速分层构建方式
*/
private LayerDataControlPane layerDataControlPane;
@ -43,15 +49,15 @@ public class TreeSettingPane extends BasicPane implements DataCreatorUI {
private static final long serialVersionUID = 1762889323082827111L;
private String[] buildWay = new String[]{com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_DataTable_Build"),
com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Auto_Build"), com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Layer_Build")};
com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Auto_Build"), com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Layer_Build")};
public TreeSettingPane(boolean isEditor) {
this.initComponents(isEditor);
public TreeSettingPane() {
this.initComponents();
}
private void initComponents(boolean isEditor) {
private void initComponents() {
this.setLayout(FRGUIPaneFactory.createBorderLayout());
JPanel buildWayPanel= FRGUIPaneFactory.createMediumHGapFlowInnerContainer_M_Pane();
JPanel buildWayPanel = FRGUIPaneFactory.createMediumHGapFlowInnerContainer_M_Pane();
buildWayPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
UILabel buildWayLabel = new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Build_Way") + " :");
buildWayPanel.add(buildWayLabel);
@ -65,8 +71,8 @@ public class TreeSettingPane extends BasicPane implements DataCreatorUI {
});
buildWayPanel.add(buildBox);
controlPane = new JTreeControlPane(new NameableCreator[] { treeNode },
new TreeDataCardPane(), isEditor);
controlPane = new JTreeControlPane(new NameableCreator[]{treeNode},
new TreeDataCardPane());
autoBuildPane = new JTreeAutoBuildPane();
layerDataControlPane = new LayerDataControlPane();
this.add(buildWayPanel, BorderLayout.NORTH);
@ -108,87 +114,14 @@ public class TreeSettingPane extends BasicPane implements DataCreatorUI {
}
NameableCreator treeNode = new NameObjectCreator(
com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Gradation"),
"/com/fr/design/images/data/source/jdbcTableData.png",
TreeNodeAttr.class);
/**
*
* @param treeEditor
*/
public void populate(TreeEditor treeEditor) {
boolean isAutoBuild = treeEditor.isAutoBuild();
TreeAttr treeAttr = treeEditor.getTreeAttr();
if (treeAttr != null) {
NameObject no = new NameObject("name", treeEditor);
controlPane.populate(no);
}
if (isAutoBuild) {
buildBox.setSelectedIndex(1);
TableDataDictionary dictionary = treeEditor.getDictionary();
autoBuildPane.populate(dictionary);
} else if (treeEditor.isFastLayerBuild()) {
buildBox.setSelectedIndex(0);
java.util.List<LayerConfig> layerConfigList = treeEditor.getLayerConfigs();
LayerConfig[] layerConfigs = new LayerConfig[layerConfigList.size()];
int i = 0;
for (LayerConfig layerConfig : layerConfigList) {
layerConfigs[i++] = layerConfig;
}
this.layerDataControlPane.populate(new NameObject("Tree Layer Data", layerConfigs));
} else {
buildBox.setSelectedIndex(2);
}
}
com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Gradation"),
"/com/fr/design/images/data/source/jdbcTableData.png",
TreeNodeAttr.class);
/**
* 视图树的update
* @return
*/
public TreeEditor updateTreeEditor() {
// NameObject no = this.controlPane.update();
// if (no != null) {
// return ((TreeEditor) no.getObject());
// }
//
// return null;
TreeEditor te = new TreeEditor();
if (buildBox.getSelectedIndex() == 1) {
TableDataDictionary dictionary = this.autoBuildPane.update();
te.setAutoBuild(true);
te.setFastLayerBuild(false);
te.setDictionary(dictionary);
te.setNodeOrDict(dictionary);
} else if (buildBox.getSelectedIndex() == 2) {
te.setAutoBuild(false);
te.setFastLayerBuild(false);
NameObject no = this.controlPane.update();
if (no != null) {
TreeEditor editor = (TreeEditor) no.getObject();
te.setAllowBlank(editor.isAllowBlank());
te.setEnabled(editor.isEnabled());
te.setDirectEdit(editor.isDirectEdit());
te.setErrorMessage(editor.getErrorMessage());
te.setWidgetName(editor.getWidgetName());
te.setVisible(editor.isVisible());
te.setWaterMark(editor.getWaterMark());
te.setRemoveRepeat(editor.isRemoveRepeat());
te.setTreeAttr(editor.getTreeAttr());
te.setTreeNodeAttr(editor.getTreeNodeAttr());
te.setNodeOrDict(editor.getTreeNodeAttr());
te.setPerformanceFirst(editor.isPerformanceFirst());
}
} else {
LayerConfig[] configs = (LayerConfig[]) layerDataControlPane.update().getObject();
te.setAutoBuild(false);
te.setFastLayerBuild(true);
te.setLayerConfigs(Arrays.asList(configs));
}
return te;
}
/**
* 树节点属性的update
*
* @return
*/
public Object updateTreeNodeAttrs() {
@ -207,64 +140,19 @@ public class TreeSettingPane extends BasicPane implements DataCreatorUI {
}
/**
* 下拉树的update
* @return
*/
public TreeComboBoxEditor updateTreeComboBox() {
TreeComboBoxEditor tcb = new TreeComboBoxEditor();
if (buildBox.getSelectedIndex() == 1) {
TableDataDictionary dictionary = this.autoBuildPane.update();
tcb.setAutoBuild(true);
tcb.setFastLayerBuild(false);
tcb.setDictionary(dictionary);
tcb.setNodeOrDict(dictionary);
} else if (buildBox.getSelectedIndex() == 2) {
tcb.setAutoBuild(false);
tcb.setFastLayerBuild(false);
NameObject no = this.controlPane.update();
if (no != null) {
if (no.getObject() instanceof TreeComboBoxEditor) {
return (TreeComboBoxEditor) no.getObject();
}
TreeEditor editor = (TreeEditor) no.getObject();
tcb.setAllowBlank(editor.isAllowBlank());
tcb.setEnabled(editor.isEnabled());
tcb.setDirectEdit(editor.isDirectEdit());
tcb.setErrorMessage(editor.getErrorMessage());
tcb.setWidgetName(editor.getWidgetName());
tcb.setVisible(editor.isVisible());
tcb.setWaterMark(editor.getWaterMark());
tcb.setRemoveRepeat(editor.isRemoveRepeat());
tcb.setTreeAttr(editor.getTreeAttr());
tcb.setTreeNodeAttr(editor.getTreeNodeAttr());
tcb.setNodeOrDict(editor.getTreeNodeAttr());
tcb.setPerformanceFirst(editor.isPerformanceFirst());
}
}else {
LayerConfig[] configs = (LayerConfig[]) layerDataControlPane.update().getObject();
tcb.setAutoBuild(false);
tcb.setFastLayerBuild(true);
tcb.setLayerConfigs(Arrays.asList(configs));
}
return tcb;
}
/**
*
* @param nodeOrDict
*/
public void populate(Object nodeOrDict) {
if(nodeOrDict instanceof TreeNodeAttr[] || nodeOrDict instanceof TreeNodeWrapper) {
if (nodeOrDict instanceof TreeNodeAttr[] || nodeOrDict instanceof TreeNodeWrapper) {
buildBox.setSelectedIndex(2);
NameObject no = new NameObject("name", nodeOrDict);
controlPane.populate(no);
} else if(nodeOrDict instanceof TableDataDictionary) {
} else if (nodeOrDict instanceof TableDataDictionary) {
buildBox.setSelectedIndex(1);
autoBuildPane.populate((TableDataDictionary)nodeOrDict);
} else if (nodeOrDict instanceof NameObject) {
autoBuildPane.populate((TableDataDictionary) nodeOrDict);
} else if (nodeOrDict instanceof LayerConfig[]) {
buildBox.setSelectedIndex(0);
layerDataControlPane.populate((NameObject) nodeOrDict);
layerDataControlPane.populate((LayerConfig[]) nodeOrDict);
}
}
}

40
designer-base/src/main/java/com/fr/design/gui/frpane/tree/layer/config/LayerDataControlPane.java

@ -179,7 +179,7 @@ public class LayerDataControlPane extends ControlPane {
public void actionPerformed(ActionEvent e) {
// TODO remove tree node
int val = FineJOptionPane.showConfirmDialog(DesignerContext.getDesignerFrame(), com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Utils_Are_You_Sure_To_Remove_The_Selected_Item") + "?",
com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Remove"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Remove"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
if (val != JOptionPane.OK_OPTION) {
return;
}
@ -193,36 +193,32 @@ public class LayerDataControlPane extends ControlPane {
}
}
public void populate(NameObject nameObject) {
public void populate(LayerConfig[] layerConfigs) {
// 重新添加tree节点的时候需要remove掉原来的所有子节点
((DefaultMutableTreeNode) defaultTreeModel.getRoot()).removeAllChildren();
if (BEAN_NAME.equals(nameObject.getName())) {
Object obj = nameObject.getObject();
LayerConfig[] layerConfigs = null;
if (obj instanceof LayerConfig[]) {
layerConfigs = ((LayerConfig[]) obj);
}
if (layerConfigs == null) {
return;
}
int count = layerConfigs == null ? 0 : layerConfigs.length;
//将树的层次一层一层的加上去
DefaultMutableTreeNode node4root = (DefaultMutableTreeNode) defaultTreeModel.getRoot();
for (int i = 0; i < count; i++) {
int count = layerConfigs.length;
//将树的层次一层一层的加上去
DefaultMutableTreeNode node4root = (DefaultMutableTreeNode) defaultTreeModel.getRoot();
for (int i = 0; i < count; i++) {
DefaultMutableTreeNode node4add = new DefaultMutableTreeNode(
DefaultMutableTreeNode node4add = new DefaultMutableTreeNode(
new NameObject(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Gradation") + (i + 1), layerConfigs[i].clone()));
node4root.add(node4add);
node4root = node4add;
}
defaultTreeModel.reload();
expandAll(tree, true);
tree.setSelectionRow(0);
node4root.add(node4add);
node4root = node4add;
}
defaultTreeModel.reload();
expandAll(tree, true);
tree.setSelectionRow(0);
}
public NameObject update() {
public LayerConfig[] update() {
return new NameObject(BEAN_NAME, updateLayerDatas());
return updateLayerDatas();
}
private LayerConfig[] updateLayerDatas() {

6
designer-base/src/main/java/com/fr/design/gui/ifilechooser/SwingFileChooser.java

@ -32,7 +32,11 @@ class SwingFileChooser implements FileChooserProvider {
@Override
public File[] getSelectedFiles() {
return fileChooser.getSelectedFiles();
if (ArrayUtils.isNotEmpty(fileChooser.getSelectedFiles())) {
return fileChooser.getSelectedFiles();
} else {
return new File[]{fileChooser.getSelectedFile()};
}
}
@Override

177
designer-base/src/main/java/com/fr/design/gui/ilist/CheckBoxListWithPartialSelect.java

@ -0,0 +1,177 @@
package com.fr.design.gui.ilist;
import com.fr.design.event.StateChangeListener;
import com.fr.design.gui.icheckbox.UICheckBox;
import com.fr.design.gui.itree.checkboxtree.TristateCheckBox;
import com.fr.design.i18n.Toolkit;
import com.fr.design.layout.FRGUIPaneFactory;
import javax.swing.AbstractListModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.ListCellRenderer;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.List;
/**
* 支持全选全不选半选的CheckBoxList面板
* @author Yvan
*/
public class CheckBoxListWithPartialSelect extends JPanel {
private UICheckBox[] dataCheckBoxes;
private TristateCheckBox chooseAllCheckBox;
private UIList dataList;
public CheckBoxListWithPartialSelect (Object[] data) {
this.setLayout(FRGUIPaneFactory.createBorderLayout());
init(data);
this.add(chooseAllCheckBox, BorderLayout.NORTH);
this.add(dataList, BorderLayout.CENTER);
}
private void init(Object[] data) {
// 复选框组
dataCheckBoxes = new UICheckBox[data.length];
for (int i = 0; i < dataCheckBoxes.length; i++) {
dataCheckBoxes[i] = new UICheckBox(transferDataValue2Show(data[i]));
dataCheckBoxes[i].setSelected(true);
}
// UIList
dataList = new UIList(dataCheckBoxes);
dataList.setModel(getListModel());
dataList.setCellRenderer(getListCellRenderer());
// 全选框
chooseAllCheckBox = new TristateCheckBox(Toolkit.i18nText("Fine-Design_Basic_Remove_All_Selected")) {
@Override
protected State getNextState(State current) {
if (current == TristateCheckBox.SELECTED || current == TristateCheckBox.DO_NOT_CARE) {
return TristateCheckBox.NOT_SELECTED;
}
return TristateCheckBox.SELECTED;
}
};
chooseAllCheckBox.setState(TristateCheckBox.SELECTED);
chooseAllCheckBox.setFocusable(false);
chooseAllCheckBox.addStateChangeListener(getChooseAllCheckBoxStateChangeListener());
dataList.addMouseListener(getDataListMouseListener());
}
public List<Object> getSelectedObjects() {
List<Object> seleted = new ArrayList<>();
for (UICheckBox dataCheckBox : dataCheckBoxes) {
if (dataCheckBox.isSelected()) {
seleted.add(dataCheckBox.getText());
}
}
return seleted;
}
protected MouseListener getDataListMouseListener() {
return new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
super.mousePressed(e);
int index = dataList.getSelectedIndex();
if (index < 0) {
return;
}
UICheckBox checkBox = (UICheckBox) dataList.getModel().getElementAt(index);
checkBox.setSelected(!checkBox.isSelected());
//根据CheckBoxes中的选择情况来更新全选框的状态
int selectedCount = calculateSelectedNum();
if (selectedCount == 0) {
chooseAllCheckBox.setState(TristateCheckBox.NOT_SELECTED);
} else if (selectedCount == dataCheckBoxes.length) {
chooseAllCheckBox.setState(TristateCheckBox.SELECTED);
} else {
chooseAllCheckBox.setState(TristateCheckBox.DO_NOT_CARE);
}
dataList.repaint();
}
};
}
/**
* 获取全选框状态改变监听
* @return
*/
protected StateChangeListener getChooseAllCheckBoxStateChangeListener() {
return () -> {
if (chooseAllCheckBox.getState() == TristateCheckBox.DO_NOT_CARE) {
return;
}
boolean isSelected = chooseAllCheckBox.isSelected();
for (int i = 0; i < dataList.getModel().getSize(); i++) {
UICheckBox checkBox = (UICheckBox) dataList.getModel().getElementAt(i);
checkBox.setSelected(isSelected);
}
dataList.repaint();
};
}
/**
* 计算CheckBox的选中情况用来更新全选框的状态
* @return
*/
protected int calculateSelectedNum() {
int count = 0;
for (UICheckBox dataCheckBox : dataCheckBoxes) {
if (dataCheckBox.isSelected()) {
count++;
}
}
return count;
}
/**
* 将传入的Object转化为字符串展示默认调用toString方法
* @param object
* @return
*/
protected String transferDataValue2Show(Object object) {
return object.toString();
}
protected AbstractListModel getListModel() {
return new SelectedListDataModel();
}
protected ListCellRenderer getListCellRenderer() {
return new SelectedListCellRender();
}
private class SelectedListCellRender extends DefaultListCellRenderer {
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, final boolean isSelected, boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
dataCheckBoxes[index] = (UICheckBox) value;
dataCheckBoxes[index].setBackground(list.getBackground());
return dataCheckBoxes[index];
}
}
private class SelectedListDataModel extends AbstractListModel {
@Override
public int getSize() {
return dataCheckBoxes.length;
}
@Override
public Object getElementAt(int index) {
return (index > getSize() - 1 || index < 0) ? null : dataCheckBoxes[index];
}
}
}

40
designer-base/src/main/java/com/fr/design/gui/iprogressbar/ProgressDialog.java

@ -5,7 +5,6 @@ import com.fr.design.dialog.UIDialog;
import com.fr.design.gui.ilable.UILabel;
import com.fr.design.os.impl.SupportOSImpl;
import com.fr.design.utils.DesignUtils;
import com.fr.design.utils.gui.GUICoreUtils;
import com.fr.general.FRFont;
import com.fr.stable.os.support.OSBasedAction;
import com.fr.stable.os.support.OSSupportCenter;
@ -23,9 +22,15 @@ import java.awt.Frame;
* 加载进度弹窗
*/
public class ProgressDialog extends UIDialog {
private JProgressBar progressBar;
protected static final FRFont font = DesignUtils
.getDefaultGUIFont()
.applySize(14)
.applyForeground(new ColorUIResource(333334));
protected JProgressBar progressBar;
private JDialog centerDialog;
private JLabel text;
protected JLabel text;
protected JPanel progressPanel;
public ProgressDialog(Frame parent) {
super(parent);
@ -42,30 +47,37 @@ public class ProgressDialog extends UIDialog {
}
private void initComponent(Frame parent) {
initProgressBar();
initProgressPanel();
initCenterDialog(parent);
}
protected void initCenterDialog(Frame parent) {
centerDialog = new JDialog(this);
centerDialog.setSize(new Dimension(482, 124));
centerDialog.setUndecorated(true);
centerDialog.setLocationRelativeTo(parent);
JPanel panel = new JPanel();
panel.setBorder(new UIProgressBorder(3, UIConstants.DEFAULT_BG_RULER, 14, 46, 47, 37, 47));
panel.setLayout(new BorderLayout(4, 15));
centerDialog.getContentPane().add(progressPanel);
}
protected void initProgressBar() {
progressBar = new JProgressBar();
progressBar.setUI(new ModernUIProgressBarUI());
progressBar.setBorderPainted(false);
progressBar.setOpaque(false);
progressBar.setBorder(null);
progressBar.setMaximum(1000);
panel.add(progressBar, BorderLayout.CENTER);
}
protected void initProgressPanel() {
progressPanel = new JPanel();
progressPanel.setBorder(new UIProgressBorder(3, UIConstants.DEFAULT_BG_RULER, 14, 46, 47, 37, 47));
progressPanel.setLayout(new BorderLayout(4, 15));
progressPanel.add(progressBar, BorderLayout.CENTER);
text = new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Loading_Project"), JLabel.CENTER);
FRFont font = DesignUtils
.getDefaultGUIFont()
.applySize(14)
.applyForeground(new ColorUIResource(333334));
text.setFont(font);
panel.add(text, BorderLayout.SOUTH);
panel.setVisible(true);
centerDialog.getContentPane().add(panel);
progressPanel.add(text, BorderLayout.SOUTH);
progressPanel.setVisible(true);
}
@Override

7
designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/RefreshableJTree.java

@ -154,6 +154,13 @@ public abstract class RefreshableJTree extends CheckBoxTree {
refresh((ExpandMutableTreeNode) this.getModel().getRoot(), childName);
}
/**
* 刷新树用于搜索结果的展示
*/
public void refresh4TreeSearch() {
}
/*
* 刷新expandRoot节点下所有已打开的节点的UserObject,并打开isExpanded为true的TreeNode
*/

5
designer-base/src/main/java/com/fr/design/gui/style/TextFormatPane.java

@ -58,8 +58,9 @@ public class TextFormatPane extends AbstractBasicStylePane implements GlobalName
private static final Integer[] TYPES = new Integer[]{
FormatContents.NULL, FormatContents.NUMBER,
FormatContents.CURRENCY, FormatContents.PERCENT,
FormatContents.SCIENTIFIC, FormatContents.DATE,
FormatContents.TIME, FormatContents.TEXT};
FormatContents.SCIENTIFIC,
FormatContents.DATE, FormatContents.TIME,
FormatContents.TEXT};
private static final Integer[] DATE_TYPES = new Integer[]{FormatContents.NULL, FormatContents.DATE, FormatContents.TIME};

5858
designer-base/src/main/java/com/fr/design/gui/syntax/ui/rsyntaxtextarea/modes/FormulaTokenMaker.java

File diff suppressed because it is too large Load Diff

8
designer-base/src/main/java/com/fr/design/javascript/JSContentPane.java

@ -63,7 +63,9 @@ public class JSContentPane extends BasicPane {
initFunctionTitle(args);
JPanel jsParaPane = createJSParaPane();
addNewPaneLabel();
if (needAdvancedEditor()) {
addNewPaneLabel();
}
this.add(jsParaPane, BorderLayout.NORTH);
UIScrollPane sp = createContentTextAreaPanel();
@ -323,4 +325,8 @@ public class JSContentPane extends BasicPane {
return provider;
}
protected boolean needAdvancedEditor() {
return true;
}
}

26
designer-base/src/main/java/com/fr/design/login/socketio/LoginAuthServer.java

@ -1,5 +1,6 @@
package com.fr.design.login.socketio;
import com.fr.concurrent.NamedThreadFactory;
import com.fr.design.DesignerEnvManager;
import com.fr.design.login.DesignerLoginType;
import com.fr.design.login.bean.BBSAccountLogin;
@ -11,7 +12,11 @@ import com.fr.third.socketio.Configuration;
import com.fr.third.socketio.SocketIOClient;
import com.fr.third.socketio.SocketIOServer;
import com.fr.third.socketio.listener.DataListener;
import java.net.URLDecoder;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @author Lanlan
@ -19,9 +24,11 @@ import java.net.URLDecoder;
* Created by Lanlan on 2021/6/3
*/
public class LoginAuthServer {
private AtomicBoolean started = new AtomicBoolean(false);
private SocketIOServer server;
private static final String HOSTNAME = "localhost";
private static final int PORT = 41925;
@ -47,6 +54,23 @@ public class LoginAuthServer {
}
public void start() {
// 只运行一次,不在乎成不成功
if (started.compareAndSet(false, true)) {
asyncStart();
}
}
public void asyncStart() {
ExecutorService asyncService = Executors.newSingleThreadExecutor(new NamedThreadFactory(LoginAuthServer.class.getName(), true));
asyncService.submit(this::compatibleStart);
asyncService.shutdown();
}
@Deprecated
public void compatibleStart() {
try {
server.start();
} catch (Exception e) {

7
designer-base/src/main/java/com/fr/design/mainframe/BaseJForm.java

@ -10,7 +10,7 @@ import javax.swing.JComponent;
* Date: 13-7-15
* Time: 上午10:28
*/
public interface BaseJForm<T> extends JTemplateProvider<T> {
public interface BaseJForm<T> extends JTemplateProvider<T>, JDashboard {
String XML_TAG = "JForm";
int FORM_TAB = 0;
@ -58,4 +58,9 @@ public interface BaseJForm<T> extends JTemplateProvider<T> {
* @param ecContainer ElementCase所在container
*/
void tabChanged(int index, FormElementCaseContainerProvider ecContainer);
@Override
default void switchToDashBoardEditor() {
tabChanged(FORM_TAB);
}
}

26
designer-base/src/main/java/com/fr/design/mainframe/CenterRegionContainerPane.java

@ -220,7 +220,7 @@ public class CenterRegionContainerPane extends JPanel {
*
* @param plus 工具条中相关信息
*/
protected void resetToolkitByPlus(ToolBarMenuDockPlus plus, ToolBarMenuDock ad) {
protected void resetToolkitByPlus(ToolBarMenuDockPlus plus, ToolBarMenuDock ad, ToolKitConfigStrategy strategy) {
resetCombineUpTooBar(ad.resetUpToolBar(plus), plus);
@ -230,28 +230,40 @@ public class CenterRegionContainerPane extends JPanel {
// 颜色,字体那些按钮的工具栏
toolbarPane.add(toolbarComponent = ad.resetToolBar(toolbarComponent, plus), BorderLayout.CENTER);
if (plus.hasToolBarPane()) {
if (strategy.hasToolBarPane(plus)) {
this.add(toolbarPane, BorderLayout.NORTH);
} else {
this.remove(toolbarPane);
}
if (strategy.hasTemplateTabPane(plus)) {
eastCenterPane.add(templateTabPane, BorderLayout.CENTER);
} else {
eastCenterPane.remove(templateTabPane);
}
if (strategy.hasCombineUp(plus)) {
eastCenterPane.add(combineUp, BorderLayout.NORTH);
} else {
eastCenterPane.remove(combineUp);
}
resetByDesignMode();
}
private void resetByDesignMode() {
if (DesignModeContext.isDuchampMode()) {
eastPane.remove(largeToolbar);
eastCenterPane.remove(templateTabPane);
centerTemplateCardPane.refresh(HistoryTemplateListCache.getInstance().getCurrentEditingTemplate());
//移除新建模板按钮
templateTabPane.remove(newWorkBookPane);
} else {
eastPane.add(largeToolbar, BorderLayout.WEST);
eastCenterPane.add(templateTabPane, BorderLayout.CENTER);
}
templateTabPane.add(newWorkBookPane, BorderLayout.WEST);
}
}
JComponent getToolbarComponent() {
return this.toolbarComponent;

21
designer-base/src/main/java/com/fr/design/mainframe/DefaultToolKitConfig.java

@ -0,0 +1,21 @@
package com.fr.design.mainframe;
import com.fr.design.base.mode.DesignModeContext;
import com.fr.design.mainframe.toolbar.ToolBarMenuDockPlus;
public class DefaultToolKitConfig implements ToolKitConfigStrategy {
@Override
public boolean hasTemplateTabPane(ToolBarMenuDockPlus plus) {
return !DesignModeContext.isDuchampMode();
}
@Override
public boolean hasCombineUp(ToolBarMenuDockPlus plus) {
return plus.hasToolBarPane();
}
@Override
public boolean hasToolBarPane(ToolBarMenuDockPlus plus) {
return plus.hasToolBarPane();
}
}

19
designer-base/src/main/java/com/fr/design/mainframe/DesignerFrame.java

@ -4,6 +4,7 @@
package com.fr.design.mainframe;
import com.fr.base.BaseUtils;
import com.fr.base.OptimizeUtil;
import com.fr.design.DesignModelAdapter;
import com.fr.design.DesignerEnvManager;
import com.fr.design.ExtraDesignClassManager;
@ -42,6 +43,7 @@ import com.fr.design.lock.LockInfoDialog;
import com.fr.event.EventDispatcher;
import com.fr.exception.DecryptTemplateException;
import com.fr.exception.TplLockedException;
import com.fr.exit.ConfigToPropMigrator;
import com.fr.exit.DesignerExiter;
import com.fr.file.FILE;
import com.fr.file.FILEFactory;
@ -369,7 +371,12 @@ public class DesignerFrame extends JFrame implements JTemplateActionListener, Ta
public void fireDesignerOpened() {
for (DesignerOpenedListener listener : designerOpenedListenerList) {
listener.designerOpened();
// 捕获下异常 避免造成启动过程监听触发异常导致设计器闪退
try {
listener.designerOpened();
} catch (Throwable e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
}
designerOpened = true;
@ -614,6 +621,10 @@ public class DesignerFrame extends JFrame implements JTemplateActionListener, Ta
* @param plus 工具条中相关信息
*/
public void resetToolkitByPlus(ToolBarMenuDockPlus plus) {
resetToolkitByPlus(plus, new DefaultToolKitConfig());
}
public void resetToolkitByPlus(ToolBarMenuDockPlus plus, ToolKitConfigStrategy strategy) {
if (plus == null) {
plus = ToolBarMenuDock.NULLAVOID;
@ -621,7 +632,7 @@ public class DesignerFrame extends JFrame implements JTemplateActionListener, Ta
NorthRegionContainerPane.getInstance().resetToolkitByPlus(plus, ad);
CenterRegionContainerPane.getInstance().resetToolkitByPlus(plus, ad);
CenterRegionContainerPane.getInstance().resetToolkitByPlus(plus, ad, strategy);
this.checkToolbarMenuEnable();
this.validate();
@ -1071,6 +1082,10 @@ public class DesignerFrame extends JFrame implements JTemplateActionListener, Ta
DesignerEnvManager.getEnvManager().setLastEastRegionContainerWidth(
EastRegionContainerPane.getInstance().getContainerWidth());
OptimizeUtil.open(() -> {
ConfigToPropMigrator.getInstance().execute();
});
DesignerEnvManager.getEnvManager().saveXMLFile();
}

4
designer-base/src/main/java/com/fr/design/mainframe/DesignerFrameFileDealerPane.java

@ -11,6 +11,7 @@ import com.fr.design.actions.UpdateAction;
import com.fr.design.constants.UIConstants;
import com.fr.design.data.DesignTableDataManager;
import com.fr.design.data.datapane.TableDataTreePane;
import com.fr.design.data.datapane.management.search.TableDataTreeSearchManager;
import com.fr.design.data.tabledata.ResponseDataSourceChange;
import com.fr.design.dialog.FineJOptionPane;
import com.fr.design.file.FileOperations;
@ -244,6 +245,9 @@ public class DesignerFrameFileDealerPane extends JPanel implements FileToolbarSt
DesignModelAdapter.setCurrentModelAdapter(jt.getModel());
fireDSChanged();
if (TableDataTreeSearchManager.getInstance().isInSearchMode()) {
TableDataTreeSearchManager.getInstance().outOfSearchMode();
}
TableDataTreePane.getInstance(DesignModelAdapter.getCurrentModelAdapter());
HistoryTemplateListPane.getInstance().setCurrentEditingTemplate(jt);
//处理自动新建的模板

10
designer-base/src/main/java/com/fr/design/mainframe/JDashboard.java

@ -0,0 +1,10 @@
package com.fr.design.mainframe;
/**
* @author Starryi
* @version 1.0
* Created by Starryi on 2022/3/1
*/
public interface JDashboard {
void switchToDashBoardEditor();
}

19
designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java

@ -65,6 +65,7 @@ import com.fr.design.write.submit.DBManipulationPane;
import com.fr.event.EventDispatcher;
import com.fr.file.FILE;
import com.fr.file.FILEChooserPane;
import com.fr.file.FileFILE;
import com.fr.file.MemFILE;
import com.fr.file.StashedFILE;
import com.fr.form.ui.NoneWidget;
@ -1010,12 +1011,26 @@ public abstract class JTemplate<T extends BaseBook, U extends BaseUndoState<?>>
if (WorkContext.getCurrent().get(LockInfoOperator.class).isTplUnLocked(getEditingFILE().getPath())) {
throw new UnLockedException();
}
// 校验锁定信息是否一致
if (getEditingFILE().exists() && !WorkContext.getCurrent().get(LockInfoOperator.class).isConsistentLock(getEditingFILE().getPath())) {
// 过滤掉本地文件
boolean localFile = getEditingFILE() instanceof FileFILE;
boolean inconsistent = !localFile && getEditingFILE().exists()
&& !WorkContext.getCurrent().get(LockInfoOperator.class).isConsistentLock(getEditingFILE().getPath());
if (inconsistent) {
throw new InconsistentLockException();
}
}
public byte[] exportData() throws Exception {
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
BaseBook target = getTarget();
if (target != null) {
target.export(outputStream);
return outputStream.toByteArray();
}
}
return new byte[0];
}
protected boolean export() throws Exception {
return this.getTarget().export(TemplateResourceManager.getResource().saveTemplate(getEditingFILE()));

27
designer-base/src/main/java/com/fr/design/mainframe/ToolKitConfigStrategy.java

@ -0,0 +1,27 @@
package com.fr.design.mainframe;
import com.fr.design.mainframe.toolbar.ToolBarMenuDockPlus;
public interface ToolKitConfigStrategy {
/**
* 展示tabpane
* @param plus
* @return
*/
boolean hasTemplateTabPane(ToolBarMenuDockPlus plus);
/**
* 展示模板操作按钮复制粘贴保存等
* @param plus
* @return
*/
boolean hasCombineUp(ToolBarMenuDockPlus plus);
/**
* 展示工具栏pane
* @param plus
* @return
*/
boolean hasToolBarPane(ToolBarMenuDockPlus plus);
}

2
designer-base/src/main/java/com/fr/design/mainframe/theme/edit/chart/ChartTitleAndBackgroundStylePane.java

@ -77,7 +77,7 @@ public class ChartTitleAndBackgroundStylePane extends AbstractChartStylePane {
private void initList() {
paneList = new ArrayList<>();
paneList.add(new NullBackgroundQuickPane());
paneList.add(new ColorBackgroundQuickPane());
paneList.add(new ColorBackgroundQuickPane(true));
paneList.add(new ImageBackgroundQuickPane(false));
paneList.add(new VanChartGradientPane());
}

2
designer-base/src/main/java/com/fr/design/mainframe/widget/accessibles/AccessibleTreeModelEditor.java

@ -23,7 +23,7 @@ public class AccessibleTreeModelEditor extends UneditableAccessibleEditor {
@Override
protected void showEditorPane() {
if (treeSettingPane == null) {
treeSettingPane = new TreeSettingPane(false);
treeSettingPane = new TreeSettingPane();
}
BasicDialog dlg = treeSettingPane.showWindow(SwingUtilities.getWindowAncestor(this));
treeSettingPane.populate(getValue());

5
designer-base/src/main/java/com/fr/design/mainframe/widget/wrappers/TreeModelWrapper.java

@ -6,8 +6,7 @@ import com.fr.data.impl.TreeNodeWrapper;
import com.fr.design.Exception.ValidationException;
import com.fr.design.designer.properties.Decoder;
import com.fr.design.designer.properties.Encoder;
import com.fr.general.NameObject;
import com.fr.form.ui.tree.LayerConfig;
import com.fr.stable.StringUtils;
public class TreeModelWrapper implements Encoder, Decoder {
@ -22,7 +21,7 @@ public class TreeModelWrapper implements Encoder, Decoder {
} else if (v instanceof TreeNodeWrapper) {
TreeNodeAttr[] treeNodeAttrs = ((TreeNodeWrapper) v).getTreeNodeAttrs();
return TemplateUtils.render(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Total_N_Grade"), new String[]{"N"}, new String[]{treeNodeAttrs.length + ""});
} else if (v instanceof NameObject) {
} else if (v instanceof LayerConfig[]) {
return com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_DataTable_Build");
} else {
return com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Auto_Build");

45
designer-base/src/main/java/com/fr/design/record/analyzer/DesignerAnalyzer.java

@ -0,0 +1,45 @@
package com.fr.design.record.analyzer;
import com.fr.design.record.analyzer.advice.TimeAdvice;
import com.fr.design.record.analyzer.advice.TrackAdvice;
import com.fr.record.analyzer.AnalyzerConfiguration;
import com.fr.record.analyzer.AnalyzerUnit;
import com.fr.record.analyzer.Assistant;
import com.fr.record.analyzer.Metrics;
import com.fr.record.analyzer.Track;
import com.fr.record.analyzer.configuration.AnalyzerAssemblyFactory;
import com.fr.stable.ArrayUtils;
import com.fr.third.net.bytebuddy.asm.Advice;
import com.fr.third.net.bytebuddy.description.type.TypeDescription;
import com.fr.third.net.bytebuddy.dynamic.DynamicType;
import com.fr.third.net.bytebuddy.matcher.ElementMatchers;
import com.fr.third.net.bytebuddy.utility.JavaModule;
/**
* created by Harrison on 2022/03/08
**/
public class DesignerAnalyzer {
private static final AnalyzerUnit ANALYZER = new AnalyzerUnit();
public static synchronized void init(AnalyzerAssemblyFactory factory, AnalyzerConfiguration... configurations) {
AnalyzerAssemblyFactory redefineFactory = factory.prepare(DesignerAssemblyFactory.getInstance());
AnalyzerConfiguration defaultConfiguration = AnalyzerConfiguration.create(new Assistant() {
@Override
public DynamicType.Builder<?> supply(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) {
return builder
.visit(Advice.to(TimeAdvice.class).on(ElementMatchers.isAnnotatedWith(Metrics.class)))
.visit(Advice.to(TrackAdvice.class).on(ElementMatchers.isAnnotatedWith(Track.class)));
}
});
AnalyzerConfiguration[] allConfigurations = ArrayUtils.add(configurations, defaultConfiguration);
// 准备监听
ANALYZER.setAgentListener(new DesignerAnalyzerListener());
ANALYZER.init(redefineFactory, allConfigurations);
}
}

131
designer-base/src/main/java/com/fr/design/record/analyzer/DesignerAnalyzerActivator.java

@ -0,0 +1,131 @@
package com.fr.design.record.analyzer;
import com.fr.base.OptimizeUtil;
import com.fr.concurrent.NamedThreadFactory;
import com.fr.design.constants.DesignerLaunchStatus;
import com.fr.design.record.analyzer.advice.DBMonitorAdvice;
import com.fr.design.record.analyzer.advice.FaultToleranceAdvice;
import com.fr.design.record.analyzer.advice.FocusAdvice;
import com.fr.design.record.analyzer.advice.MonitorAdvice;
import com.fr.design.record.analyzer.advice.PerformancePointAdvice;
import com.fr.event.Event;
import com.fr.event.EventDispatcher;
import com.fr.event.Listener;
import com.fr.event.Null;
import com.fr.intelli.metrics.Compute;
import com.fr.intelli.record.Focus;
import com.fr.intelli.record.PerformancePoint;
import com.fr.module.Activator;
import com.fr.module.extension.Prepare;
import com.fr.record.analyzer.AnalyzerConfiguration;
import com.fr.record.analyzer.AnalyzerKey;
import com.fr.record.analyzer.DBMetrics;
import com.fr.record.analyzer.FineAnalyzer;
import com.fr.record.analyzer.advice.AnalyzerAdviceKey;
import com.fr.record.analyzer.advice.FineAdviceAssistant;
import com.fr.record.analyzer.configuration.AnalyzerAssemblyFactory;
import com.fr.record.analyzer.configuration.FineAnalyzerAssemblyFactory;
import com.fr.stable.collections.CollectionUtils;
import com.fr.third.net.bytebuddy.matcher.ElementMatchers;
import com.fr.tolerance.FaultTolerance;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.concurrent.ExecutorService;
/**
* created by Harrison on 2022/03/04
**/
public class DesignerAnalyzerActivator extends Activator implements Prepare {
@Override
public void start() {
OptimizeUtil.open(() -> {
AnalyzerAssemblyFactory basicFactory = createBasicFactory();
// 兼容逻辑
List<AnalyzerConfiguration> backwardsConfigurations = findMutableBackwards(AnalyzerKey.KEY);
if (!CollectionUtils.isEmpty(backwardsConfigurations)) {
// 直接初始化,不添加默认值,防止和下面的冲突
FineAnalyzer.initDirectly(basicFactory, backwardsConfigurations.toArray(new AnalyzerConfiguration[0]));
}
// 等页面完全打开后,再进行 retransform, 别影响了启动速度
EventDispatcher.listen(DesignerLaunchStatus.STARTUP_COMPLETE, new Listener<Null>() {
@Override
public void on(Event event, Null param) {
ExecutorService es = newSingleThreadExecutor(new NamedThreadFactory("designer-analyzer", true));
try {
// 加入 retransform 部分的逻辑
List<FineAdviceAssistant> adviceConfigurations = findMutable(AnalyzerAdviceKey.KEY);
if (!CollectionUtils.isEmpty(adviceConfigurations)) {
AnalyzerConfiguration[] configurations = convertConfigurations(adviceConfigurations);
es.submit(() -> {
DesignerAnalyzer.init(basicFactory, configurations);
});
}
} finally {
es.shutdown();
}
}
});
});
}
@NotNull
private AnalyzerConfiguration[] convertConfigurations(List<FineAdviceAssistant> list) {
return list.stream()
.map(AnalyzerConfiguration::create)
.toArray(AnalyzerConfiguration[]::new);
}
@Override
public void stop() {
}
@Override
public void prepare() {
addMutable(AnalyzerAdviceKey.KEY, FineAdviceAssistant.create(
ElementMatchers.isAnnotatedWith(Focus.class),
FocusAdvice.class
));
addMutable(AnalyzerAdviceKey.KEY, FineAdviceAssistant.create(
ElementMatchers.isAnnotatedWith(Compute.class),
MonitorAdvice.class
));
addMutable(AnalyzerAdviceKey.KEY, FineAdviceAssistant.create(
ElementMatchers.isAnnotatedWith(DBMetrics.class),
DBMonitorAdvice.class
));
addMutable(AnalyzerAdviceKey.KEY, FineAdviceAssistant.create(
ElementMatchers.isAnnotatedWith(PerformancePoint.class),
PerformancePointAdvice.class
));
addMutable(AnalyzerAdviceKey.KEY, FineAdviceAssistant.create(
ElementMatchers.isAnnotatedWith(FaultTolerance.class),
FaultToleranceAdvice.class
));
}
private AnalyzerAssemblyFactory createBasicFactory() {
AnalyzerAssemblyFactory factory = findSingleton(AnalyzerAssemblyFactory.class);
FineAnalyzerAssemblyFactory basicFactory = new FineAnalyzerAssemblyFactory();
basicFactory.prepare(factory);
return basicFactory;
}
}

12
designer-base/src/main/java/com/fr/design/record/analyzer/DesignerAnalyzerAdvice.java

@ -0,0 +1,12 @@
package com.fr.design.record.analyzer;
import com.fr.record.analyzer.advice.AnalyzerAdvice;
/**
* 仅作为标志
* 没有方法
*
* created by Harrison on 2022/03/04
**/
public interface DesignerAnalyzerAdvice extends AnalyzerAdvice {
}

23
designer-base/src/main/java/com/fr/design/record/analyzer/DesignerAnalyzerListener.java

@ -0,0 +1,23 @@
package com.fr.design.record.analyzer;
import com.fr.log.FineLoggerFactory;
import com.fr.third.net.bytebuddy.agent.builder.AgentBuilder;
import com.fr.third.net.bytebuddy.description.type.TypeDescription;
import com.fr.third.net.bytebuddy.dynamic.DynamicType;
import com.fr.third.net.bytebuddy.utility.JavaModule;
/**
* created by Harrison on 2022/03/08
**/
public class DesignerAnalyzerListener extends AgentBuilder.Listener.Adapter {
@Override
public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded, DynamicType dynamicType) {
FineLoggerFactory.getLogger().debug("Designer-Analyzer transform successfully:{}", typeDescription);
}
@Override
public void onError(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded, Throwable throwable) {
FineLoggerFactory.getLogger().error("Designer-Analyzer transform error:" + typeName);
}
}

91
designer-base/src/main/java/com/fr/design/record/analyzer/DesignerAssemblyFactory.java

@ -0,0 +1,91 @@
package com.fr.design.record.analyzer;
import com.fr.record.analyzer.configuration.AnalyzerAssemblyFactory;
import com.fr.third.net.bytebuddy.agent.builder.AgentBuilder;
import java.util.List;
import java.util.Map;
/**
* 装配 Agent 为后置启动
* <p>必须在一个线程中处理 retransform 的事务否则会阻塞整个的线程导致效果不佳</p>
*
* created by Harrison on 2022/03/07
**/
public class DesignerAssemblyFactory implements AnalyzerAssemblyFactory<Void> {
/**
* 每次执行 1 class retransform
*/
private static final int FIXED_SIZE = 1;
/**
* 单位 ms
* 每次间隔 500 ms, 执行一次
*/
private static final int DELAY_INTERVAL = 500;
private final AgentBuilder.RedefinitionStrategy.BatchAllocator batchAllocator = AgentBuilder.RedefinitionStrategy.BatchAllocator.ForFixedSize.ofSize(FIXED_SIZE);
private final AgentBuilder.RedefinitionStrategy.Listener redefinitionListener = new DelayListener(DELAY_INTERVAL);
public static DesignerAssemblyFactory getInstance() {
return DesignerAssemblyFactoryHolder.INSTANCE;
}
private static class DesignerAssemblyFactoryHolder {
private static final DesignerAssemblyFactory INSTANCE = new DesignerAssemblyFactory();
}
@Override
public AnalyzerAssemblyFactory<Void> prepare(Void material) {
return this;
}
@Override
public AgentBuilder assembly(AgentBuilder raw) {
return raw.disableClassFormatChanges()
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
// 每次只 transform 一部分否则会导致 UI 变慢
.with(batchAllocator)
.with(redefinitionListener)
.with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
.with(AgentBuilder.TypeStrategy.Default.REDEFINE);
}
private class DelayListener implements AgentBuilder.RedefinitionStrategy.Listener {
/**
* 单位 ms
*/
private final int interval;
public DelayListener(int interval) {
this.interval = interval;
}
/**
* 执行完后等待一段时间再执行
*/
@Override
public void onBatch(int index, List<Class<?>> batch, List<Class<?>> types) {
try {
Thread.sleep(interval);
} catch (Exception ignore) {
}
}
@Override
public Iterable<? extends List<Class<?>>> onError(int index, List<Class<?>> batch, Throwable throwable, List<Class<?>> types) {
return null;
}
@Override
public void onComplete(int amount, List<Class<?>> types, Map<List<Class<?>>, Throwable> failures) {
}
}
}

23
designer-base/src/main/java/com/fr/design/record/analyzer/advice/DBMonitorAdvice.java

@ -0,0 +1,23 @@
package com.fr.design.record.analyzer.advice;
import com.fr.design.record.analyzer.DesignerAnalyzerAdvice;
import com.fr.general.data.DataModel;
import com.fr.measure.DBMeterFactory;
import com.fr.measure.metric.DBMetric;
import com.fr.third.net.bytebuddy.asm.Advice;
import com.fr.third.net.bytebuddy.implementation.bytecode.assign.Assigner;
/**
* created by Harrison on 2022/03/07
**/
public class DBMonitorAdvice implements DesignerAnalyzerAdvice {
@Advice.OnMethodExit(onThrowable = Exception.class)
public static void onMethodExit(@Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] args) {
if (args.length > 1 && args[1] instanceof DataModel) {
DBMetric meter = ((DataModel) args[1]).getMetric();
DBMeterFactory.getMeter().record(meter);
}
}
}

46
designer-base/src/main/java/com/fr/design/record/analyzer/advice/FaultToleranceAdvice.java

@ -0,0 +1,46 @@
package com.fr.design.record.analyzer.advice;
import com.fr.design.record.analyzer.DesignerAnalyzerAdvice;
import com.fr.record.analyzer.advice.AdviceContext;
import com.fr.record.analyzer.advice.DefaultAdviceCallable;
import com.fr.third.net.bytebuddy.asm.Advice;
import com.fr.third.net.bytebuddy.implementation.bytecode.assign.Assigner;
import com.fr.tolerance.FaultTolerance;
import com.fr.tolerance.FaultToleranceFactory;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
/**
* created by Harrison on 2022/03/07
**/
public class FaultToleranceAdvice implements DesignerAnalyzerAdvice {
@Advice.OnMethodEnter(skipOn = Advice.OnDefaultValue.class)
public static boolean onMethodEnter(@Advice.Local("context") AdviceContext adviceContext) throws Exception {
adviceContext = AdviceContext
.builder()
.onAdviceCall()
.build();
// 如果是切面调用,则忽视当前方法
return adviceContext.isOnAdviceCall();
}
@Advice.OnMethodExit(onThrowable = Exception.class)
public static void onMethodExit(@Advice.This(optional = true, typing = Assigner.Typing.DYNAMIC) Object self,
@Advice.Origin Method method,
@Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] args,
@Advice.Return(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object result,
@Advice.Local("context")AdviceContext adviceContext) throws Exception {
// 如果是切面调用,则忽视不继续 exit
if (adviceContext != null && adviceContext.isOnAdviceCall()) {
return;
}
FaultTolerance faultTolerance = method.getAnnotation(FaultTolerance.class);
Callable<Object> callable = new DefaultAdviceCallable<>(self, method, args);
result = FaultToleranceFactory.getInstance().getScene(faultTolerance.scene()).getProcessor().execute(self, callable, args);
}
}

31
designer-base/src/main/java/com/fr/design/record/analyzer/advice/FocusAdvice.java

@ -0,0 +1,31 @@
package com.fr.design.record.analyzer.advice;
import com.fr.design.record.analyzer.DesignerAnalyzerAdvice;
import com.fr.intelli.record.Focus;
import com.fr.intelli.record.FocusPoint;
import com.fr.intelli.record.FocusPolicy;
import com.fr.log.counter.DefaultLimitedMetric;
import com.fr.third.net.bytebuddy.asm.Advice;
import com.fr.third.net.bytebuddy.implementation.bytecode.assign.Assigner;
import java.lang.reflect.Method;
/**
* created by Harrison on 2022/03/07
**/
public class FocusAdvice implements DesignerAnalyzerAdvice {
private static final String FOCUS_POINT_ID_PREFIX = "function_";
@Advice.OnMethodExit(onThrowable = Exception.class)
public static void onMethodExit(@Advice.Origin Method method,
@Advice.Return(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object result) throws Exception {
if (FocusPolicy.IGNORE == result) {
return;
}
Focus focus = method.getAnnotation(Focus.class);
String id = FOCUS_POINT_ID_PREFIX + focus.id();
DefaultLimitedMetric.INSTANCE.submit(FocusPoint.create(id, focus.text(), focus.source()), id);
}
}

155
designer-base/src/main/java/com/fr/design/record/analyzer/advice/MonitorAdvice.java

@ -0,0 +1,155 @@
package com.fr.design.record.analyzer.advice;
import com.fr.design.record.analyzer.DesignerAnalyzerAdvice;
import com.fr.general.GeneralUtils;
import com.fr.intelli.measure.Estimator;
import com.fr.intelli.metrics.Compute;
import com.fr.intelli.metrics.MessageRecorderFactory;
import com.fr.intelli.metrics.SupervisoryConfig;
import com.fr.intelli.record.Measurable;
import com.fr.intelli.record.MeasureObject;
import com.fr.intelli.record.MeasureUnit;
import com.fr.log.FineLoggerFactory;
import com.fr.measure.DBMeterFactory;
import com.fr.stable.ArrayUtils;
import com.fr.stable.StringUtils;
import com.fr.stable.web.Session;
import com.fr.stable.web.SessionProvider;
import com.fr.third.net.bytebuddy.asm.Advice;
import com.fr.third.net.bytebuddy.implementation.bytecode.assign.Assigner;
import com.fr.web.core.SessionPoolManager;
import com.fr.web.session.SessionLocalManager;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* created by Harrison on 2022/03/07
**/
public class MonitorAdvice implements DesignerAnalyzerAdvice {
private static final Pattern P = Pattern.compile("-?\\d+");
private static final int MIN_ERROR_CODE = 10000000;
@Advice.OnMethodEnter
public static void onMethodEnter(@Advice.Origin Method method,
@Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] args,
@Advice.Local("startTime") Long startTime,
@Advice.Local("registeredSession") Boolean registeredSession) {
startTime = (System.currentTimeMillis());
registeredSession = (findSessionAnnotation(method, args));
}
@Advice.OnMethodExit(onThrowable = Exception.class)
public static void onMethodExit(@Advice.This(optional = true, typing = Assigner.Typing.DYNAMIC) Object self,
@Advice.Origin Method method,
@Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] args,
@Advice.Thrown(typing = Assigner.Typing.DYNAMIC) Exception e,
@Advice.Local("startTime") Long startTime,
@Advice.Local("registeredSession") Boolean registeredSession) throws Exception {
String error = StringUtils.EMPTY;
try {
if (e != null) {
try {
error = getErrorContent(e);
} catch (Exception ignore) {
}
}
} finally {
try {
if (self instanceof Measurable) {
long consume = System.currentTimeMillis() - startTime;
Compute once = method.getAnnotation(Compute.class);
Measurable measurable = (Measurable) self;
MeasureObject measureObject = MeasureObject.create();
recordMemory(once, measurable, measureObject);
recordSQL(once, measureObject);
measureObject.consume(consume);
measureObject.error(error);
String id = UUID.randomUUID().toString();
List<Object> newArgs = new ArrayList<>(Arrays.asList(args));
newArgs.add(id);
recordSQLDetail(id);
Object message = null;
try {
message = measurable.durableEntity(measureObject, newArgs.toArray());
} catch (Throwable throwable) {
FineLoggerFactory.getLogger().error(throwable.getMessage(), throwable);
}
if (measurable instanceof Estimator) {
MessageRecorderFactory.getInstance().asyncSubmit(message);
} else {
MessageRecorderFactory.getInstance().syncSubmit(message);
}
}
} catch (Exception ignore) {
//埋点信息入库失败应该不能影响业务流程
} finally {
if (registeredSession) {
// 如果上面记录了,这里就要释放
SessionLocalManager.releaseSession();
}
}
}
}
public static String getErrorContent(Exception e) {
int errorCode = GeneralUtils.objectToNumber(
extractCodeFromString(e.getMessage())
).intValue();
// 提取字符串中的第一个数字,最小的错误码为10000000
return e.getClass().getName() + ":" + (errorCode >= MIN_ERROR_CODE ? errorCode : StringUtils.EMPTY);
}
public static String extractCodeFromString(String errorMsg) {
Matcher m = P.matcher(errorMsg);
if (m.find()) {
return m.group();
}
return StringUtils.EMPTY;
}
public static void recordSQLDetail(String uuid) {
DBMeterFactory.getMeter().submit(uuid);
}
public static void recordSQL(Compute once, MeasureObject measureObject) {
if (SupervisoryConfig.getInstance().isEnableMeasureSql() && once.computeSql()) {
measureObject.sqlTime(SessionLocalManager.getSqlTime());
measureObject.sql(SessionLocalManager.getSql());
}
}
public static void recordMemory(Compute once, Measurable measurable, MeasureObject measureObject) {
if (SupervisoryConfig.getInstance().isEnableMeasureMemory() && once.computeMemory()) {
MeasureUnit unit = measurable.measureUnit();
measureObject.memory(unit.measureMemory());
}
}
public static boolean findSessionAnnotation(Method method, Object[] args) {
Annotation[][] all = method.getParameterAnnotations();
int len = ArrayUtils.getLength(args);
for (int i = 0; i < len; i++) {
Annotation[] current = all[i];
for (Annotation annotation : current) {
if (annotation.annotationType().equals(Session.class)) {
SessionLocalManager.setSession(
SessionPoolManager.getSessionIDInfor(GeneralUtils.objectToString(args[i]), SessionProvider.class));
return true;
}
}
}
return false;
}
}

49
designer-base/src/main/java/com/fr/design/record/analyzer/advice/PerformancePointAdvice.java

@ -0,0 +1,49 @@
package com.fr.design.record.analyzer.advice;
import com.fr.design.record.analyzer.DesignerAnalyzerAdvice;
import com.fr.intelli.record.ConsumePoint;
import com.fr.intelli.record.MetricRegistry;
import com.fr.intelli.record.PerformancePoint;
import com.fr.intelli.record.PerformancePointRecord;
import com.fr.stable.StringUtils;
import com.fr.third.net.bytebuddy.asm.Advice;
import com.fr.third.net.bytebuddy.implementation.bytecode.assign.Assigner;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* created by Harrison on 2022/03/07
**/
public class PerformancePointAdvice implements DesignerAnalyzerAdvice {
@Advice.OnMethodEnter
public static void onMethodEnter(@Advice.Local("startTime") Long startTime) {
startTime = (System.currentTimeMillis());
}
@Advice.OnMethodExit(onThrowable = Exception.class)
public static void onMethodExit(@Advice.This(optional = true, typing = Assigner.Typing.DYNAMIC) Object self,
@Advice.Origin Method method,
@Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] args,
@Advice.Local("startTime") Long startTime) {
PerformancePoint point = method.getAnnotation(PerformancePoint.class);
String id = point.id();
long endTime = System.currentTimeMillis();
long consume = endTime - startTime;
if (self instanceof PerformancePointRecord) {
PerformancePointRecord measurable = (PerformancePointRecord) self;
List<Object> newArgs = new ArrayList<Object>(Arrays.asList(args));
ConsumePoint consumePoint = ConsumePoint.create(id, startTime, endTime, consume, point.source());
MetricRegistry.getMetric().submit(measurable.recordPoint(consumePoint, newArgs.toArray()));
} else {
if (StringUtils.isNotEmpty(id)) {
MetricRegistry.getMetric().submit(ConsumePoint.create(id, consume, point.source()));
}
}
}
}

36
designer-base/src/main/java/com/fr/design/record/analyzer/advice/TimeAdvice.java

@ -0,0 +1,36 @@
package com.fr.design.record.analyzer.advice;
import com.fr.design.record.analyzer.DesignerAnalyzerAdvice;
import com.fr.log.FineLoggerFactory;
import com.fr.record.analyzer.Metrics;
import com.fr.third.net.bytebuddy.asm.Advice;
import java.lang.reflect.Method;
/**
* created by Harrison on 2022/03/08
**/
public class TimeAdvice implements DesignerAnalyzerAdvice {
@Advice.OnMethodEnter
public static void onMethodEnter(@Advice.Local("startTime") Long startTime) {
startTime = (System.currentTimeMillis());
}
@Advice.OnMethodExit(onThrowable = Exception.class)
public static void onMethodExit(@Advice.Origin Method method,
@Advice.Local("startTime") Long startTime) {
Metrics metrics = method.getAnnotation(Metrics.class);
Object prefix;
String description = metrics.description();
if ("".equals(description)) {
prefix = method.getDeclaringClass().getName() + "#" + method.getName();
} else {
prefix = description;
}
FineLoggerFactory.getLogger().info("{} took {} ms.", prefix, System.currentTimeMillis() - startTime);
}
}

25
designer-base/src/main/java/com/fr/design/record/analyzer/advice/TrackAdvice.java

@ -0,0 +1,25 @@
package com.fr.design.record.analyzer.advice;
import com.fr.intelli.record.MetricRegistry;
import com.fr.third.javax.persistence.Entity;
import com.fr.third.net.bytebuddy.asm.Advice;
import com.fr.third.net.bytebuddy.implementation.bytecode.assign.Assigner;
import java.util.List;
/**
* created by Harrison on 2022/03/08
**/
public class TrackAdvice {
@Advice.OnMethodExit(onThrowable = Exception.class)
public static void onMethodExit(@Advice.Return(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object result) {
if (result != null) {
Class clazz = result.getClass();
if (clazz.getAnnotation(Entity.class) != null || result instanceof List) {
MetricRegistry.getMetric().submit(result);
}
}
}
}

31
designer-base/src/main/java/com/fr/design/upm/UpmFinder.java

@ -1,6 +1,6 @@
package com.fr.design.upm;
import com.fr.base.FRContext;
import com.fr.decision.webservice.v10.plugin.helper.category.impl.BaseResourceLoader;
import com.fr.decision.webservice.v10.plugin.helper.category.impl.UpmResourceLoader;
import com.fr.design.dialog.FineJOptionPane;
import com.fr.design.dialog.UIDialog;
@ -12,8 +12,11 @@ import com.fr.design.update.ui.dialog.UpdateMainDialog;
import com.fr.event.Event;
import com.fr.event.EventDispatcher;
import com.fr.event.Listener;
import com.fr.general.CommonIOUtils;
import com.fr.general.GeneralContext;
import com.fr.general.IOUtils;
import com.fr.log.FineLoggerFactory;
import com.fr.plugin.PluginStoreConstants;
import com.fr.stable.StableUtils;
import com.fr.workspace.Workspace;
import com.fr.workspace.WorkspaceEvent;
@ -33,7 +36,7 @@ public class UpmFinder {
private static final String MAIN_RESOURCE_PATH = UPM_DIR + "/plugin_design.html";
private static final String JXBROWSER = "com.teamdev.jxbrowser.browser.Browser";
public static String installHome = FRContext.getCommonOperator().getWebRootPath();
public static String installHome = PluginStoreConstants.getLocalInstallHome();
private static UIDialog dialog = null;
@ -41,7 +44,8 @@ public class UpmFinder {
EventDispatcher.listen(WorkspaceEvent.AfterSwitch, new Listener<Workspace>() {
@Override
public void on(Event event, Workspace param) {
installHome = FRContext.getCommonOperator().getWebRootPath();
installHome = PluginStoreConstants.getLocalInstallHome();
UpmResourceLoader.INSTANCE.checkOldShopFile();
}
});
}
@ -75,24 +79,24 @@ public class UpmFinder {
}
private static void showUpmPane() {
if (!checkUPMResourcesExist()){
if (!checkUPMResourcesExist()) {
// upm下载
int val = FineJOptionPane.showConfirmDialog(null, Toolkit.i18nText("Fine-Design_Basic_Plugin_Shop_Need_Install"),
Toolkit.i18nText("Fine-Design_Basic_Confirm"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE);
if (val == JOptionPane.OK_OPTION){
if (val == JOptionPane.OK_OPTION) {
try {
UpmResourceLoader.INSTANCE.download();
UpmResourceLoader.INSTANCE.install();
installUpmResource();
FineJOptionPane.showMessageDialog(null, Toolkit.i18nText("Fine-Design_Basic_Plugin_Shop_Installed"),
Toolkit.i18nText("Fine-Design_Basic_Message"), JOptionPane.INFORMATION_MESSAGE);
} catch (Exception e){
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
FineJOptionPane.showMessageDialog(null, Toolkit.i18nText("Fine-Design_Updater_Download_Failed"),
Toolkit.i18nText("Fine-Design_Basic_Message"), JOptionPane.INFORMATION_MESSAGE);
}
}
}
else {
} else {
UpmShowPane upmPane = new UpmShowPane();
if (dialog == null) {
dialog = new UpmShowDialog(DesignerContext.getDesignerFrame(), upmPane);
@ -102,6 +106,15 @@ public class UpmFinder {
}
}
private static void installUpmResource() {
String installHome = PluginStoreConstants.getLocalInstallHome();
File scriptZip = new File(BaseResourceLoader.SCRIPT_DOWNLOAD_PATH);
if (scriptZip.exists()) {
IOUtils.unzip(scriptZip, installHome);
CommonIOUtils.deleteFile(scriptZip);
}
}
private static void showUpdatePane() {
JOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), Toolkit.i18nText("Fine-Design_Update_Info_Plugin_Message"));
if (!GeneralContext.getLocale().equals(Locale.JAPANESE) && !GeneralContext.getLocale().equals(Locale.JAPAN)

19
designer-base/src/main/java/com/fr/design/upm/UpmShowPane.java

@ -32,23 +32,14 @@ public class UpmShowPane extends BasicPane {
UpmShowPane() {
setLayout(new BorderLayout());
modernUIPane = ModernUIPaneFactory.modernUIPaneBuilder()
.prepareForV6(new ScriptContextAdapter() {
modernUIPane = new ModernUIPane.Builder<>()
.prepare(new ScriptContextAdapter() {
@Override
public void onScriptContextCreated(ScriptContextEvent event) {
// 6.x
JSValue window = event.getBrowser().executeJavaScriptAndReturnValue("window");
window.asObject().setProperty("PluginHelper", UpmBridge.getBridge(event.getBrowser()));
}
})
.prepareForV7(params -> {
// 7.x
JsObject window = params.frame().executeJavaScript("window");
if (window != null) {
window.putProperty("PluginHelper", NewUpmBridge.getBridge(window));
}
return InjectJsCallback.Response.proceed();
})
.withURL(UpmFinder.getMainResourcePath(), UpmUtils.renderMap())
.build();
EventDispatcher.listen(DownloadEvent.UPDATE, new Listener<String>() {
@ -57,6 +48,12 @@ public class UpmShowPane extends BasicPane {
modernUIPane.redirect(UpmFinder.getMainResourcePath(), UpmUtils.renderMap());
}
});
EventDispatcher.listen(DownloadEvent.UPDATE, new Listener<String>() {
@Override
public void on(Event event, String param) {
modernUIPane.redirect(UpmFinder.getMainResourcePath(), UpmUtils.renderMap());
}
});
add(modernUIPane, BorderLayout.CENTER);
}
}

12
designer-base/src/main/java/com/fr/design/utils/TemplateUtils.java

@ -1,7 +1,6 @@
package com.fr.design.utils;
import com.fr.base.extension.FileExtension;
import com.fr.base.io.BaseBook;
import com.fr.design.file.HistoryTemplateListCache;
import com.fr.design.file.TemplateTreePane;
import com.fr.design.i18n.Toolkit;
@ -17,9 +16,9 @@ import com.fr.stable.CoreConstants;
import com.fr.stable.ProductConstants;
import com.fr.workspace.WorkContext;
import com.fr.workspace.server.lock.TplOperator;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import javax.swing.SwingWorker;
import java.io.OutputStream;
/**
* @author hades
@ -65,12 +64,7 @@ public class TemplateUtils {
if (!needOpen) {
// 从当前编辑模板中生成备份文件
JTemplate<?, ?> template = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
BaseBook target = template.getTarget();
if (target != null) {
target.export(outputStream);
content = outputStream.toByteArray();
}
content = template.exportData();
} else {
content = WorkContext.getWorkResource().readFully(oldPath);
}

27
designer-base/src/main/java/com/fr/env/utils/WorkspaceUtils.java vendored

@ -0,0 +1,27 @@
package com.fr.env.utils;
import com.fr.design.DesignerEnvManager;
import com.fr.design.env.DesignerWorkspaceInfo;
import com.fr.design.env.LocalDesignerWorkspaceInfo;
import com.fr.stable.StringUtils;
/**
* @author hades
* @version 11.0
* Created by hades on 2022/3/10
*/
public class WorkspaceUtils {
private static final String SPECIFY_WORKSPACE = "fr.designer.workspace";
public static DesignerWorkspaceInfo getWorkspaceInfo() {
String workspacePath;
String current = DesignerEnvManager.getEnvManager().getCurEnvName();
if (StringUtils.isNotEmpty(workspacePath = System.getProperty(SPECIFY_WORKSPACE))) {
return LocalDesignerWorkspaceInfo.create(StringUtils.EMPTY, workspacePath);
} else {
return DesignerEnvManager.getEnvManager().getWorkspaceInfo(current);
}
}
}

111
designer-base/src/main/java/com/fr/exit/ConfigToPropMigrator.java

@ -0,0 +1,111 @@
package com.fr.exit;
import com.fr.config.dao.PropertiesConstants;
import com.fr.design.DesignerEnvManager;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.CommonUtils;
import com.fr.stable.StableUtils;
import com.fr.stable.project.ProjectConstants;
import com.fr.workspace.WorkContext;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
/**
* 设计器关闭前的配置缓存一份到Properties
*
* @author hades
* @version 11.0
* Created by hades on 2022/3/1
*/
public class ConfigToPropMigrator {
private static final String SELECT_FOR_ENTITY = "select id, value from fine_conf_entity";
private static final String SELECT_FOR_CLASSNAME = "select id, classname from fine_conf_classname";
private static final String SELECT_FOR_XML_ENTITY = "select id, value from fine_conf_xmlentity";
private static final ConfigToPropMigrator INSTANCE = new ConfigToPropMigrator();
public static ConfigToPropMigrator getInstance() {
return INSTANCE;
}
public void execute() {
if (WorkContext.getCurrent().isLocal()) {
String url = "jdbc:hsqldb:file://" + WorkContext.getCurrent().getPath() + "/" + ProjectConstants.EMBED_DB_DIRECTORY + "/finedb/db;hsqldb.tx=mvcc";
try {
Class.forName("com.fr.third.org.hsqldb.jdbcDriver");
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
return ;
}
initDirectory();
try (Connection c = DriverManager.getConnection(url);
FileOutputStream entityOut = new FileOutputStream(PropertiesConstants.ENTITY_PROP_PATH);
FileOutputStream classHelperOut = new FileOutputStream(PropertiesConstants.CLASS_NAME_PROP_PATH);
FileOutputStream xmlEntityOut = new FileOutputStream(PropertiesConstants.XML_ENTITY_PROP_PATH)) {
processClassOrEntity(c, new Properties(), SELECT_FOR_ENTITY, entityOut);
processClassOrEntity(c, new Properties(), SELECT_FOR_CLASSNAME, classHelperOut);
processXmlEntity(c, new Properties(), xmlEntityOut);
DesignerEnvManager.getEnvManager().setPropertiesUsable(true);
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
deletePropertiesCache();
}
}
}
private void initDirectory() {
File directory = new File(StableUtils.pathJoin(WorkContext.getCurrent().getPath(), ProjectConstants.EMBED_DB_DIRECTORY, ProjectConstants.PROPERTIES_CACHE_FOR_CONFIG));
if (!directory.exists()) {
directory.mkdir();
}
}
private void processClassOrEntity(Connection c, Properties map, String sql, FileOutputStream outputStream) throws SQLException, IOException {
PreparedStatement query = c.prepareStatement(sql);
ResultSet resultSet = query.executeQuery();
while (resultSet.next()) {
String id = resultSet.getString(1);
String value = resultSet.getString(2);
if (id != null && value != null) {
map.setProperty(id, value);
}
}
map.store(outputStream, null);
}
private void processXmlEntity(Connection c, Properties map, FileOutputStream outputStream) throws SQLException, IOException {
PreparedStatement query = c.prepareStatement(SELECT_FOR_XML_ENTITY);
ResultSet resultSet = query.executeQuery();
while (resultSet.next()) {
String id = resultSet.getString(1);
Blob value = resultSet.getBlob(2);
byte[] bytes = value.getBytes(1L, (int) value.length());
map.setProperty(id, new String(bytes));
}
map.store(outputStream, null);
}
public void deletePropertiesCache() {
CommonUtils.deleteFile(new File(PropertiesConstants.ENTITY_PROP_PATH));
CommonUtils.deleteFile(new File(PropertiesConstants.XML_ENTITY_PROP_PATH));
CommonUtils.deleteFile(new File(PropertiesConstants.CLASS_NAME_PROP_PATH));
}
}

34
designer-base/src/main/java/com/fr/file/FILEChooserPane.java

@ -202,10 +202,11 @@ public class FILEChooserPane extends BasicPane {
return INSTANCE;
}
public static FILEChooserPane getInstanceWithDesignatePath(String path, FILEFilter filter) {
public static FILEChooserPane getInstanceWithDesignatePath(String path, FILEFilter filter, String topPath) {
INSTANCE.showLoc = false;
INSTANCE.showEnv = false;
INSTANCE.showWebReport = false;
INSTANCE.setTopPath(topPath);
INSTANCE.setDesignateModel(path);
INSTANCE.removeAllFilter();
INSTANCE.addChooseFILEFilter(filter, 0);
@ -633,6 +634,9 @@ public class FILEChooserPane extends BasicPane {
this.filterList.clear();
}
public void removeTopPath() {
this.setTopPath(StringUtils.EMPTY);
}
/**
* 设置filter,刷新右侧subFileList中的items
@ -1045,9 +1049,14 @@ public class FILEChooserPane extends BasicPane {
if (placesList == null) {
return;
}
setPlaceListModel(new DesignateRemotePlaceListModel(path));
}
private void setTopPath(String path) {
this.locationBtnPane.setTopPath(path);
}
private void setMultiPlaceListModel() {
if (placesList == null) {
@ -1385,6 +1394,8 @@ public class FILEChooserPane extends BasicPane {
private List<UIButton> buttonList = new ArrayList<>();
private int pathIndex = 0;
private int maxPathIndex = 0;
// 对顶层目录进行的限制
private String topPath;
public LocationButtonPane() {
this.setLayout(FRGUIPaneFactory.createBoxFlowLayout());
@ -1430,6 +1441,10 @@ public class FILEChooserPane extends BasicPane {
});
}
public void setTopPath(String path) {
this.topPath = path;
}
public void highLightButton(FILE dir) {
for (int i = 0; i < this.buttonList.size(); i++) {
this.buttonList.get(i).setForeground(null);
@ -1471,6 +1486,7 @@ public class FILEChooserPane extends BasicPane {
}
Matcher matcher = SEPARATOR_PATTERN.matcher(path);
int node_start = 0;
boolean needTopPath = !StringUtils.isEmpty(topPath);
while (matcher.find()) {
int start = matcher.start();
String btn_text = path.substring(node_start, start);
@ -1478,11 +1494,14 @@ public class FILEChooserPane extends BasicPane {
if (StringUtils.isBlank(btn_text) && isWebAppNamePath) {
btn_text = webAppName;
}
node_start = matcher.end();
if (needTopPath && topPath.equals(btn_text)) {
needTopPath = false;
}
this.buttonList.add(createBlankButton((new SetDirectoryAction(btn_text + '/',
// alex:dir.prefix不和btn_path一起参与pathJoin,因为btn_path是否以/打头在unix,linux
// OS中意义很不一样
FILEFactory.createFolder(dir.prefix() + StableUtils.pathJoin(btn_path, "/"))))));
node_start = matcher.end();
FILEFactory.createFolder(dir.prefix() + StableUtils.pathJoin(btn_path, "/")), !needTopPath))));
}
maxPathIndex = calculateMaxPathIndex();
@ -1583,6 +1602,7 @@ public class FILEChooserPane extends BasicPane {
private class SetDirectoryAction extends UpdateAction {
private FILE dir;
private boolean response = true;
public SetDirectoryAction(String name) {
this.setName(name);
@ -1594,9 +1614,15 @@ public class FILEChooserPane extends BasicPane {
this.dir = file;
}
public SetDirectoryAction(String name, FILE file, boolean response) {
this.setName(name);
this.dir = file;
this.response = response;
}
@Override
public void actionPerformed(ActionEvent evt) {
if (dir != null) {
if (dir != null && response) {
setSelectedDirectory(dir);
}
}

29
designer-base/src/main/java/com/fr/nx/app/designer/utils/CptAndCptxCompatibilityUtil.java

@ -3,7 +3,9 @@ package com.fr.nx.app.designer.utils;
import com.fr.base.extension.FileExtension;
import com.fr.design.mainframe.JTemplate;
import com.fr.file.FILE;
import com.fr.main.FineBook;
import com.fr.main.TemplateWorkBook;
import com.fr.report.core.ReportUtils;
import com.fr.report.report.Report;
import com.fr.report.stable.LayerReportAttr;
import com.fr.report.worksheet.WorkSheet;
@ -30,7 +32,7 @@ public class CptAndCptxCompatibilityUtil {
}
/**
* 判断是不是cptx模板或者开启了新引擎的cpt模板的另存为操作
* 判断是不是cptx模板或者开启了新引擎的cpt模板的另存为操作
**/
private static boolean isSaveAs(JTemplate jTemplate, String oldName, String newName){
return isEngineXEnable(jTemplate.getTarget(), newName) && ((FileExtension.CPTX.matchExtension(oldName) && FileExtension.CPTX.matchExtension(newName)) || (FileExtension.CPT.matchExtension(oldName) && FileExtension.CPT.matchExtension(newName)));
@ -40,7 +42,7 @@ public class CptAndCptxCompatibilityUtil {
* cptx另存为cpt需要修改报表引擎属性
**/
private static boolean setFrEngineAttr(JTemplate jTemplate){
WorkSheet workSheet = gainWorkSheet(jTemplate.getTarget());
WorkSheet workSheet = getFirstWorkSheet(jTemplate.getTarget());
if (workSheet == null){
return false;
}else {
@ -49,8 +51,7 @@ public class CptAndCptxCompatibilityUtil {
layerReportAttr = new LayerReportAttr();
workSheet.setLayerReportAttr(layerReportAttr);
}
layerReportAttr.setClientPaging(true);
layerReportAttr.setEngineState(0);
layerReportAttr.enableEngineX();
return true;
}
}
@ -60,29 +61,17 @@ public class CptAndCptxCompatibilityUtil {
* cptx自动走新引擎非兼容模式cpt需要进行设置
* */
public static boolean isEngineXEnable(Object workBook, String fileName){
WorkSheet workSheet = gainWorkSheet(workBook);
LayerReportAttr layerReportAttr = gainLayerReportAttr(workSheet);
return isEngineXEnable(layerReportAttr, fileName);
}
private static LayerReportAttr gainLayerReportAttr(WorkSheet workSheet){
if (workSheet != null){
LayerReportAttr layerReportAttr = workSheet.getLayerReportAttr();
return layerReportAttr;
} else {
return null;
if (workBook == null || !(workBook instanceof FineBook)){
return false;
}
return ReportUtils.hasEngineXReport4Template((FineBook) workBook) || FileExtension.CPTX.matchExtension(fileName);
}
private static WorkSheet gainWorkSheet(Object workBook){
private static WorkSheet getFirstWorkSheet(Object workBook){
if (workBook == null || !(workBook instanceof TemplateWorkBook)){
return null;
}
Report report = ((TemplateWorkBook) workBook).getReport(0);
return report instanceof WorkSheet ? (WorkSheet)report : null;
}
private static boolean isEngineXEnable(LayerReportAttr layerReportAttr, String fileName){
return (layerReportAttr!= null && layerReportAttr.isClientPaging() && layerReportAttr.getEngineState() == LayerReportAttr.ENGINE_X) || FileExtension.CPTX.matchExtension(fileName);
}
}

28
designer-base/src/main/java/com/fr/start/BaseDesigner.java

@ -3,6 +3,7 @@
*/
package com.fr.start;
import com.fr.base.extension.FileExtension;
import com.fr.common.report.ReportState;
import com.fr.design.DesignerEnvManager;
import com.fr.design.ExtraDesignClassManager;
@ -32,7 +33,9 @@ import com.fr.process.engine.core.CarryMessageEvent;
import com.fr.process.engine.core.FineProcessContext;
import com.fr.stable.OperatingSystem;
import com.fr.start.event.LazyStartupEvent;
import com.fr.workspace.base.WorkspaceStatus;
import java.awt.Window;
import java.io.File;
import java.lang.reflect.Method;
@ -86,6 +89,7 @@ public abstract class BaseDesigner extends ToolBarMenuDock {
eventPipe.fire(new CarryMessageEvent(ReportState.STOP.getValue()));
}
EventDispatcher.fire(WorkspaceStatus.Prepared);
EventDispatcher.asyncFire(LazyStartupEvent.INSTANCE);
collectUserInformation();
}
});
@ -119,14 +123,7 @@ public abstract class BaseDesigner extends ToolBarMenuDock {
}
File f = new File(arg);
String path = f.getAbsolutePath();
boolean pathends1 = path.endsWith(".cpt")
|| path.endsWith(".xls");
boolean pathends2 = path.endsWith(".xlsx")
|| path.endsWith(".frm");
boolean pathends3 = path.endsWith(".form")
|| path.endsWith(".cht");
boolean pathends4 = pathends1 || pathends2 || pathends3;
if (pathends4 || path.endsWith(".chart")) {
if (isAcceptFilePathEnd(path)) {
file = new FileFILE(f);
}
}
@ -146,6 +143,21 @@ public abstract class BaseDesigner extends ToolBarMenuDock {
}
}
private boolean isAcceptFilePathEnd(String path) {
FileExtension[] acceptFileExtensions = new FileExtension[]{
FileExtension.CPT, FileExtension.XLS, FileExtension.XLSX, FileExtension.FRM, FileExtension.CHT, FileExtension.VIS
};
for (FileExtension acceptFileExtension : acceptFileExtensions) {
String[] extensions = acceptFileExtension.getExtensions();
for (String extension : extensions) {
if (path.endsWith("." + extension)) {
return true;
}
}
}
return false;
}
private boolean openFile(final DesignerFrame df, boolean isException, FILE file) {
//启动时打开指定文件的接口

14
designer-base/src/main/java/com/fr/start/event/LazyStartupEvent.java

@ -0,0 +1,14 @@
package com.fr.start.event;
import com.fr.event.Event;
import com.fr.event.Null;
/**
* @author hades
* @version 11.0
* Created by hades on 2022/3/7
*/
public enum LazyStartupEvent implements Event<Null> {
INSTANCE
}

26
designer-base/src/main/resources/com/fr/design/gui/syntax/ui/rsyntaxtextarea/modes/FormulaTokenMaker.flex

@ -584,7 +584,31 @@ FunctionNames = "ABS"|
"QUERY"|
"query"|
"WEBIMAGE"|
"webimage"
"webimage"|
"ACCSUM"|
"accsum"|
"COUNTIFS"|
"countifs"|
"SUMIFS"|
"sumifs"|
"ENDOFMONTH"|
"endofmonth"|
"NUMTOZH"|
"numtozh"|
"MIDCHAR"|
"midchar"|
"ISWORKDAY"|
"isworkday"|
"ENBYSTRNUM"|
"enbystrnum"|
"TEXTGETNUM"|
"textgetnum"|
"GETCHARNUM"|
"getcharnum"|
"GCD"|
"gcd"|
"LCM"|
"lcm"
%state MLC

6
designer-base/src/main/resources/com/fr/design/i18n/dimension_en.properties

@ -14,4 +14,8 @@ com.fr.design.report.fit.templatePane.dialog=800*400
com.fr.design.report.fit.firstColumn=120*20
com.fr.design.report.fit.column=160*20
com.fr.design.lock.LockInfoDialog=500*180
com.fr.design.mainframe.ForbiddenPane.refreshButton=75*24
com.fr.design.mainframe.ForbiddenPane.refreshButton=75*24
com.fr.design.cell.expand.sort.pane=257*185
com.fr.design.sort.rule.item=125*20
com.fr.design.ds.column.sort.pane=250*180
com.fr.design.sort.expand.header.pane=95*10

6
designer-base/src/main/resources/com/fr/design/i18n/dimension_ja_JP.properties

@ -13,4 +13,8 @@ com.fr.design.report.fit.templatePane.dialog=600*400
com.fr.design.report.fit.firstColumn=170*20
com.fr.design.report.fit.column=100*20
com.fr.design.lock.LockInfoDialog=500*180
com.fr.design.mainframe.ForbiddenPane.refreshButton=68*24
com.fr.design.mainframe.ForbiddenPane.refreshButton=68*24
com.fr.design.cell.expand.sort.pane=257*170
com.fr.design.sort.rule.item=125*20
com.fr.design.ds.column.sort.pane=250*165
com.fr.design.sort.expand.header.pane=95*10

6
designer-base/src/main/resources/com/fr/design/i18n/dimension_ko_KR.properties

@ -13,4 +13,8 @@ com.fr.design.report.fit.templatePane.dialog=600*400
com.fr.design.report.fit.firstColumn=130*20
com.fr.design.report.fit.column=100*20
com.fr.design.lock.LockInfoDialog=500*180
com.fr.design.mainframe.ForbiddenPane.refreshButton=80*24
com.fr.design.mainframe.ForbiddenPane.refreshButton=80*24
com.fr.design.cell.expand.sort.pane=267*165
com.fr.design.sort.rule.item=125*20
com.fr.design.ds.column.sort.pane=250*180
com.fr.design.sort.expand.header.pane=95*10

6
designer-base/src/main/resources/com/fr/design/i18n/dimension_zh.properties

@ -14,4 +14,8 @@ com.fr.design.report.fit.templatePane.dialog=600*400
com.fr.design.report.fit.firstColumn=80*20
com.fr.design.report.fit.column=100*20
com.fr.design.lock.LockInfoDialog=400*160
com.fr.design.mainframe.ForbiddenPane.refreshButton=68*24
com.fr.design.mainframe.ForbiddenPane.refreshButton=68*24
com.fr.design.cell.expand.sort.pane=227*155
com.fr.design.sort.rule.item=80*20
com.fr.design.ds.column.sort.pane=220*150
com.fr.design.sort.expand.header.pane=108*10

6
designer-base/src/main/resources/com/fr/design/i18n/dimension_zh_TW.properties

@ -13,4 +13,8 @@ com.fr.design.report.fit.templatePane.dialog=600*400
com.fr.design.report.fit.firstColumn=80*20
com.fr.design.report.fit.column=100*20
com.fr.design.lock.LockInfoDialog=400*160
com.fr.design.mainframe.ForbiddenPane.refreshButton=68*24
com.fr.design.mainframe.ForbiddenPane.refreshButton=68*24
com.fr.design.cell.expand.sort.pane=227*155
com.fr.design.sort.rule.item=80*20
com.fr.design.ds.column.sort.pane=220*150
com.fr.design.sort.expand.header.pane=108*10

5
designer-base/src/main/resources/com/fr/design/images/control/batch_esd_off_disabled.svg

@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g opacity="0.3">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14 3.33333C14 4.622 11.3137 5.66667 8 5.66667C4.68629 5.66667 2 4.622 2 3.33333C2 2.04467 4.68629 1 8 1C11.3137 1 14 2.04467 14 3.33333ZM8 6.83333C11.3137 6.83333 14 5.78866 14 4.5V7.75777C15.206 8.56504 16 9.93979 16 11.5C16 13.9853 13.9853 16 11.5 16C10.4205 16 9.42985 15.6199 8.65446 14.9863C8.43951 14.9953 8.22115 15 8 15C4.68629 15 2 13.9553 2 12.6667V9.16667C2 10.3228 4.16235 11.2826 7.00012 11.4677C7.00293 11.0674 7.058 10.6796 7.15887 10.3106C4.24338 10.1515 2 9.17765 2 8V4.5C2 5.78866 4.68629 6.83333 8 6.83333ZM11.5 8C12.3224 8 13.0785 8.28364 13.6759 8.75842C14.4827 9.3996 15 10.3894 15 11.5C15 13.433 13.433 15 11.5 15C11.1026 15 10.7208 14.9338 10.3648 14.8118C8.98898 14.3403 8 13.0356 8 11.5C8 11.0905 8.07035 10.6973 8.19961 10.3321C8.68044 8.97334 9.97655 8 11.5 8ZM12.9142 9.37868L13.6213 10.0858L12.2071 11.5L13.6213 12.9142L12.9142 13.6213L11.5 12.2071L10.0858 13.6213L9.37868 12.9142L10.7929 11.5L9.37868 10.0858L10.0858 9.37868L11.5 10.7929L12.9142 9.37868Z" fill="#333334"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

3
designer-base/src/main/resources/com/fr/design/images/control/batch_esd_off_normal.svg

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14 3.33333C14 4.622 11.3137 5.66667 8 5.66667C4.68629 5.66667 2 4.622 2 3.33333C2 2.04467 4.68629 1 8 1C11.3137 1 14 2.04467 14 3.33333ZM8 6.83333C11.3137 6.83333 14 5.78866 14 4.5V7.75777C15.206 8.56504 16 9.93979 16 11.5C16 13.9853 13.9853 16 11.5 16C10.4205 16 9.42985 15.6199 8.65446 14.9863C8.43951 14.9953 8.22115 15 8 15C4.68629 15 2 13.9553 2 12.6667V9.16667C2 10.3228 4.16235 11.2826 7.00012 11.4677C7.00293 11.0674 7.058 10.6796 7.15887 10.3106C4.24338 10.1515 2 9.17765 2 8V4.5C2 5.78866 4.68629 6.83333 8 6.83333ZM11.5 8C12.3224 8 13.0785 8.28364 13.6759 8.75842C14.4827 9.3996 15 10.3894 15 11.5C15 13.433 13.433 15 11.5 15C11.1026 15 10.7208 14.9338 10.3648 14.8118C8.98898 14.3403 8 13.0356 8 11.5C8 11.0905 8.07035 10.6973 8.19961 10.3321C8.68044 8.97334 9.97655 8 11.5 8ZM12.9142 9.37868L13.6213 10.0858L12.2071 11.5L13.6213 12.9142L12.9142 13.6213L11.5 12.2071L10.0858 13.6213L9.37868 12.9142L10.7929 11.5L9.37868 10.0858L10.0858 9.37868L11.5 10.7929L12.9142 9.37868Z" fill="#333334"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

5
designer-base/src/main/resources/com/fr/design/images/control/batch_esd_on_disabled.svg

@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g opacity="0.3">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14 3.33333C14 4.622 11.3137 5.66667 8 5.66667C4.68629 5.66667 2 4.622 2 3.33333C2 2.04467 4.68629 1 8 1C11.3137 1 14 2.04467 14 3.33333ZM8 6.83333C11.3137 6.83333 14 5.78866 14 4.5V7.75777C15.206 8.56504 16 9.93979 16 11.5C16 13.9853 13.9853 16 11.5 16C10.4205 16 9.42985 15.6199 8.65446 14.9863C8.43951 14.9953 8.22115 15 8 15C4.68629 15 2 13.9553 2 12.6667V9.16667C2 10.3228 4.16235 11.2826 7.00012 11.4677C7.00293 11.0674 7.058 10.6796 7.15887 10.3106C4.24338 10.1515 2 9.17765 2 8V4.5C2 5.78866 4.68629 6.83333 8 6.83333ZM11.5 8C12.3224 8 13.0785 8.28364 13.6759 8.75842C14.4827 9.3996 15 10.3894 15 11.5C15 13.433 13.433 15 11.5 15C11.1026 15 10.7208 14.9338 10.3648 14.8118C8.98898 14.3403 8 13.0356 8 11.5C8 11.0905 8.07035 10.6973 8.19961 10.3321C8.68044 8.97334 9.97655 8 11.5 8ZM10.6482 13.5372L13.4434 11.5L10.6482 9.53723V13.5372Z" fill="#333334"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

3
designer-base/src/main/resources/com/fr/design/images/control/batch_esd_on_normal.svg

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14 3.33333C14 4.622 11.3137 5.66667 8 5.66667C4.68629 5.66667 2 4.622 2 3.33333C2 2.04467 4.68629 1 8 1C11.3137 1 14 2.04467 14 3.33333ZM8 6.83333C11.3137 6.83333 14 5.78866 14 4.5V7.75777C15.206 8.56504 16 9.93979 16 11.5C16 13.9853 13.9853 16 11.5 16C10.4205 16 9.42985 15.6199 8.65446 14.9863C8.43951 14.9953 8.22115 15 8 15C4.68629 15 2 13.9553 2 12.6667V9.16667C2 10.3228 4.16235 11.2826 7.00012 11.4677C7.00293 11.0674 7.058 10.6796 7.15887 10.3106C4.24338 10.1515 2 9.17765 2 8V4.5C2 5.78866 4.68629 6.83333 8 6.83333ZM11.5 8C12.3224 8 13.0785 8.28364 13.6759 8.75842C14.4827 9.3996 15 10.3894 15 11.5C15 13.433 13.433 15 11.5 15C11.1026 15 10.7208 14.9338 10.3648 14.8118C8.98898 14.3403 8 13.0356 8 11.5C8 11.0905 8.07035 10.6973 8.19961 10.3321C8.68044 8.97334 9.97655 8 11.5 8ZM10.6482 13.5372L13.4434 11.5L10.6482 9.53723V13.5372Z" fill="#333334"/>
</svg>

After

Width:  |  Height:  |  Size: 1014 B

9
designer-base/src/main/resources/com/fr/design/images/data/back_normal.svg

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>icon_返回_normal</title>
<g id="icon_返回_normal" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M6.57897109,8 L11.8084387,3.05563189 C12.0638538,2.81414149 12.0638538,2.4226082 11.8084387,2.1811178 C11.5530236,1.9396274 11.1389139,1.9396274 10.8834988,2.1811178 L5.19156131,7.56274296 C4.93614623,7.80423335 4.93614623,8.19576665 5.19156131,8.43725704 L10.8834988,13.8188822 C11.1389139,14.0603726 11.5530236,14.0603726 11.8084387,13.8188822 C12.0638538,13.5773918 12.0638538,13.1858585 11.8084387,12.9443681 L6.57897109,8 Z"
id="Rectangle-135" fill="#333334" fill-rule="nonzero"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 861 B

10
designer-base/src/main/resources/com/fr/design/images/data/clear_normal.svg

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>icon_关闭_normal</title>
<g id="icon_关闭_normal" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M7.89949494,0.899494937 C8.28609426,0.899494937 8.59949494,1.21820884 8.59949494,1.59375711 L8.59849494,7.19949494 L14.2052328,7.19949494 C14.5567106,7.19949494 14.8471855,7.46057376 14.8931571,7.80394288 L14.8994949,7.89949494 C14.8994949,8.28609426 14.580781,8.59949494 14.2052328,8.59949494 L8.59849494,8.59849494 L8.59949494,14.2052328 C8.59949494,14.5567106 8.33841611,14.8471855 7.99504699,14.8931571 L7.89949494,14.8994949 C7.51289561,14.8994949 7.19949494,14.580781 7.19949494,14.2052328 L7.19949494,8.59849494 L1.59375711,8.59949494 C1.24227924,8.59949494 0.951804388,8.33841611 0.905832732,7.99504699 L0.899494937,7.89949494 C0.899494937,7.51289561 1.21820884,7.19949494 1.59375711,7.19949494 L7.19949494,7.19949494 L7.19949494,1.59375711 C7.19949494,1.24227924 7.46057376,0.951804388 7.80394288,0.905832732 L7.89949494,0.899494937 Z"
id="Combined-Shape" fill="#333334"
transform="translate(7.899495, 7.899495) rotate(45.000000) translate(-7.899495, -7.899495) "></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

9
designer-base/src/main/resources/com/fr/design/images/data/search_normal.svg

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>icon_搜索_normal</title>
<g id="icon_搜索_normal" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M6.5,1 C9.53756612,1 12,3.46243388 12,6.5 C12,7.75769341 11.577854,8.91678934 10.8675261,9.84332369 L14.7880061,13.764409 C15.0706646,14.0470675 15.0706646,14.5053476 14.7880061,14.7880061 C14.5053476,15.0706646 14.0470675,15.0706646 13.764409,14.7880061 L9.84332369,10.8675261 C8.91678934,11.577854 7.75769341,12 6.5,12 C3.46243388,12 1,9.53756612 1,6.5 C1,3.46243388 3.46243388,1 6.5,1 Z M6.5,2 C4.01471863,2 2,4.01471863 2,6.5 C2,8.98528137 4.01471863,11 6.5,11 C8.98528137,11 11,8.98528137 11,6.5 C11,4.01471863 8.98528137,2 6.5,2 Z"
id="Combined-Shape" fill="#333334" fill-rule="nonzero"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 970 B

57
designer-base/src/test/java/com/fr/design/data/MapCompareUtilsTest.java

@ -0,0 +1,57 @@
package com.fr.design.data;
import org.junit.Assert;
import org.junit.Test;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author rinoux
* @version 10.0
* Created by rinoux on 2022/3/28
*/
public class MapCompareUtilsTest {
@Test
public void contrastMapEntries() {
Map<String, String> orig = new LinkedHashMap<>();
orig.put("aaa", "aaa");
orig.put("bbb", "bbb");
orig.put("ccc", "ccc");
orig.put("ddd", "ddd");
Map<String, String> other = new LinkedHashMap<>();
other.put("aaa", "111");
other.put("bbb", "bbb");
other.put("ccc", "ccc");
other.put("eee", "eee");
MapCompareUtils.contrastMapEntries(orig, other, new MapCompareUtils.EventHandler<String, String>() {
@Override
public void on(MapCompareUtils.EntryEventKind entryEventKind, String s, String s2) {
switch (entryEventKind) {
case UPDATED:
Assert.assertEquals(s, "aaa");
Assert.assertEquals(s2, "111");
break;
case REMOVED:
Assert.assertEquals(s, "ddd");
break;
case ADDED:
Assert.assertEquals(s, "eee");
Assert.assertEquals(s2, "eee");
break;
default:
Assert.fail();
}
}
});
}
}

43
designer-base/src/test/java/com/fr/design/data/datapane/management/clip/TableDataTreeClipboardTest.java

@ -0,0 +1,43 @@
package com.fr.design.data.datapane.management.clip;
import com.fr.data.impl.EmbeddedTableData;
import com.fr.design.data.tabledata.wrapper.AbstractTableDataWrapper;
import com.fr.design.data.tabledata.wrapper.TemplateTableDataWrapper;
import junit.framework.TestCase;
import org.junit.Assert;
import java.util.HashMap;
import java.util.Map;
/**
* @author Yvan
*/
public class TableDataTreeClipboardTest extends TestCase {
public void testAddAndTake() {
Map<String, AbstractTableDataWrapper> testMap = new HashMap<>();
testMap.put("ds1", new TemplateTableDataWrapper(new EmbeddedTableData()));
testMap.put("ds2", new TemplateTableDataWrapper(new EmbeddedTableData()));
Map<String, AbstractTableDataWrapper> anotherTestMap = new HashMap<>();
anotherTestMap.put("ds3", new TemplateTableDataWrapper(new EmbeddedTableData()));
Map<String, AbstractTableDataWrapper> clip;
TableDataTreeClipboard.getInstance().addToClip(testMap);
clip = TableDataTreeClipboard.getInstance().takeFromClip();
Assert.assertEquals(2, clip.size());
Assert.assertTrue(clip.containsKey("ds1"));
Assert.assertTrue(clip.containsKey("ds2"));
// 验证多次取出
clip = TableDataTreeClipboard.getInstance().takeFromClip();
Assert.assertEquals(2, clip.size());
Assert.assertTrue(clip.containsKey("ds1"));
Assert.assertTrue(clip.containsKey("ds2"));
TableDataTreeClipboard.getInstance().addToClip(anotherTestMap);
clip = TableDataTreeClipboard.getInstance().takeFromClip();
Assert.assertEquals(1, clip.size());
Assert.assertTrue(clip.containsKey("ds3"));
}
}

201
designer-base/src/test/java/com/fr/design/data/tabledata/paste/TableDataFollowingPasteUtilsTest.java

@ -0,0 +1,201 @@
package com.fr.design.data.tabledata.paste;
import com.fr.base.TableData;
import com.fr.data.Dictionary;
import com.fr.data.TableDataSource;
import com.fr.data.impl.EmbeddedTableData;
import com.fr.design.data.DesignTableDataManager;
import com.fr.design.data.tabledata.wrapper.TableDataWrapper;
import com.fr.design.data.tabledata.wrapper.TemplateTableDataWrapper;
import com.fr.form.data.DataBinding;
import com.fr.form.data.DataTableConfig;
import com.fr.form.ui.AbstractDataControl;
import com.fr.form.ui.DictionaryContainer;
import com.fr.form.ui.Widget;
import com.fr.form.ui.WidgetValue;
import com.fr.report.cell.tabledata.ElementUsedTableDataProvider;
import com.fr.script.Calculator;
import com.fr.stable.script.CalculatorProvider;
import com.fr.stable.script.NameSpace;
import com.fr.web.core.TemplateSessionIDInfo;
import junit.framework.TestCase;
import org.easymock.EasyMock;
import org.junit.Assert;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author Yvan
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest({DesignTableDataManager.class})
public class TableDataFollowingPasteUtilsTest extends TestCase {
@Before
public void beforeTest() {
Map<String, TableDataWrapper> templateDataMap = new LinkedHashMap<String, TableDataWrapper>();
Map<String, TableDataWrapper> serverDataMap = new LinkedHashMap<String, TableDataWrapper>();
Map<String, TableDataWrapper> storeProcedureMap = new LinkedHashMap<String, TableDataWrapper>();
templateDataMap.put("ds1", new TemplateTableDataWrapper(new EmbeddedTableData()));
templateDataMap.put("ds2", new TemplateTableDataWrapper(new EmbeddedTableData()));
templateDataMap.put("ds3", new TemplateTableDataWrapper(new EmbeddedTableData()));
templateDataMap.put("ds4", new TemplateTableDataWrapper(new EmbeddedTableData()));
templateDataMap.put("ds5", new TemplateTableDataWrapper(new EmbeddedTableData()));
List<Map<String, TableDataWrapper>> list = new ArrayList<Map<String, TableDataWrapper>>();
list.add(templateDataMap);
list.add(serverDataMap);
list.add(storeProcedureMap);
TableDataSource tableDataSource = EasyMock.mock(TableDataSource.class);
PowerMock.mockStatic(DesignTableDataManager.class);
EasyMock.expect(DesignTableDataManager.getEditingTableDataSource()).andReturn(tableDataSource).anyTimes();
EasyMock.expect(DesignTableDataManager.getEditingDataSet(tableDataSource)).andReturn(list).anyTimes();
PowerMock.replayAll();
}
public void testTransferProvider2TableDataMap() {
ElementUsedTableDataProvider[] providers = generateElementUsedTableDataProvider();
Map<String, TableData> tableDataMap = TableDataFollowingPasteUtils.transferProvider2TableDataMap(providers);
Assert.assertEquals(2, tableDataMap.size());
Assert.assertTrue(tableDataMap.containsKey("ds1"));
Assert.assertTrue(tableDataMap.containsKey("ds2"));
}
private ElementUsedTableDataProvider[] generateElementUsedTableDataProvider() {
ElementUsedTableDataProvider elementUsedTableDataProvider1 = new ElementUsedTableDataProvider() {
@Override
public Set<String> getElementUsedTableDataNames() {
Set<String> set = new HashSet<>();
set.add("ds1");
return set;
}
};
ElementUsedTableDataProvider elementUsedTableDataProvider2 = new ElementUsedTableDataProvider() {
@Override
public Set<String> getElementUsedTableDataNames() {
Set<String> set = new HashSet<>();
set.add("ds2");
return set;
}
};
return new ElementUsedTableDataProvider[]{elementUsedTableDataProvider1, elementUsedTableDataProvider2};
}
public void testTransferWidgetArray2TableDataMap() {
Widget[] widgets = generateWidgetArray();
Map<String, TableData> tableDataMap = TableDataFollowingPasteUtils.transferWidgetArray2TableDataMap(widgets);
Assert.assertEquals(3, tableDataMap.size());
Assert.assertTrue(tableDataMap.containsKey("ds3"));
Assert.assertTrue(tableDataMap.containsKey("ds4"));
Assert.assertTrue(tableDataMap.containsKey("ds5"));
}
private Widget[] generateWidgetArray() {
Set<String> set = new HashSet<>();
set.add("ds3");
MockWidget widget1 = EasyMock.mock(MockWidget.class);
EasyMock.expect(widget1.getUsedTableDataSets()).andReturn(set).anyTimes();
EasyMock.replay(widget1);
DataBinding dataBinding = new DataBinding("ds4", "");
WidgetValue widgetValue2 = new WidgetValue();
widgetValue2.setValue(dataBinding);
AbstractDataControl widget2 = EasyMock.mock(AbstractDataControl.class);
EasyMock.expect(widget2.getWidgetValue()).andReturn(widgetValue2).anyTimes();
EasyMock.replay(widget2);
DataTableConfig dataTableConfig = EasyMock.mock(DataTableConfig.class);
EasyMock.expect(dataTableConfig.getTableDataName()).andReturn("ds5").anyTimes();
WidgetValue widgetValue3 = new WidgetValue();
widgetValue3.setValue(dataTableConfig);
AbstractDataControl widget3 = EasyMock.mock(AbstractDataControl.class);
EasyMock.expect(widget3.getWidgetValue()).andReturn(widgetValue3).anyTimes();
EasyMock.replay(dataTableConfig, widget3);
Widget[] widgets = new Widget[3];
widgets[0] = widget1;
widgets[1] = widget2;
widgets[2] = widget3;
return widgets;
}
private class MockWidget extends Widget implements DictionaryContainer {
@Override
public String[] supportedEvents() {
return new String[0];
}
@Override
public void setDictionary(Dictionary model) {
}
@Override
public Dictionary getDictionary() {
return null;
}
@Override
public Object getViewValue(Object value, Calculator c, TemplateSessionIDInfo sessionIDInfor, HttpServletRequest req) {
return null;
}
@Override
public Object getModuleValue(Object text, Calculator c, TemplateSessionIDInfo sessionIDInfor, HttpServletRequest req) {
return null;
}
@Override
public Object getViewValue(Object value, Calculator c, TemplateSessionIDInfo sessionIDInfor, HttpServletRequest req, NameSpace dependenceNameSpace) {
return null;
}
@Override
public Object getModuleValue(Object text, Calculator c, TemplateSessionIDInfo sessionIDInfor, HttpServletRequest req, NameSpace dependenceNameSpace) {
return null;
}
@Override
public boolean isValueAllInDictionary(Object value, Calculator c, TemplateSessionIDInfo sessionIDInfor, HttpServletRequest req, NameSpace dependenceNameSpace) {
return false;
}
@Override
public String getXType() {
return null;
}
@Override
public boolean isEditor() {
return false;
}
@Override
public void setDependenceMap(Map dependenceMap) {
}
@Override
public Map getDependenceMap() {
return null;
}
@Override
public String[] dependence(CalculatorProvider calculatorProvider) {
return new String[0];
}
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save