Browse Source

Merge pull request #15 in ~NEIL/design from feature/10.0 to release/10.0

* commit '1d349e1db53d5e9bb5d9a0c0085db9202c6d20d7': (83 commits)
  ct
  ct
  ct
  ct
  页大小改大一点
  格式化
  bug fix
  封装vcsconfigmanager
  REPORT-16461 设计器中关闭自动推送更新无效=>调整代码
  REPORT-16461 设计器中关闭自动推送更新无效
  REPORT-16527 @xiaoxia 通过直接预览触发的模版保存,该模版不会保存新版本
  REPORT-16517 交互修改
  REPORT-16456 【10.0.3埋点-模板】远程设计不生效
  REPORT-16494 模板信息收集, 设计器启动有报错=>调整代码
  REPORT-16494 模板信息收集, 设计器启动有报错=>调整代码
  REPORT-16494 模板信息收集, 设计器启动有报错
  REPORT-14835 设计器界面调整
  使用JDateTime
  文件没有正常删除; 功能点上传的时候数据对不上;
  REPORT-14835 国际化
  ...
bugfix/10.0
neil 6 years ago
parent
commit
3b0e30b89c
  1. 17
      designer-base/src/main/java/com/fr/design/gui/ispinner/UISpinner.java
  2. 28
      designer-base/src/main/java/com/fr/design/layout/FRGUIPaneFactory.java
  3. 97
      designer-base/src/main/java/com/fr/design/mainframe/DesignerFrameFileDealerPane.java
  4. 2
      designer-base/src/main/java/com/fr/design/mainframe/DesktopCardPane.java
  5. 72
      designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java
  6. 2
      designer-base/src/main/java/com/fr/design/mainframe/JVirtualTemplate.java
  7. 117
      designer-base/src/main/java/com/fr/design/mainframe/template/info/DesignerOpenHistory.java
  8. 47
      designer-base/src/main/java/com/fr/design/mainframe/template/info/SendHelper.java
  9. 260
      designer-base/src/main/java/com/fr/design/mainframe/template/info/TemplateInfo.java
  10. 226
      designer-base/src/main/java/com/fr/design/mainframe/template/info/TemplateInfoCollector.java
  11. 6
      designer-base/src/main/java/com/fr/design/mainframe/template/info/TemplateProcessInfo.java
  12. 69
      designer-base/src/main/java/com/fr/design/mainframe/template/info/TimeConsumeTimer.java
  13. 550
      designer-base/src/main/java/com/fr/design/mainframe/templateinfo/TemplateInfoCollector.java
  14. 2
      designer-base/src/main/java/com/fr/design/mainframe/toolbar/ToolBarMenuDock.java
  15. 74
      designer-base/src/main/java/com/fr/design/mainframe/vcs/VcsConfigManager.java
  16. 71
      designer-base/src/main/java/com/fr/design/mainframe/vcs/common/VcsCacheFileNodeFile.java
  17. 147
      designer-base/src/main/java/com/fr/design/mainframe/vcs/common/VcsHelper.java
  18. 99
      designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/EditFileVersionDialog.java
  19. 74
      designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/FileVersionCellEditor.java
  20. 43
      designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/FileVersionCellRender.java
  21. 94
      designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/FileVersionDialog.java
  22. 21
      designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/FileVersionFirstRowPanel.java
  23. 150
      designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/FileVersionRowPanel.java
  24. 79
      designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/FileVersionTable.java
  25. 200
      designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/FileVersionsPanel.java
  26. 6
      designer-base/src/main/java/com/fr/design/update/actions/FileDownloader.java
  27. 5
      designer-base/src/main/java/com/fr/design/update/actions/SoftwareUpdateAction.java
  28. 2
      designer-base/src/main/java/com/fr/design/update/domain/DownloadItem.java
  29. 2
      designer-base/src/main/java/com/fr/design/update/domain/UpdateConstants.java
  30. 2
      designer-base/src/main/java/com/fr/design/update/domain/UpdateInfoCachePropertyManager.java
  31. 2
      designer-base/src/main/java/com/fr/design/update/factory/DirectoryOperationFactory.java
  32. 61
      designer-base/src/main/java/com/fr/design/update/push/DesignerPushUpdateConfigManager.java
  33. 190
      designer-base/src/main/java/com/fr/design/update/push/DesignerPushUpdateDialog.java
  34. 168
      designer-base/src/main/java/com/fr/design/update/push/DesignerPushUpdateManager.java
  35. 91
      designer-base/src/main/java/com/fr/design/update/push/DesignerUpdateInfo.java
  36. 2
      designer-base/src/main/java/com/fr/design/update/ui/dialog/EncodingDetect.java
  37. 9
      designer-base/src/main/java/com/fr/design/update/ui/dialog/RestoreDialog.java
  38. 49
      designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java
  39. 2
      designer-base/src/main/java/com/fr/design/update/ui/widget/ColorfulCellRender.java
  40. 2
      designer-base/src/main/java/com/fr/design/update/ui/widget/LoadingLabel.java
  41. 2
      designer-base/src/main/java/com/fr/design/update/ui/widget/UpdateActionLabel.java
  42. 2
      designer-base/src/main/java/com/fr/design/update/ui/widget/UpdateInfoTable.java
  43. 2
      designer-base/src/main/java/com/fr/design/update/ui/widget/UpdateInfoTableCellRender.java
  44. 2
      designer-base/src/main/java/com/fr/design/update/ui/widget/UpdateInfoTableModel.java
  45. 2
      designer-base/src/main/java/com/fr/design/update/ui/widget/UpdateInfoTextAreaCellRender.java
  46. 58
      designer-base/src/main/resources/com/fr/aspectj/designerbase/TemplateProcessTracker.aj
  47. BIN
      designer-base/src/main/resources/com/fr/design/images/vcs/icon_back_normal@2x.png
  48. BIN
      designer-base/src/main/resources/com/fr/design/images/vcs/icon_delete.png
  49. BIN
      designer-base/src/main/resources/com/fr/design/images/vcs/icon_delete@2x.png
  50. BIN
      designer-base/src/main/resources/com/fr/design/images/vcs/icon_edit.png
  51. BIN
      designer-base/src/main/resources/com/fr/design/images/vcs/icon_edit@2x.png
  52. BIN
      designer-base/src/main/resources/com/fr/design/images/vcs/icon_filter@1x.png
  53. BIN
      designer-base/src/main/resources/com/fr/design/images/vcs/icon_filter@2x.png
  54. BIN
      designer-base/src/main/resources/com/fr/design/images/vcs/icon_list_disabled.png
  55. BIN
      designer-base/src/main/resources/com/fr/design/images/vcs/icon_list_disabled@2x.png
  56. BIN
      designer-base/src/main/resources/com/fr/design/images/vcs/icon_list_normal@2x.png
  57. BIN
      designer-base/src/main/resources/com/fr/design/images/vcs/icon_revert.png
  58. BIN
      designer-base/src/main/resources/com/fr/design/images/vcs/icon_revert@2x.png
  59. BIN
      designer-base/src/main/resources/com/fr/design/images/vcs/icon_save_disabled.png
  60. BIN
      designer-base/src/main/resources/com/fr/design/images/vcs/icon_save_disabled@2x.png
  61. BIN
      designer-base/src/main/resources/com/fr/design/images/vcs/icon_save_normal@2x.png
  62. BIN
      designer-base/src/main/resources/com/fr/design/images/vcs/icon_user@1x.png
  63. BIN
      designer-base/src/main/resources/com/fr/design/images/vcs/icon_user@2x.png
  64. BIN
      designer-base/src/main/resources/com/fr/design/images/vcs/vcs_back.png
  65. BIN
      designer-base/src/main/resources/com/fr/design/images/vcs/vcs_list.png
  66. BIN
      designer-base/src/main/resources/com/fr/design/images/vcs/vcs_save.png
  67. 86
      designer-base/src/main/resources/com/fr/design/ui/update/push/pushUpdate.css
  68. 149
      designer-base/src/main/resources/com/fr/design/ui/update/push/pushUpdate.js
  69. 108
      designer-base/src/test/java/com/fr/design/mainframe/template/info/DesignerOpenHistoryTest.java
  70. 35
      designer-base/src/test/java/com/fr/design/mainframe/template/info/SendHelperTest.java
  71. 195
      designer-base/src/test/java/com/fr/design/mainframe/template/info/TemplateInfoCollectorTest.java
  72. 125
      designer-base/src/test/java/com/fr/design/mainframe/template/info/TemplateInfoTest.java
  73. 49
      designer-base/src/test/java/com/fr/design/mainframe/template/info/TemplateInfoTestHelper.java
  74. 62
      designer-base/src/test/java/com/fr/design/mainframe/template/info/TimeConsumeTimerTest.java
  75. 72
      designer-base/src/test/java/com/fr/design/update/push/DesignerPushUpdateConfigManagerTest.java
  76. 23
      designer-base/src/test/java/com/fr/design/update/push/DesignerPushUpdateDialogTest.java
  77. 76
      designer-base/src/test/java/com/fr/design/update/push/DesignerPushUpdateManagerTest.java
  78. 97
      designer-base/src/test/java/com/fr/design/update/push/DesignerUpdateInfoTest.java
  79. 35
      designer-base/src/test/resources/com/fr/design/mainframe/template/info/tpl.info
  80. 3
      designer-chart/src/main/resources/com/fr/aspectj/designerchart/TemplateProcessTracker.aj
  81. 22
      designer-form/src/main/java/com/fr/design/designer/creator/XWFitLayout.java
  82. 13
      designer-form/src/main/java/com/fr/design/designer/properties/mobile/BodyMobilePropertyUI.java
  83. 28
      designer-form/src/main/java/com/fr/design/form/util/FormDesignerUtils.java
  84. 4
      designer-form/src/main/java/com/fr/design/mainframe/JForm.java
  85. 2
      designer-form/src/main/java/com/fr/design/mainframe/template/info/JFormProcessInfo.java
  86. 18
      designer-form/src/main/java/com/fr/design/widget/ui/designer/XmlRelationedBasicPane.java
  87. 44
      designer-form/src/main/java/com/fr/design/widget/ui/designer/component/PaddingBoundPane.java
  88. 82
      designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/BodyMobileDefinePane.java
  89. 21
      designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/ChartEditorDefinePane.java
  90. 17
      designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/MobileWidgetDefinePane.java
  91. 41
      designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/TabMobileWidgetDefinePane.java
  92. 45
      designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/component/MobileComponentAdvancePane.java
  93. 71
      designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/component/MobileComponentLayoutIntervalPane.java
  94. 2
      designer-form/src/main/resources/com/fr/aspectj/designerform/TemplateProcessTracker.aj
  95. 4
      designer-realize/src/main/java/com/fr/design/mainframe/JWorkBook.java
  96. 5
      designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/AlphaFineDialog.java
  97. 10
      designer-realize/src/main/java/com/fr/design/mainframe/errorinfo/ErrorInfo.java
  98. 14
      designer-realize/src/main/java/com/fr/design/mainframe/errorinfo/ErrorInfoLogAppender.java
  99. 152
      designer-realize/src/main/java/com/fr/design/mainframe/messagecollect/entity/FileEntityBuilder.java
  100. 133
      designer-realize/src/main/java/com/fr/design/mainframe/messagecollect/impl/AbstractSendDataToCloud.java
  101. Some files were not shown because too many files have changed in this diff Show More

17
designer-base/src/main/java/com/fr/design/gui/ispinner/UISpinner.java

@ -92,6 +92,18 @@ public class UISpinner extends JPanel implements UIObserver, GlobalNameObserver
}
public void setValue(double value) {
setValue(value, true);
}
/**
* 赋值但不触发保存,只是展现,一般是populate的时候用
* @param value
*/
public void setValueWithoutEvent(double value) {
setValue(value, false);
}
public void setValue(double value, boolean fireStateChange) {
if (globalNameListener != null && shouldResponseNameListener()) {
globalNameListener.setGlobalName(spinnerName);
}
@ -102,8 +114,9 @@ public class UISpinner extends JPanel implements UIObserver, GlobalNameObserver
}
this.value = value;
setTextField(value);
fireStateChanged();
if (fireStateChange) {
fireStateChanged();
}
}
protected void setTextField(double value){

28
designer-base/src/main/java/com/fr/design/layout/FRGUIPaneFactory.java

@ -3,8 +3,16 @@ package com.fr.design.layout;
import com.fr.design.border.UITitledBorder;
import com.fr.design.gui.ilable.UILabel;
import javax.swing.*;
import java.awt.*;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.LayoutManager;
public class FRGUIPaneFactory {
@ -143,6 +151,22 @@ public class FRGUIPaneFactory {
return jp;
}
/**
* 创建一个带标题边框面板垂直居左布局
*
* @param string 边框标题
* @return JPanel对象
*/
public static JPanel createVerticalTitledBorderPane(String string) {
JPanel jp = new JPanel();
UITitledBorder explainBorder = UITitledBorder.createBorderWithTitle(string);
jp.setBorder(explainBorder);
VerticalFlowLayout layout = new VerticalFlowLayout();
layout.setAlignLeft(true);
jp.setLayout(layout);
return jp;
}
/**
* 创建一个带标题边框面板并且居中显示
*

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

@ -26,6 +26,7 @@ import com.fr.design.i18n.Toolkit;
import com.fr.design.layout.FRGUIPaneFactory;
import com.fr.design.layout.TableLayout;
import com.fr.design.layout.TableLayoutHelper;
import com.fr.design.mainframe.vcs.ui.FileVersionsPanel;
import com.fr.design.menu.KeySetUtils;
import com.fr.design.menu.ShortCut;
import com.fr.design.menu.ToolBarDef;
@ -43,9 +44,12 @@ import com.fr.plugin.manage.PluginFilter;
import com.fr.plugin.observer.PluginEvent;
import com.fr.plugin.observer.PluginEventListener;
import com.fr.stable.CoreConstants;
import com.fr.stable.StableUtils;
import com.fr.stable.StringUtils;
import com.fr.stable.project.ProjectConstants;
import com.fr.third.org.apache.commons.io.FilenameUtils;
import com.fr.workspace.WorkContext;
import com.fr.design.mainframe.vcs.common.VcsHelper;
import javax.swing.BorderFactory;
import javax.swing.JDialog;
@ -111,6 +115,8 @@ public class DesignerFrameFileDealerPane extends JPanel implements FileToolbarSt
private DelFileAction delFileAction = new DelFileAction();
private VcsAction vcsAction = new VcsAction();
/**
* 刷新
@ -196,6 +202,9 @@ public class DesignerFrameFileDealerPane extends JPanel implements FileToolbarSt
for (ShortCut shortCut : extraShortCuts) {
toolbarDef.addShortCut(shortCut);
}
if (VcsHelper.needInit()) {
toolbarDef.addShortCut(vcsAction);
}
toolbarDef.updateToolBar(toolBar);
resetActionStatus();
refresh();
@ -209,6 +218,7 @@ public class DesignerFrameFileDealerPane extends JPanel implements FileToolbarSt
showInExplorerAction.setEnabled(false);
renameAction.setEnabled(false);
delFileAction.setEnabled(false);
vcsAction.setEnabled(false);
this.repaint();
}
@ -273,6 +283,62 @@ public class DesignerFrameFileDealerPane extends JPanel implements FileToolbarSt
}
/**
* 版本管理
*/
private class VcsAction extends UpdateAction {
public VcsAction() {
this.setName(Toolkit.i18nText("Fine-Design_Vcs_Title"));
this.setSmallIcon(VcsHelper.VCS_LIST_PNG);
}
@Override
public void actionPerformed(ActionEvent e) {
String path = DesignerFrameFileDealerPane.getInstance().getSelectedOperation().getFilePath();
path = StableUtils.pathJoin(ProjectConstants.REPORTLETS_NAME, path);
boolean isCurrentEditing = isCurrentEditing(path);
// 如果模板正在编辑,保存后再打开版本管理
if (isCurrentEditing) {
saveCurrentEditingTemplate();
}
// 如果模板已经打开了,关掉,避免出现2个同名tab(1个是模板,1个是版本)
closeOpenedTemplate(path, isCurrentEditing);
FileVersionsPanel fileVersionTablePanel = FileVersionsPanel.getInstance();
fileVersionTablePanel.showFileVersionsPane();
}
private void closeOpenedTemplate(String path, boolean isCurrentEditing) {
for (JTemplate jTemplate : HistoryTemplateListCache.getInstance().getHistoryList()) {
if (ComparatorUtils.equals(jTemplate.getEditingFILE().getPath(), path)) {
if (isCurrentEditing) {
MutilTempalteTabPane.getInstance().setIsCloseCurrent(true);
}
MutilTempalteTabPane.getInstance().closeFormat(jTemplate);
MutilTempalteTabPane.getInstance().closeSpecifiedTemplate(jTemplate);
return;
}
}
}
}
private void saveCurrentEditingTemplate() {
JTemplate<?, ?> jt = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate();
jt.stopEditing();
jt.saveTemplate();
jt.requestFocus();
}
private boolean isCurrentEditing(String path) {
JTemplate<?, ?> jt = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate();
String editing = jt.getEditingFILE().getPath();
return ComparatorUtils.equals(editing, path);
}
/**
* 在系统资源管理器中打开
*/
@ -387,18 +453,45 @@ public class DesignerFrameFileDealerPane extends JPanel implements FileToolbarSt
newFolderAction.setEnabled(singleSelected);
renameAction.setEnabled(singleSelected);
showInExplorerAction.setEnabled(singleSelected);
// 删除操作在至少选中一个时可用
boolean selected = selectedPathNum > 0;
delFileAction.setEnabled(selected);
// 刷新操作始终可用
refreshTreeAction.setEnabled(true);
handleVcsAction();
// 其他状态
otherStateChange();
}
private void handleVcsAction() {
if (!DesignerEnvManager.getEnvManager().getVcsConfigManager().isVcsEnable() || VcsHelper.isUnSelectedTemplate()) {
vcsAction.setEnabled(false);
return;
}
if (WorkContext.getCurrent() != null) {
if (!WorkContext.getCurrent().isLocal()) {
//当前环境为远程环境时
FileNode node = TemplateTreePane.getInstance().getTemplateFileTree().getSelectedFileNode();
if (selectedOperation.getFilePath() != null) {
if (node.getLock() != null && !ComparatorUtils.equals(node.getUserID(), node.getLock())) {
vcsAction.setEnabled(false);
} else {
vcsAction.setEnabled(true);
}
} else {
vcsAction.setEnabled(false);
}
} else {
//当前环境为本地环境时
vcsAction.setEnabled(selectedOperation.getFilePath() != null);
}
}
}
public FileOperations getSelectedOperation() {
return selectedOperation;
}

2
designer-base/src/main/java/com/fr/design/mainframe/DesktopCardPane.java

@ -35,6 +35,7 @@ public class DesktopCardPane extends BasicPane implements TargetModifiedListener
}
DesignerFrameFileDealerPane.getInstance().setCurrentEditingTemplate(jt);
if (component != null) {
component.onLostFocus();
remove(component);
}
add(component = jt, BorderLayout.CENTER);
@ -42,6 +43,7 @@ public class DesktopCardPane extends BasicPane implements TargetModifiedListener
repaint();
revalidate();
component.requestGridFocus();
component.onGetFocus();
}
protected JTemplate<?, ?> getSelectedJTemplate() {

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

@ -34,10 +34,12 @@ import com.fr.design.gui.imenu.UIMenuItem;
import com.fr.design.gui.itree.filetree.TemplateFileTree;
import com.fr.design.i18n.Toolkit;
import com.fr.design.layout.FRGUIPaneFactory;
import com.fr.design.mainframe.templateinfo.TemplateInfoCollector;
import com.fr.design.mainframe.templateinfo.TemplateProcessInfo;
import com.fr.design.mainframe.template.info.TemplateInfoCollector;
import com.fr.design.mainframe.template.info.TemplateProcessInfo;
import com.fr.design.mainframe.template.info.TimeConsumeTimer;
import com.fr.design.mainframe.toolbar.ToolBarMenuDockPlus;
import com.fr.design.mainframe.toolbar.VcsScene;
import com.fr.design.mainframe.vcs.common.VcsHelper;
import com.fr.design.menu.MenuDef;
import com.fr.design.menu.NameSeparator;
import com.fr.design.menu.ShortCut;
@ -91,9 +93,7 @@ public abstract class JTemplate<T extends BaseBook, U extends BaseUndoState<?>>
private static short currentIndex = 0;// 此变量用于多次新建模板时,让名字不重复
private DesignModelAdapter<T, ?> designModel;
private PreviewProvider previewType;
private long openTime = 0L; // 打开模板的时间点(包括新建模板)
private TemplateInfoCollector tic = TemplateInfoCollector.getInstance();
private StringBuilder process = new StringBuilder(""); // 制作模板的过程
private TimeConsumeTimer consumeTimer = new TimeConsumeTimer();
public int resolution = ScreenResolution.getScreenResolution();
public JTemplate() {
@ -101,7 +101,6 @@ public abstract class JTemplate<T extends BaseBook, U extends BaseUndoState<?>>
public JTemplate(T t, String defaultFileName) {
this(t, new MemFILE(newTemplateNameByIndex(defaultFileName)), true);
openTime = System.currentTimeMillis();
}
public JTemplate(T t, FILE file) {
@ -124,12 +123,24 @@ public abstract class JTemplate<T extends BaseBook, U extends BaseUndoState<?>>
this.add(createCenterPane(), BorderLayout.CENTER);
this.undoState = createUndoState();
designModel = createDesignModel();
// 如果不是新建模板,并且在收集列表中
if (!isNewFile && tic.inList(t)) {
openTime = System.currentTimeMillis();
process.append(tic.loadProcess(t));
}
consumeTimer.setEnabled(shouldInitForCollectInfo(isNewFile));
}
void onGetFocus() {
consumeTimer.start();
}
void onLostFocus() {
consumeTimer.stop();
}
private boolean shouldInitForCollectInfo(boolean isNewFile) {
if (isNewFile) {
return true;
}
// 不是新建模板,但是已经在收集列表中
return TemplateInfoCollector.getInstance().contains(template.getTemplateID());
}
// 刷新右侧属性面板
@ -142,35 +153,28 @@ public abstract class JTemplate<T extends BaseBook, U extends BaseUndoState<?>>
// 为收集模版信息作准备
private void initForCollect() {
generateTemplateId();
if (openTime == 0) {
openTime = System.currentTimeMillis();
}
consumeTimer.setEnabled(true);
consumeTimer.start();
}
private void collectInfo() { // 执行收集操作
if (openTime == 0) { // 旧模板,不收集数据
collectInfo(StringUtils.EMPTY);
}
private void collectInfo(String originID) { // 执行收集操作
if (!consumeTimer.isEnabled()) {
return;
}
long saveTime = System.currentTimeMillis(); // 保存模板的时间点
try {
tic.collectInfo(template, this, openTime, saveTime);
int timeConsume = consumeTimer.popTime();
TemplateInfoCollector.getInstance().collectInfo(template.getTemplateID(), originID, getProcessInfo(), timeConsume);
} catch (Throwable th) { // 不管收集过程中出现任何异常,都不应该影响模版保存
}
openTime = saveTime; // 更新 openTime,准备下一次计算
consumeTimer.start(); // 准备下一次计算
}
public abstract TemplateProcessInfo<T> getProcessInfo();
// 追加过程记录
public void appendProcess(String s) {
process.append(s);
}
// 获取过程记录
public String getProcess() {
return process.toString();
}
public U getUndoState() {
return undoState;
}
@ -539,6 +543,9 @@ public abstract class JTemplate<T extends BaseBook, U extends BaseUndoState<?>>
return false;
}
collectInfo();
if (DesignerEnvManager.getEnvManager().getVcsConfigManager().isVcsEnable()) {
VcsHelper.dealWithVcs(this);
}
return this.saveFile();
}
@ -621,15 +628,20 @@ public abstract class JTemplate<T extends BaseBook, U extends BaseUndoState<?>>
}
}
// 保存新模板时会进入此方法(新建模板直接保存,或者另存为)
protected boolean saveNewFile(FILE editingFILE, String oldName) {
String originID = StringUtils.EMPTY;
if (StringUtils.isNotEmpty(this.template.getTemplateID())) {
originID = this.template.getTemplateID();
}
// 在保存之前,初始化 templateID
initForCollect(); // 如果保存新模板(新建模板直接保存,或者另存为),则添加 templateID
initForCollect();
this.editingFILE = editingFILE;
boolean result = this.saveFile();
if (result) {
DesignerFrameFileDealerPane.getInstance().refresh();
collectInfo();
collectInfo(originID);
}
//更换最近打开
DesignerEnvManager.getEnvManager().replaceRecentOpenedFilePath(oldName, this.getPath());

2
designer-base/src/main/java/com/fr/design/mainframe/JVirtualTemplate.java

@ -7,7 +7,7 @@ import com.fr.design.file.HistoryTemplateListPane;
import com.fr.design.gui.frpane.HyperlinkGroupPane;
import com.fr.design.gui.frpane.HyperlinkGroupPaneActionProvider;
import com.fr.design.gui.imenu.UIMenuItem;
import com.fr.design.mainframe.templateinfo.TemplateProcessInfo;
import com.fr.design.mainframe.template.info.TemplateProcessInfo;
import com.fr.design.menu.ShortCut;
import com.fr.design.menu.ToolBarDef;
import com.fr.file.FILE;

117
designer-base/src/main/java/com/fr/design/mainframe/template/info/DesignerOpenHistory.java

@ -0,0 +1,117 @@
package com.fr.design.mainframe.template.info;
import com.fr.general.ComparatorUtils;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.StringUtils;
import com.fr.stable.xml.XMLPrintWriter;
import com.fr.stable.xml.XMLReadable;
import com.fr.stable.xml.XMLWriter;
import com.fr.stable.xml.XMLableReader;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* 管理打开设计器的日期记录
* Created by plough on 2019/4/19.
*/
class DesignerOpenHistory implements XMLReadable, XMLWriter {
static final String XML_TAG = "DesignerOpenHistory";
private static DesignerOpenHistory singleton;
private static final String SPLITTER = ",";
private static final int LENGTH = 3; // 保留最近 3 次的记录
// 最近的日期是 history[0],最早的日期是 history[LENGTH-1]
private String[] history = new String[LENGTH];
private DesignerOpenHistory() {
for (int i = 0; i < LENGTH; i++) {
history[i] = StringUtils.EMPTY;
}
}
static DesignerOpenHistory getInstance() {
if (singleton == null) {
singleton = new DesignerOpenHistory();
}
return singleton;
}
void update() {
String today = getToday();
if (ComparatorUtils.equals(history[0], today)) {
return;
}
shiftByOne();
history[0] = today;
}
/**
* 获取历史记录中囊括的日子数即最早的历史记录 history[LENGTH - 1]到最晚的记录 history[0] 之间的时间跨度
*/
int getHistorySpanDayCount() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
Date earliestDate = sdf.parse(getEarliestDate());
Date latestDate = sdf.parse(history[0]);
long diffInMillies = latestDate.getTime() - earliestDate.getTime();
return (int)TimeUnit.DAYS.convert(diffInMillies,TimeUnit.MILLISECONDS) + 1;
} catch (ParseException e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
return 1;
}
/**
* 今天是否已经打开过设计器
*/
boolean hasOpenedToday() {
String today = getToday();
return ComparatorUtils.equals(today, history[0]);
}
private String getEarliestDate() {
for (int i = LENGTH - 1; i >= 0; i--) {
if (StringUtils.isNotEmpty(history[i])) {
return history[i];
}
}
throw new AssertionError("Designer open history is empty!");
}
private void shiftByOne() {
for (int i = LENGTH - 1; i > 0; i--) {
history[i] = history[i-1];
}
}
private String getToday() {
return new SimpleDateFormat("yyyy-MM-dd").format(Calendar.getInstance().getTime());
}
@Override
public String toString() {
return StringUtils.join(SPLITTER, history);
}
private void parseString(String s) {
String[] arr = s.split(SPLITTER);
System.arraycopy(arr, 0, history, 0, arr.length);
}
@Override
public void readXML(XMLableReader reader) {
if (XML_TAG.equals(reader.getTagName())) {
parseString(reader.getElementValue());
}
}
@Override
public void writeXML(XMLPrintWriter writer) {
writer.startTAG(XML_TAG);
writer.textNode(toString());
writer.end();
}
}

47
designer-base/src/main/java/com/fr/design/mainframe/template/info/SendHelper.java

@ -0,0 +1,47 @@
package com.fr.design.mainframe.template.info;
import com.fr.design.mainframe.SiteCenterToken;
import com.fr.general.CloudCenter;
import com.fr.general.ComparatorUtils;
import com.fr.general.http.HttpToolbox;
import com.fr.json.JSONObject;
import com.fr.log.FineLoggerFactory;
import java.util.HashMap;
import java.util.Map;
/**
* 负责向服务器发送信息
* Created by plough on 2019/4/18.
*/
class SendHelper {
private static final String CONSUMING_URL = CloudCenter.getInstance().acquireUrlByKind("tempinfo.consuming") + "/single";
private static final String PROCESS_URL = CloudCenter.getInstance().acquireUrlByKind("tempinfo.process") + "/single";
private static boolean sendConsumingInfo(String content) {
return sendSingleTemplateInfo(CONSUMING_URL, content);
}
private static boolean sendProcessInfo(String content) {
return sendSingleTemplateInfo(PROCESS_URL, content);
}
static boolean sendTemplateInfo(TemplateInfo templateInfo) {
return SendHelper.sendConsumingInfo(templateInfo.getConsumingMapJsonString()) && SendHelper.sendProcessInfo(templateInfo.getProcessMapJsonString());
}
private static boolean sendSingleTemplateInfo(String url, String content) {
Map<String, Object> para = new HashMap<>();
para.put("token", SiteCenterToken.generateToken());
para.put("content", content);
try {
String res = HttpToolbox.post(url, para);
return ComparatorUtils.equals(new JSONObject(res).get("status"), "success");
} catch (Throwable e) {
// 客户不需要关心,错误等级为 debug 就行了
FineLoggerFactory.getLogger().debug(e.getMessage(), e);
}
return false;
}
}

260
designer-base/src/main/java/com/fr/design/mainframe/template/info/TemplateInfo.java

@ -0,0 +1,260 @@
package com.fr.design.mainframe.template.info;
import com.fr.config.MarketConfig;
import com.fr.design.DesignerEnvManager;
import com.fr.general.GeneralUtils;
import com.fr.json.JSONObject;
import com.fr.stable.ProductConstants;
import com.fr.stable.StringUtils;
import com.fr.stable.xml.XMLPrintWriter;
import com.fr.stable.xml.XMLReadable;
import com.fr.stable.xml.XMLWriter;
import com.fr.stable.xml.XMLableReader;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
/**
* 对应一张模版的记录
* Created by plough on 2019/4/18.
*/
class TemplateInfo implements XMLReadable, XMLWriter {
static final String XML_TAG = "TemplateInfo";
private static final String XML_PROCESS_MAP = "processMap";
private static final String XML_CONSUMING_MAP = "consumingMap";
private static final String ATTR_DAY_COUNT = "day_count";
private static final String ATTR_TEMPLATE_ID = "templateID";
private static final String ATTR_ORIGIN_ID = "originID";
private static final String ATTR_PROCESS = "process";
private static final String ATTR_FLOAT_COUNT = "float_count";
private static final String ATTR_WIDGET_COUNT = "widget_count";
private static final String ATTR_CELL_COUNT = "cell_count";
private static final String ATTR_BLOCK_COUNT = "block_count";
private static final String ATTR_REPORT_TYPE = "report_type";
private static final String ATTR_ACTIVITYKEY = "activitykey";
private static final String ATTR_JAR_TIME = "jar_time";
private static final String ATTR_CREATE_TIME = "create_time";
private static final String ATTR_UUID = "uuid";
private static final String ATTR_TIME_CONSUME = "time_consume";
private static final String ATTR_ORIGIN_TIME = "originTime";
private static final String ATTR_VERSION = "version";
private static final String ATTR_USERNAME = "username";
private static final int VALID_CELL_COUNT = 5; // 有效报表模板的格子数
private static final int VALID_WIDGET_COUNT = 5; // 有效报表模板的控件数
private static final int COMPLETE_DAY_COUNT = 15; // 判断模板是否完成的天数
private int idleDayCount; // 到现在为止,模版闲置(上次保存后没有再编辑过)的天数
private String templateID = StringUtils.EMPTY;
private String originID = StringUtils.EMPTY;
// todo: processMap 和 consumingMap 还可以再拆解为小类,以后继续重构
private Map<String, Object> processMap = new HashMap<>();
private Map<String, Object> consumingMap = new HashMap<>();
private TemplateInfo() {
}
private TemplateInfo(String templateID, String originID) {
this.templateID = templateID;
this.originID = originID;
}
static TemplateInfo newInstanceByRead(XMLableReader reader) {
TemplateInfo templateInfo = new TemplateInfo();
reader.readXMLObject(templateInfo);
return templateInfo;
}
static TemplateInfo newInstance(String templateID) {
return newInstance(templateID, StringUtils.EMPTY, 0);
}
static TemplateInfo newInstance(String templateID, String originID, int originTime) {
HashMap<String, Object> consumingMap = new HashMap<>();
String username = MarketConfig.getInstance().getBbsUsername();
String uuid = DesignerEnvManager.getEnvManager().getUUID();
String activitykey = DesignerEnvManager.getEnvManager().getActivationKey();
String createTime = new SimpleDateFormat("yyyy-MM-dd HH:mm").format(Calendar.getInstance().getTime());
String jarTime = GeneralUtils.readBuildNO();
String version = ProductConstants.VERSION;
consumingMap.put(ATTR_USERNAME, username);
consumingMap.put(ATTR_UUID, uuid);
consumingMap.put(ATTR_ACTIVITYKEY, activitykey);
consumingMap.put(ATTR_TEMPLATE_ID, templateID);
consumingMap.put(ATTR_ORIGIN_ID, originID);
consumingMap.put(ATTR_CREATE_TIME, createTime);
consumingMap.put(ATTR_TIME_CONSUME, originTime); // timeConsume 在原来模版的基础上累加
consumingMap.put(ATTR_ORIGIN_TIME, originTime);
consumingMap.put(ATTR_JAR_TIME, jarTime);
consumingMap.put(ATTR_VERSION, version);
TemplateInfo templateInfo = new TemplateInfo(templateID, originID);
templateInfo.consumingMap = consumingMap;
return templateInfo;
}
String getTemplateID() {
return templateID;
}
String getOriginID() {
return originID;
}
int getTimeConsume() {
return (int)consumingMap.get(ATTR_TIME_CONSUME);
}
public void writeXML(XMLPrintWriter writer) {
writer.startTAG(XML_TAG);
if (StringUtils.isNotEmpty(templateID)) {
writer.attr(ATTR_TEMPLATE_ID, this.templateID);
}
if (StringUtils.isNotEmpty(originID)) {
writer.attr(ATTR_ORIGIN_ID, this.originID);
}
if (idleDayCount >= 0) {
writer.attr(ATTR_DAY_COUNT, this.idleDayCount);
}
writeProcessMap(writer);
writeConsumingMap(writer);
writer.end();
}
private void writeProcessMap(XMLPrintWriter writer) {
writer.startTAG(XML_PROCESS_MAP);
writer.attr(ATTR_PROCESS, (String) processMap.get(ATTR_PROCESS));
writer.attr(ATTR_FLOAT_COUNT, (int) processMap.get(ATTR_FLOAT_COUNT));
writer.attr(ATTR_WIDGET_COUNT, (int) processMap.get(ATTR_WIDGET_COUNT));
writer.attr(ATTR_CELL_COUNT, (int) processMap.get(ATTR_CELL_COUNT));
writer.attr(ATTR_BLOCK_COUNT, (int) processMap.get(ATTR_BLOCK_COUNT));
writer.attr(ATTR_REPORT_TYPE, (int) processMap.get(ATTR_REPORT_TYPE));
writer.end();
}
private void writeConsumingMap(XMLPrintWriter writer) {
writer.startTAG(XML_CONSUMING_MAP);
writer.attr(ATTR_ACTIVITYKEY, (String) consumingMap.get(ATTR_ACTIVITYKEY));
writer.attr(ATTR_JAR_TIME, (String) consumingMap.get(ATTR_JAR_TIME));
writer.attr(ATTR_CREATE_TIME, (String) consumingMap.get(ATTR_CREATE_TIME));
writer.attr(ATTR_UUID, (String) consumingMap.get(ATTR_UUID));
writer.attr(ATTR_TIME_CONSUME, (int)consumingMap.get(ATTR_TIME_CONSUME));
writer.attr(ATTR_ORIGIN_TIME, (int)consumingMap.get(ATTR_ORIGIN_TIME));
writer.attr(ATTR_VERSION, (String) consumingMap.get(ATTR_VERSION));
writer.attr(ATTR_USERNAME, (String) consumingMap.get(ATTR_USERNAME));
writer.end();
}
public void readXML(XMLableReader reader) {
if (!reader.isChildNode()) {
idleDayCount = reader.getAttrAsInt(ATTR_DAY_COUNT, 0);
templateID = reader.getAttrAsString(ATTR_TEMPLATE_ID, StringUtils.EMPTY);
originID = reader.getAttrAsString(ATTR_ORIGIN_ID, StringUtils.EMPTY);
} else {
try {
String name = reader.getTagName();
if (XML_PROCESS_MAP.equals(name)) {
processMap.put(ATTR_PROCESS, reader.getAttrAsString(ATTR_PROCESS, StringUtils.EMPTY));
processMap.put(ATTR_FLOAT_COUNT, reader.getAttrAsInt(ATTR_FLOAT_COUNT, 0));
processMap.put(ATTR_WIDGET_COUNT, reader.getAttrAsInt(ATTR_WIDGET_COUNT, 0));
processMap.put(ATTR_CELL_COUNT, reader.getAttrAsInt(ATTR_CELL_COUNT, 0));
processMap.put(ATTR_BLOCK_COUNT, reader.getAttrAsInt(ATTR_BLOCK_COUNT, 0));
processMap.put(ATTR_REPORT_TYPE, reader.getAttrAsInt(ATTR_REPORT_TYPE, 0));
processMap.put(ATTR_TEMPLATE_ID, templateID);
} else if (XML_CONSUMING_MAP.equals(name)) {
consumingMap.put(ATTR_ACTIVITYKEY, reader.getAttrAsString(ATTR_ACTIVITYKEY, StringUtils.EMPTY));
consumingMap.put(ATTR_JAR_TIME, reader.getAttrAsString(ATTR_JAR_TIME, StringUtils.EMPTY));
consumingMap.put(ATTR_CREATE_TIME, reader.getAttrAsString(ATTR_CREATE_TIME, StringUtils.EMPTY));
consumingMap.put(ATTR_TEMPLATE_ID, templateID);
consumingMap.put(ATTR_ORIGIN_ID, originID);
consumingMap.put(ATTR_UUID, reader.getAttrAsString(ATTR_UUID, StringUtils.EMPTY));
consumingMap.put(ATTR_TIME_CONSUME, reader.getAttrAsInt(ATTR_TIME_CONSUME, 0));
consumingMap.put(ATTR_ORIGIN_TIME, reader.getAttrAsInt(ATTR_ORIGIN_TIME, 0));
consumingMap.put(ATTR_VERSION, reader.getAttrAsString(ATTR_VERSION, "8.0"));
consumingMap.put(ATTR_USERNAME, reader.getAttrAsString(ATTR_USERNAME, StringUtils.EMPTY));
}
} catch (Exception ex) {
// 什么也不做,使用默认值
}
}
}
boolean isTestTemplate() {
if (!isComplete()) {
return false;
}
int reportType = (int) processMap.get(ATTR_REPORT_TYPE);
int cellCount = (int) processMap.get(ATTR_CELL_COUNT);
int floatCount = (int) processMap.get(ATTR_FLOAT_COUNT);
int blockCount = (int) processMap.get(ATTR_BLOCK_COUNT);
int widgetCount = (int) processMap.get(ATTR_WIDGET_COUNT);
boolean isTestTemplate;
if (reportType == 0) { // 普通报表
isTestTemplate = cellCount <= VALID_CELL_COUNT && floatCount <= 1 && widgetCount <= VALID_WIDGET_COUNT;
} else if (reportType == 1) { // 聚合报表
isTestTemplate = blockCount <= 1 && widgetCount <= VALID_WIDGET_COUNT;
} else { // 表单(reportType == 2)
isTestTemplate = widgetCount <= 1;
}
return isTestTemplate;
}
boolean isComplete() {
// 条件 1. 超过15天未编辑
// 条件 2. 设计器在这段未编辑的时间内启动超过 X 次(目前定的 X = 3)。即"设计器最近 X 次启动的时间跨度" < "未编辑时间";
return idleDayCount > COMPLETE_DAY_COUNT
&& DesignerOpenHistory.getInstance().getHistorySpanDayCount() < idleDayCount;
}
String getConsumingMapJsonString() {
return new JSONObject(consumingMap).toString();
}
String getProcessMapJsonString() {
return new JSONObject(processMap).toString();
}
boolean isReadyForSend() {
return isComplete() && !isTestTemplate();
}
void addTimeConsume(int timeConsume) {
timeConsume += (int)consumingMap.get(ATTR_TIME_CONSUME); // 加上之前的累计编辑时间
consumingMap.put(ATTR_TIME_CONSUME, timeConsume);
}
void updateProcessMap(TemplateProcessInfo processInfo) {
HashMap<String, Object> processMap = new HashMap<>();
// 暂不支持模版制作过程的收集
processMap.put(ATTR_PROCESS, StringUtils.EMPTY);
processMap.put(ATTR_REPORT_TYPE, processInfo.getReportType());
processMap.put(ATTR_CELL_COUNT, processInfo.getCellCount());
processMap.put(ATTR_FLOAT_COUNT, processInfo.getFloatCount());
processMap.put(ATTR_BLOCK_COUNT, processInfo.getBlockCount());
processMap.put(ATTR_WIDGET_COUNT, processInfo.getWidgetCount());
this.processMap = processMap;
}
void resetIdleDayCount() {
this.idleDayCount = 0;
}
void addIdleDayCountByOne() {
this.idleDayCount += 1;
}
int getIdleDayCount() {
return this.idleDayCount;
}
}

226
designer-base/src/main/java/com/fr/design/mainframe/template/info/TemplateInfoCollector.java

@ -0,0 +1,226 @@
package com.fr.design.mainframe.template.info;
import com.fr.base.FRContext;
import com.fr.design.DesignerEnvManager;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.ProductConstants;
import com.fr.stable.StableUtils;
import com.fr.stable.StringUtils;
import com.fr.stable.xml.XMLPrintWriter;
import com.fr.stable.xml.XMLReadable;
import com.fr.stable.xml.XMLTools;
import com.fr.stable.xml.XMLWriter;
import com.fr.stable.xml.XMLableReader;
import com.fr.third.javax.xml.stream.XMLStreamException;
import com.fr.workspace.WorkContext;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
* 做模板的过程和耗时收集辅助类
* Created by plough on 2017/2/21.
*/
public class TemplateInfoCollector implements XMLReadable, XMLWriter {
private static final String XML_TAG = "TplInfo";
private static final String XML_TEMPLATE_INFO_LIST = "TemplateInfoList";
private static final String XML_FILE_NAME = "tpl.info";
private static TemplateInfoCollector instance;
private Map<String, TemplateInfo> templateInfoMap;
private DesignerOpenHistory designerOpenHistory;
private TemplateInfoCollector() {
init();
}
private void init() {
templateInfoMap = new HashMap<>();
designerOpenHistory = DesignerOpenHistory.getInstance();
loadFromFile();
}
public static TemplateInfoCollector getInstance() {
if (instance == null) {
instance = new TemplateInfoCollector();
}
return instance;
}
/**
* 根据模板ID是否在收集列表中判断是否需要收集当前模板的信息
*/
public boolean contains(String templateID) {
return StringUtils.isNotEmpty(templateID) && templateInfoMap.containsKey(templateID);
}
/**
* 收集模板信息如果之前没有记录则新增如果已有记录则更新
* 同时将最新数据保存到文件中
* @param templateID 模版id
* @param originID 模版的原始id仅对另存为的模版有效对于非另存为的模版值总是为空
* @param processInfo 包含模版的一些基本信息如模版类型包含控件数量等
* @param timeConsume 本次制作耗时单位为 s
*/
public void collectInfo(String templateID, String originID, TemplateProcessInfo processInfo, int timeConsume) {
if (!shouldCollectInfo()) {
return;
}
TemplateInfo templateInfo;
if (this.contains(templateID)) {
templateInfo = templateInfoMap.get(templateID);
} else {
int originTime = this.contains(originID) ? templateInfoMap.get(originID).getTimeConsume() : 0;
templateInfo = TemplateInfo.newInstance(templateID, originID, originTime);
templateInfoMap.put(templateID, templateInfo);
}
// 收集制作耗时
templateInfo.addTimeConsume(timeConsume);
// 收集模版基本信息
templateInfo.updateProcessMap(processInfo);
// 刷新闲置日计数器
templateInfo.resetIdleDayCount();
// 每次更新之后,都同步到暂存文件中
saveInfo();
}
/**
* 发送本地模板信息到服务器并清空已发送模版的本地记录
*/
public void sendTemplateInfo() {
// 每次启动设计器后,都会执行这个函数(被 InformationCollector 的 collectStartTime 调用)
addIdleDayCount();
removeTestTemplates();
for (String key : templateInfoMap.keySet()) {
TemplateInfo templateInfo = templateInfoMap.get(key);
if (templateInfo.isReadyForSend()) {
if (SendHelper.sendTemplateInfo(templateInfo)) {
// 清空记录
removeFromTemplateInfoList(templateInfo.getTemplateID());
}
}
}
saveInfo();
}
/**
* 获取缓存文件存放路径
*/
private static File getInfoFile() {
return new File(StableUtils.pathJoin(ProductConstants.getEnvHome(), XML_FILE_NAME));
}
void loadFromFile() {
if (!getInfoFile().exists()) {
return;
}
try {
XMLableReader xmlReader = XMLableReader.createXMLableReader(new FileReader(getInfoFile()));
xmlReader.readXMLObject(this);
} catch (XMLStreamException e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
} catch (FileNotFoundException e) {
// do nothing
}
}
TemplateInfo getOrCreateTemplateInfoByID(String templateID) {
if (templateInfoMap.containsKey(templateID)) {
return templateInfoMap.get(templateID);
}
TemplateInfo templateInfo = TemplateInfo.newInstance(templateID);
templateInfoMap.put(templateID, templateInfo);
return templateInfo;
}
private boolean shouldCollectInfo() {
return DesignerEnvManager.getEnvManager().isJoinProductImprove() && FRContext.isChineseEnv();
}
/**
* 将包含所有信息的对象保存到文件
*/
private void saveInfo() {
try {
FileOutputStream out = new FileOutputStream(getInfoFile());
XMLTools.writeOutputStreamXML(this, out);
} catch (Exception ex) {
FineLoggerFactory.getLogger().error(ex.getMessage());
}
}
/**
* 更新 day_count打开设计器却未编辑模板的连续日子
*/
private void addIdleDayCount() {
// 判断今天是否第一次打开设计器,为了防止同一天内,多次 addIdleDayCount
if (designerOpenHistory.hasOpenedToday()) {
return;
}
for (TemplateInfo templateInfo : templateInfoMap.values()) {
templateInfo.addIdleDayCountByOne();
}
designerOpenHistory.update();
}
// 删除所有已完成的测试模版
private void removeTestTemplates() {
ArrayList<String> testTemplateKeys = new ArrayList<>(); // 保存测试模板的key
for (String key : templateInfoMap.keySet()) {
if (templateInfoMap.get(key).isTestTemplate()) {
testTemplateKeys.add(key);
}
}
// 删除测试模板
for (String key : testTemplateKeys) {
removeFromTemplateInfoList(key);
}
}
private void removeFromTemplateInfoList(String key) {
templateInfoMap.remove(key);
}
@Override
public void readXML(XMLableReader reader) {
if (reader.isChildNode()) {
try {
String name = reader.getTagName();
if (DesignerOpenHistory.XML_TAG.equals(name)) {
reader.readXMLObject(designerOpenHistory);
} else if (TemplateInfo.XML_TAG.equals(name)) {
TemplateInfo templateInfo = TemplateInfo.newInstanceByRead(reader);
templateInfoMap.put(templateInfo.getTemplateID(), templateInfo);
}
} catch (Exception ex) {
// 什么也不做,使用默认值
}
}
}
@Override
public void writeXML(XMLPrintWriter writer) {
writer.startTAG(XML_TAG);
designerOpenHistory.writeXML(writer);
writer.startTAG(XML_TEMPLATE_INFO_LIST);
for (TemplateInfo templateInfo : templateInfoMap.values()) {
templateInfo.writeXML(writer);
}
writer.end();
writer.end();
}
}

6
designer-base/src/main/java/com/fr/design/mainframe/templateinfo/TemplateProcessInfo.java → designer-base/src/main/java/com/fr/design/mainframe/template/info/TemplateProcessInfo.java

@ -1,10 +1,14 @@
package com.fr.design.mainframe.templateinfo;
package com.fr.design.mainframe.template.info;
import com.fr.base.io.BaseBook;
/**
* Created by plough on 2017/3/17.
*/
// todo: 重构
// 1. 命名不好,表意不清晰。
// 2. 逻辑混乱,到底是一个 Info 类,还是一个 InfoCollector 类?
// 3. 耦合太强,用组合替代继承
public abstract class TemplateProcessInfo<T extends BaseBook> {
protected T template;

69
designer-base/src/main/java/com/fr/design/mainframe/template/info/TimeConsumeTimer.java

@ -0,0 +1,69 @@
package com.fr.design.mainframe.template.info;
/**
* Created by plough on 2019/4/19.
*/
public class TimeConsumeTimer {
private static final int ONE_THOUSAND = 1000;
private enum State {
RUNNING, STOPPED
}
private int timeConsume; // 单位 s
private long startMS; // 单位 ms
private long stopMS;
private State state;
private boolean enabled;
public TimeConsumeTimer() {
reset();
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public void start() {
if (!isEnabled() || isRunning()) {
return;
}
startMS = System.currentTimeMillis();
state = State.RUNNING;
}
public void stop() {
if (!isEnabled() || !isRunning()) {
return;
}
stopMS = System.currentTimeMillis();
timeConsume += ((stopMS - startMS) / ONE_THOUSAND);
startMS = 0;
stopMS = 0;
state = State.STOPPED;
}
public int popTime() {
if (!isEnabled()) {
return 0;
}
stop();
int result = timeConsume;
reset();
return result;
}
private boolean isRunning() {
return state == State.RUNNING;
}
private void reset() {
timeConsume = 0;
startMS = 0;
stopMS = 0;
state = State.STOPPED;
}
}

550
designer-base/src/main/java/com/fr/design/mainframe/templateinfo/TemplateInfoCollector.java

@ -1,550 +0,0 @@
package com.fr.design.mainframe.templateinfo;
import com.fr.base.FRContext;
import com.fr.base.io.BaseBook;
import com.fr.config.MarketConfig;
import com.fr.design.DesignerEnvManager;
import com.fr.design.mainframe.DesignerContext;
import com.fr.design.mainframe.JTemplate;
import com.fr.design.mainframe.SiteCenterToken;
import com.fr.general.CloudCenter;
import com.fr.general.ComparatorUtils;
import com.fr.general.GeneralUtils;
import com.fr.general.IOUtils;
import com.fr.general.http.HttpClient;
import com.fr.json.JSONObject;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.EncodeConstants;
import com.fr.stable.ProductConstants;
import com.fr.stable.StableUtils;
import com.fr.stable.StringUtils;
import com.fr.stable.xml.XMLPrintWriter;
import com.fr.stable.xml.XMLReadable;
import com.fr.stable.xml.XMLTools;
import com.fr.stable.xml.XMLWriter;
import com.fr.stable.xml.XMLableReader;
import com.fr.third.javax.xml.stream.XMLStreamException;
import com.fr.workspace.WorkContext;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
/**
* 做模板的过程和耗时收集辅助类
* Created by plough on 2017/2/21.
*/
public class TemplateInfoCollector<T extends BaseBook> implements Serializable, XMLReadable, XMLWriter {
static final long serialVersionUID = 2007L;
private static final String FILE_NAME = "tpl.info";
private static final String OBJECT_FILE_NAME = "tplInfo.ser";
private static final int VALID_CELL_COUNT = 5; // 有效报表模板的格子数
private static final int VALID_WIDGET_COUNT = 5; // 有效报表模板的控件数
private static final int COMPLETE_DAY_COUNT = 15; // 判断模板是否完成的天数
private static final int ONE_THOUSAND = 1000;
private static final String XML_DESIGNER_OPEN_DATE = "DesignerOpenDate";
private static final String XML_TEMPLATE_INFO_LIST = "TemplateInfoList";
private static final String XML_TEMPLATE_INFO = "TemplateInfo";
private static final String XML_PROCESS_MAP = "processMap";
private static final String XML_CONSUMING_MAP = "consumingMap";
private static final String ATTR_DAY_COUNT = "day_count";
private static final String ATTR_TEMPLATE_ID = "templateID";
private static final String ATTR_PROCESS = "process";
private static final String ATTR_FLOAT_COUNT = "float_count";
private static final String ATTR_WIDGET_COUNT = "widget_count";
private static final String ATTR_CELL_COUNT = "cell_count";
private static final String ATTR_BLOCK_COUNT = "block_count";
private static final String ATTR_REPORT_TYPE = "report_type";
private static final String ATTR_ACTIVITYKEY = "activitykey";
private static final String ATTR_JAR_TIME = "jar_time";
private static final String ATTR_CREATE_TIME = "create_time";
private static final String ATTR_UUID = "uuid";
private static final String ATTR_TIME_CONSUME = "time_consume";
private static final String ATTR_VERSION = "version";
private static final String ATTR_USERNAME = "username";
private static final String JSON_CONSUMING_MAP = "jsonConsumingMap";
private static final String JSON_PROCESS_MAP = "jsonProcessMap";
private static TemplateInfoCollector instance;
private Map<String, HashMap<String, Object>> templateInfoList;
private String designerOpenDate; //设计器最近一次打开日期
@SuppressWarnings("unchecked")
private TemplateInfoCollector() {
templateInfoList = new HashMap<>();
setDesignerOpenDate();
}
/**
* 获取缓存文件存放路径
*/
private static File getInfoFile() {
return new File(StableUtils.pathJoin(ProductConstants.getEnvHome(), FILE_NAME));
}
private static File getObjectInfoFile() {
return new File(StableUtils.pathJoin(ProductConstants.getEnvHome(), OBJECT_FILE_NAME));
}
public static TemplateInfoCollector getInstance() {
if (instance == null) {
instance = new TemplateInfoCollector();
readXMLFile(instance, getInfoFile());
// 兼容过渡。如果没有新文件,则从老文件读取数据。以后都是读写新的 xml 文件
if (!getInfoFile().exists() && getObjectInfoFile().exists()) {
try {
ObjectInputStream is = new ObjectInputStream(new FileInputStream(getObjectInfoFile()));
instance = (TemplateInfoCollector) is.readObject();
} catch (Exception ex) {
// 什么也不做,instance 使用新值
}
}
}
return instance;
}
private static void readXMLFile(XMLReadable xmlReadable, File xmlFile) {
if (xmlFile == null || !xmlFile.exists()) {
return;
}
String charset = EncodeConstants.ENCODING_UTF_8;
try {
String fileContent = getFileContent(xmlFile);
InputStream xmlInputStream = new ByteArrayInputStream(fileContent.getBytes(charset));
InputStreamReader inputStreamReader = new InputStreamReader(xmlInputStream, charset);
XMLableReader xmlReader = XMLableReader.createXMLableReader(inputStreamReader);
if (xmlReader != null) {
xmlReader.readXMLObject(xmlReadable);
}
xmlInputStream.close();
} catch (FileNotFoundException e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
} catch (IOException e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
} catch (XMLStreamException e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
}
private static String getFileContent(File xmlFile) throws FileNotFoundException, UnsupportedEncodingException {
InputStream is = new FileInputStream(xmlFile);
return IOUtils.inputStream2String(is);
}
public static void main(String[] args) {
TemplateInfoCollector tic = TemplateInfoCollector.getInstance();
tic.sendTemplateInfo();
}
/**
* 把设计器最近打开日期设定为当前日期
*/
private void setDesignerOpenDate() {
designerOpenDate = new SimpleDateFormat("yyyy-MM-dd").format(Calendar.getInstance().getTime());
}
/**
* 判断今天是否第一次打开设计器
*/
private boolean designerOpenFirstTime() {
String today = new SimpleDateFormat("yyyy-MM-dd").format(Calendar.getInstance().getTime());
return !ComparatorUtils.equals(today, designerOpenDate);
}
private boolean shouldCollectInfo() {
//只收集本地环境的
if (!WorkContext.getCurrent().isLocal()) {
return false;
}
return DesignerEnvManager.getEnvManager().isJoinProductImprove() && FRContext.isChineseEnv();
}
public void appendProcess(String log) {
if (!shouldCollectInfo()) {
return;
}
// 获取当前编辑的模板
JTemplate jt = DesignerContext.getDesignerFrame().getSelectedJTemplate();
// 追加过程记录
jt.appendProcess(log);
}
/**
* 加载已经存储的模板过程
*/
@SuppressWarnings("unchecked")
public String loadProcess(T t) {
HashMap<String, Object> processMap = (HashMap<String, Object>) templateInfoList.get(t.getTemplateID()).get(XML_PROCESS_MAP);
return (String) processMap.get(ATTR_PROCESS);
}
/**
* 根据模板ID是否在收集列表中判断是否需要收集当前模板的信息
*/
public boolean inList(T t) {
return templateInfoList.containsKey(t.getTemplateID());
}
/**
* 将包含所有信息的对象保存到文件
*/
private void saveInfo() {
try {
FileOutputStream out = new FileOutputStream(getInfoFile());
XMLTools.writeOutputStreamXML(this, out);
} catch (Exception ex) {
FineLoggerFactory.getLogger().error(ex.getMessage());
}
}
/**
* 更新 day_count打开设计器却未编辑模板的连续日子
*/
private void addDayCount() {
if (designerOpenFirstTime()) {
for (String key : templateInfoList.keySet()) {
HashMap<String, Object> templateInfo = templateInfoList.get(key);
int dayCount = (int) templateInfo.get(ATTR_DAY_COUNT) + 1;
templateInfo.put(ATTR_DAY_COUNT, dayCount);
}
setDesignerOpenDate();
}
}
/**
* 收集模板信息如果之前没有记录则新增如果已有记录则更新
* 同时将最新数据保存到文件中
*/
@SuppressWarnings("unchecked")
public void collectInfo(T t, JTemplate jt, long openTime, long saveTime) {
if (!shouldCollectInfo()) {
return;
}
HashMap<String, Object> templateInfo;
long timeConsume = ((saveTime - openTime) / ONE_THOUSAND); // 制作模板耗时(单位:s)
String templateID = t.getTemplateID();
if (inList(t)) { // 已有记录
templateInfo = templateInfoList.get(templateID);
// 更新 conusmingMap
HashMap<String, Object> consumingMap = (HashMap<String, Object>) templateInfo.get(XML_CONSUMING_MAP);
timeConsume += (long) consumingMap.get(ATTR_TIME_CONSUME); // 加上之前的累计编辑时间
consumingMap.put(ATTR_TIME_CONSUME, timeConsume);
} else { // 新增
templateInfo = new HashMap<>();
templateInfo.put(XML_CONSUMING_MAP, getNewConsumingMap(templateID, openTime, timeConsume));
}
// 直接覆盖 processMap
templateInfo.put(XML_PROCESS_MAP, getProcessMap(templateID, jt));
// 保存模板时,让 day_count 归零
templateInfo.put(ATTR_DAY_COUNT, 0);
templateInfoList.put(templateID, templateInfo);
saveInfo(); // 每次更新之后,都同步到暂存文件中
}
private HashMap<String, Object> getNewConsumingMap(String templateID, long openTime, long timeConsume) {
HashMap<String, Object> consumingMap = new HashMap<>();
String username = MarketConfig.getInstance().getBbsUsername();
String uuid = DesignerEnvManager.getEnvManager().getUUID();
String activitykey = DesignerEnvManager.getEnvManager().getActivationKey();
String createTime = new SimpleDateFormat("yyyy-MM-dd HH:mm").format(Calendar.getInstance().getTime());
String jarTime = GeneralUtils.readBuildNO();
String version = ProductConstants.VERSION;
consumingMap.put(ATTR_USERNAME, username);
consumingMap.put(ATTR_UUID, uuid);
consumingMap.put(ATTR_ACTIVITYKEY, activitykey);
consumingMap.put(ATTR_TEMPLATE_ID, templateID);
consumingMap.put(ATTR_CREATE_TIME, createTime);
consumingMap.put(ATTR_TIME_CONSUME, timeConsume);
consumingMap.put(ATTR_JAR_TIME, jarTime);
consumingMap.put(ATTR_VERSION, version);
return consumingMap;
}
private HashMap<String, Object> getProcessMap(String templateID, JTemplate jt) {
HashMap<String, Object> processMap = new HashMap<>();
processMap.put(ATTR_TEMPLATE_ID, templateID);
processMap.put(ATTR_PROCESS, jt.getProcess());
TemplateProcessInfo info = jt.getProcessInfo();
processMap.put(ATTR_REPORT_TYPE, info.getReportType());
processMap.put(ATTR_CELL_COUNT, info.getCellCount());
processMap.put(ATTR_FLOAT_COUNT, info.getFloatCount());
processMap.put(ATTR_BLOCK_COUNT, info.getBlockCount());
processMap.put(ATTR_WIDGET_COUNT, info.getWidgetCount());
return processMap;
}
/**
* 发送本地模板信息到服务器
*/
public void sendTemplateInfo() {
addDayCount();
String consumingUrl = CloudCenter.getInstance().acquireUrlByKind("tempinfo.consuming") + "/single";
String processUrl = CloudCenter.getInstance().acquireUrlByKind("tempinfo.process") + "/single";
ArrayList<HashMap<String, String>> completeTemplatesInfo = getCompleteTemplatesInfo();
for (HashMap<String, String> templateInfo : completeTemplatesInfo) {
String jsonConsumingMap = templateInfo.get(JSON_CONSUMING_MAP);
String jsonProcessMap = templateInfo.get(JSON_PROCESS_MAP);
if (sendSingleTemplateInfo(consumingUrl, jsonConsumingMap) && sendSingleTemplateInfo(processUrl, jsonProcessMap)) {
// 清空记录
removeFromTemplateInfoList(templateInfo.get(ATTR_TEMPLATE_ID));
}
}
saveInfo();
}
private boolean sendSingleTemplateInfo(String url, String content) {
HashMap<String, String> para = new HashMap<>();
para.put("token", SiteCenterToken.generateToken());
para.put("content", content);
HttpClient httpClient = new HttpClient(url, para, true);
httpClient.setTimeout(5000);
httpClient.asGet();
if (!httpClient.isServerAlive()) {
return false;
}
String res = httpClient.getResponseText();
boolean success;
try {
success = ComparatorUtils.equals(new JSONObject(res).get("status"), "success");
} catch (Exception ex) {
success = false;
}
return success;
}
/**
* 返回已完成的模板信息
*/
@SuppressWarnings("unchecked")
private ArrayList<HashMap<String, String>> getCompleteTemplatesInfo() {
ArrayList<HashMap<String, String>> completeTemplatesInfo = new ArrayList<>();
ArrayList<String> testTemplateKeys = new ArrayList<>(); // 保存测试模板的key
for (String key : templateInfoList.keySet()) {
HashMap<String, Object> templateInfo = templateInfoList.get(key);
if ((int) templateInfo.get(ATTR_DAY_COUNT) <= COMPLETE_DAY_COUNT) { // 未完成模板
continue;
}
if (isTestTemplate(templateInfo)) {
testTemplateKeys.add(key);
continue;
}
HashMap<String, Object> consumingMap = (HashMap<String, Object>) templateInfo.get(XML_CONSUMING_MAP);
HashMap<String, Object> processMap = (HashMap<String, Object>) templateInfo.get(XML_PROCESS_MAP);
String jsonConsumingMap = new JSONObject(consumingMap).toString();
String jsonProcessMap = new JSONObject(processMap).toString();
HashMap<String, String> jsonTemplateInfo = new HashMap<>();
jsonTemplateInfo.put(JSON_CONSUMING_MAP, jsonConsumingMap);
jsonTemplateInfo.put(JSON_PROCESS_MAP, jsonProcessMap);
jsonTemplateInfo.put(ATTR_TEMPLATE_ID, key);
completeTemplatesInfo.add(jsonTemplateInfo);
}
// 删除测试模板
for (String key : testTemplateKeys) {
removeFromTemplateInfoList(key);
}
return completeTemplatesInfo;
}
private void removeFromTemplateInfoList(String key) {
templateInfoList.remove(key);
}
@SuppressWarnings("unchecked")
private boolean isTestTemplate(HashMap<String, Object> templateInfo) {
HashMap<String, Object> processMap = (HashMap<String, Object>) templateInfo.get(XML_PROCESS_MAP);
int reportType = (int) processMap.get(ATTR_REPORT_TYPE);
int cellCount = (int) processMap.get(ATTR_CELL_COUNT);
int floatCount = (int) processMap.get(ATTR_FLOAT_COUNT);
int blockCount = (int) processMap.get(ATTR_BLOCK_COUNT);
int widgetCount = (int) processMap.get(ATTR_WIDGET_COUNT);
boolean isTestTemplate = false;
if (reportType == 0) { // 普通报表
isTestTemplate = cellCount <= VALID_CELL_COUNT && floatCount <= 1 && widgetCount <= VALID_WIDGET_COUNT;
} else if (reportType == 1) { // 聚合报表
isTestTemplate = blockCount <= 1 && widgetCount <= VALID_WIDGET_COUNT;
} else { // 表单(reportType == 2)
isTestTemplate = widgetCount <= 1;
}
return isTestTemplate;
}
@SuppressWarnings("unchecked")
@Override
public void readXML(XMLableReader reader) {
if (reader.isChildNode()) {
try {
String name = reader.getTagName();
if (XML_DESIGNER_OPEN_DATE.equals(name)) {
this.designerOpenDate = reader.getElementValue();
} else if (XML_TEMPLATE_INFO_LIST.equals(name)) {
readTemplateInfoList(reader);
}
} catch (Exception ex) {
// 什么也不做,使用默认值
}
}
}
private void readTemplateInfoList(XMLableReader reader) {
reader.readXMLObject(new XMLReadable() {
public void readXML(XMLableReader reader) {
if (XML_TEMPLATE_INFO.equals(reader.getTagName())) {
TemplateInfo templateInfo = new TemplateInfo();
reader.readXMLObject(templateInfo);
templateInfoList.put(templateInfo.getTemplateID(), templateInfo.getTemplateInfo());
}
}
});
}
@Override
public void writeXML(XMLPrintWriter writer) {
writer.startTAG("TplInfo");
writer.startTAG(XML_DESIGNER_OPEN_DATE);
writer.textNode(designerOpenDate);
writer.end();
writeTemplateInfoList(writer);
writer.end();
}
private void writeTemplateInfoList(XMLPrintWriter writer) {
//启停
writer.startTAG(XML_TEMPLATE_INFO_LIST);
for (String templateID : templateInfoList.keySet()) {
new TemplateInfo(templateInfoList.get(templateID)).writeXML(writer);
}
writer.end();
}
private class TemplateInfo implements XMLReadable, XMLWriter {
private int dayCount;
private String templateID;
private HashMap<String, Object> processMap = new HashMap<>();
private HashMap<String, Object> consumingMap = new HashMap<>();
@SuppressWarnings("unchecked")
public TemplateInfo(HashMap<String, Object> templateInfo) {
this.dayCount = (int) templateInfo.get(ATTR_DAY_COUNT);
this.processMap = (HashMap<String, Object>) templateInfo.get(XML_PROCESS_MAP);
this.consumingMap = (HashMap<String, Object>) templateInfo.get(XML_CONSUMING_MAP);
this.templateID = (String) processMap.get(ATTR_TEMPLATE_ID);
}
public TemplateInfo() {
}
public String getTemplateID() {
return templateID;
}
public HashMap<String, Object> getTemplateInfo() {
HashMap<String, Object> templateInfo = new HashMap<>();
templateInfo.put(XML_PROCESS_MAP, processMap);
templateInfo.put(XML_CONSUMING_MAP, consumingMap);
templateInfo.put(ATTR_DAY_COUNT, dayCount);
return templateInfo;
}
public void writeXML(XMLPrintWriter writer) {
writer.startTAG(XML_TEMPLATE_INFO);
if (StringUtils.isNotEmpty(templateID)) {
writer.attr(ATTR_TEMPLATE_ID, this.templateID);
}
if (dayCount >= 0) {
writer.attr(ATTR_DAY_COUNT, this.dayCount);
}
writeProcessMap(writer);
writeConsumingMap(writer);
writer.end();
}
private void writeProcessMap(XMLPrintWriter writer) {
writer.startTAG(XML_PROCESS_MAP);
writer.attr(ATTR_PROCESS, (String) processMap.get(ATTR_PROCESS));
writer.attr(ATTR_FLOAT_COUNT, (int) processMap.get(ATTR_FLOAT_COUNT));
writer.attr(ATTR_WIDGET_COUNT, (int) processMap.get(ATTR_WIDGET_COUNT));
writer.attr(ATTR_CELL_COUNT, (int) processMap.get(ATTR_CELL_COUNT));
writer.attr(ATTR_BLOCK_COUNT, (int) processMap.get(ATTR_BLOCK_COUNT));
writer.attr(ATTR_REPORT_TYPE, (int) processMap.get(ATTR_REPORT_TYPE));
writer.end();
}
private void writeConsumingMap(XMLPrintWriter writer) {
writer.startTAG(XML_CONSUMING_MAP);
writer.attr(ATTR_ACTIVITYKEY, (String) consumingMap.get(ATTR_ACTIVITYKEY));
writer.attr(ATTR_JAR_TIME, (String) consumingMap.get(ATTR_JAR_TIME));
writer.attr(ATTR_CREATE_TIME, (String) consumingMap.get(ATTR_CREATE_TIME));
writer.attr(ATTR_UUID, (String) consumingMap.get(ATTR_UUID));
writer.attr(ATTR_TIME_CONSUME, (long) consumingMap.get(ATTR_TIME_CONSUME));
writer.attr(ATTR_VERSION, (String) consumingMap.get(ATTR_VERSION));
writer.attr(ATTR_USERNAME, (String) consumingMap.get(ATTR_USERNAME));
writer.end();
}
public void readXML(XMLableReader reader) {
if (!reader.isChildNode()) {
dayCount = reader.getAttrAsInt(ATTR_DAY_COUNT, 0);
templateID = reader.getAttrAsString(ATTR_TEMPLATE_ID, StringUtils.EMPTY);
} else {
try {
String name = reader.getTagName();
if (XML_PROCESS_MAP.equals(name)) {
processMap.put(ATTR_PROCESS, reader.getAttrAsString(ATTR_PROCESS, StringUtils.EMPTY));
processMap.put(ATTR_FLOAT_COUNT, reader.getAttrAsInt(ATTR_FLOAT_COUNT, 0));
processMap.put(ATTR_WIDGET_COUNT, reader.getAttrAsInt(ATTR_WIDGET_COUNT, 0));
processMap.put(ATTR_CELL_COUNT, reader.getAttrAsInt(ATTR_CELL_COUNT, 0));
processMap.put(ATTR_BLOCK_COUNT, reader.getAttrAsInt(ATTR_BLOCK_COUNT, 0));
processMap.put(ATTR_REPORT_TYPE, reader.getAttrAsInt(ATTR_REPORT_TYPE, 0));
processMap.put(ATTR_TEMPLATE_ID, templateID);
} else if (XML_CONSUMING_MAP.equals(name)) {
consumingMap.put(ATTR_ACTIVITYKEY, reader.getAttrAsString(ATTR_ACTIVITYKEY, StringUtils.EMPTY));
consumingMap.put(ATTR_JAR_TIME, reader.getAttrAsString(ATTR_JAR_TIME, StringUtils.EMPTY));
consumingMap.put(ATTR_CREATE_TIME, reader.getAttrAsString(ATTR_CREATE_TIME, StringUtils.EMPTY));
consumingMap.put(ATTR_TEMPLATE_ID, templateID);
consumingMap.put(ATTR_UUID, reader.getAttrAsString(ATTR_UUID, StringUtils.EMPTY));
consumingMap.put(ATTR_TIME_CONSUME, reader.getAttrAsLong(ATTR_TIME_CONSUME, 0));
consumingMap.put(ATTR_VERSION, reader.getAttrAsString(ATTR_VERSION, "8.0"));
consumingMap.put(ATTR_USERNAME, reader.getAttrAsString(ATTR_USERNAME, StringUtils.EMPTY));
}
} catch (Exception ex) {
// 什么也不做,使用默认值
}
}
}
}
}

2
designer-base/src/main/java/com/fr/design/mainframe/toolbar/ToolBarMenuDock.java

@ -52,7 +52,7 @@ import com.fr.design.menu.MenuDef;
import com.fr.design.menu.SeparatorDef;
import com.fr.design.menu.ShortCut;
import com.fr.design.menu.ToolBarDef;
import com.fr.design.onlineupdate.actions.SoftwareUpdateAction;
import com.fr.design.update.actions.SoftwareUpdateAction;
import com.fr.design.remote.action.RemoteDesignAuthManagerAction;
import com.fr.design.utils.ThemeUtils;
import com.fr.general.ComparatorUtils;

74
designer-base/src/main/java/com/fr/design/mainframe/vcs/VcsConfigManager.java

@ -0,0 +1,74 @@
package com.fr.design.mainframe.vcs;
import com.fr.stable.xml.XMLPrintWriter;
import com.fr.stable.xml.XMLReadable;
import com.fr.stable.xml.XMLWriter;
import com.fr.stable.xml.XMLableReader;
/**
* Created by XiaXiang on 2019/4/26.
*/
public class VcsConfigManager implements XMLReadable, XMLWriter {
public static final String XML_TAG = "VcsConfigManager";
private static volatile VcsConfigManager instance = new VcsConfigManager();
private boolean vcsEnable;
private boolean saveCommit;
private boolean useInterval;
private int saveInterval;
public static VcsConfigManager getInstance() {
return instance;
}
public boolean isVcsEnable() {
return vcsEnable;
}
public void setVcsEnable(boolean vcsEnable) {
this.vcsEnable = vcsEnable;
}
public boolean isSaveCommit() {
return saveCommit;
}
public void setSaveCommit(boolean saveCommit) {
this.saveCommit = saveCommit;
}
public boolean isUseInterval() {
return useInterval;
}
public void setUseInterval(boolean useInterval) {
this.useInterval = useInterval;
}
public int getSaveInterval() {
return saveInterval;
}
public void setSaveInterval(int saveInterval) {
this.saveInterval = saveInterval;
}
@Override
public void readXML(XMLableReader reader) {
if (reader.isAttr()) {
this.setSaveCommit(reader.getAttrAsBoolean("saveCommit", true));
this.setSaveInterval(reader.getAttrAsInt("saveInterval", 60));
this.setUseInterval(reader.getAttrAsBoolean("useInterval", true));
this.setVcsEnable(reader.getAttrAsBoolean("vcsEnable", true));
}
}
@Override
public void writeXML(XMLPrintWriter writer) {
writer.startTAG(XML_TAG);
writer.attr("saveCommit", this.isSaveCommit());
writer.attr("saveInterval", this.getSaveInterval());
writer.attr("useInterval", this.isUseInterval());
writer.attr("vcsEnable", this.isVcsEnable());
writer.end();
}
}

71
designer-base/src/main/java/com/fr/design/mainframe/vcs/common/VcsCacheFileNodeFile.java

@ -0,0 +1,71 @@
package com.fr.design.mainframe.vcs.common;
import com.fr.base.io.XMLEncryptUtils;
import com.fr.file.FileNodeFILE;
import com.fr.file.filetree.FileNode;
import com.fr.general.ComparatorUtils;
import com.fr.stable.StableUtils;
import com.fr.workspace.WorkContext;
import com.fr.workspace.resource.WorkResource;
import com.fr.workspace.resource.WorkResourceOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class VcsCacheFileNodeFile extends FileNodeFILE {
private final FileNode node;
public VcsCacheFileNodeFile(FileNode node) {
super(node);
this.node = node;
}
/**
* 和FileNodeFILE中一样只是去掉了必须以reportlets开头的限制改为vcs开头
*
* @return
* @throws Exception
*/
@Override
public InputStream asInputStream() {
if (node == null) {
return null;
}
String envPath = node.getEnvPath();
// envPath必须以vcs开头
if (!envPath.startsWith(VcsHelper.VCS_CACHE_DIR)) {
return null;
}
InputStream in = WorkContext.getCurrent().get(WorkResource.class)
.openStream(StableUtils.pathJoin(VcsHelper.VCS_CACHE_DIR, envPath.substring(VcsHelper.VCS_CACHE_DIR.length() + 1)));
return envPath.endsWith(".cpt") || envPath.endsWith(".frm")
? XMLEncryptUtils.decodeInputStream(in) : in;
}
/**
* 和FileNodeFILE中一样只是去掉了必须以reportlets开头的限制改为vcs开头
*
* @return
* @throws Exception
*/
@Override
public OutputStream asOutputStream() {
if (ComparatorUtils.equals(node, null)) {
return null;
}
String envPath = node.getEnvPath();
// envPath必须以reportLets开头
if (!envPath.startsWith(VcsHelper.VCS_CACHE_DIR)) {
return null;
}
return new WorkResourceOutputStream(StableUtils.pathJoin(VcsHelper.VCS_CACHE_DIR, envPath.substring(VcsHelper.VCS_CACHE_DIR.length() + 1)));
}
}

147
designer-base/src/main/java/com/fr/design/mainframe/vcs/common/VcsHelper.java

@ -0,0 +1,147 @@
package com.fr.design.mainframe.vcs.common;
import com.fr.design.DesignerEnvManager;
import com.fr.design.file.HistoryTemplateListCache;
import com.fr.design.file.TemplateTreePane;
import com.fr.design.gui.itree.filetree.TemplateFileTree;
import com.fr.design.i18n.Toolkit;
import com.fr.design.mainframe.DesignerFrameFileDealerPane;
import com.fr.design.mainframe.JTemplate;
import com.fr.design.mainframe.vcs.VcsConfigManager;
import com.fr.design.mainframe.vcs.ui.FileVersionTable;
import com.fr.general.IOUtils;
import com.fr.plugin.context.PluginContext;
import com.fr.plugin.manage.PluginManager;
import com.fr.report.entity.VcsEntity;
import com.fr.stable.StringUtils;
import com.fr.stable.project.ProjectConstants;
import com.fr.workspace.WorkContext;
import com.fr.workspace.server.vcs.VcsOperator;
import javax.swing.Icon;
import javax.swing.border.EmptyBorder;
import java.awt.Color;
import java.util.Date;
import static com.fr.stable.StableUtils.pathJoin;
/**
* Created by XiaXiang on 2019/4/17.
*/
public class VcsHelper {
private final static String VCS_DIR = "vcs";
public final static String VCS_CACHE_DIR = pathJoin(VCS_DIR, "cache");
private static final int MINUTE = 60 * 1000;
private final static String VCS_PLUGIN_ID = "com.fr.plugin.vcs.v10";
public final static String CURRENT_USERNAME = WorkContext.getCurrent().isLocal()
? Toolkit.i18nText("Fine-Design_Vcs_Local_User")
: WorkContext.getCurrent().getConnection().getUserName();
public final static Color TABLE_SELECT_BACKGROUND = new Color(0xD8F2FD);
public final static Color COPY_VERSION_BTN_COLOR = new Color(0x419BF9);
public final static EmptyBorder EMPTY_BORDER = new EmptyBorder(10, 10, 0, 10);
public final static EmptyBorder EMPTY_BORDER_BOTTOM = new EmptyBorder(10, 10, 10, 10);
public final static Icon VCS_LIST_PNG = IOUtils.readIcon("/com/fr/design/images/vcs/vcs_list.png");
public final static Icon VCS_BACK_PNG = IOUtils.readIcon("/com/fr/design/images/vcs/vcs_back.png");
public final static Icon VCS_FILTER_PNG = IOUtils.readIcon("/com/fr/design/images/vcs/icon_filter@1x.png");
public final static Icon VCS_EDIT_PNG = IOUtils.readIcon("/com/fr/design/images/vcs/icon_edit.png");
public final static Icon VCS_DELETE_PNG = IOUtils.readIcon("/com/fr/design/images/vcs/icon_delete.png");
public final static Icon VCS_USER_PNG = IOUtils.readIcon("/com/fr/design/images/vcs/icon_user@1x.png");
public final static Icon VCS_REVERT = IOUtils.readIcon("/com/fr/design/images/vcs/icon_revert.png");
private static int containsFolderCounts() {
TemplateFileTree fileTree = TemplateTreePane.getInstance().getTemplateFileTree();
if (fileTree.getSelectionPaths() == null) {
return 0;
}
//选择的包含文件和文件夹的数目
if (fileTree.getSelectionPaths().length == 0) {
return 0;
}
//所有的num减去模板的count,得到文件夹的count
return fileTree.getSelectionPaths().length - fileTree.getSelectedTemplatePaths().length;
}
private static int selectedTemplateCounts() {
TemplateFileTree fileTree = TemplateTreePane.getInstance().getTemplateFileTree();
if (fileTree.getSelectionPaths() == null) {
return 0;
}
return fileTree.getSelectedTemplatePaths().length;
}
public static boolean isUnSelectedTemplate() {
return VcsHelper.containsFolderCounts() + VcsHelper.selectedTemplateCounts() != 1;
}
public static String getEditingFilename() {
JTemplate jt = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate();
String editingFilePath = jt.getEditingFILE().getPath();
if (editingFilePath.startsWith(ProjectConstants.REPORTLETS_NAME)) {
editingFilePath = editingFilePath.replaceFirst(ProjectConstants.REPORTLETS_NAME, StringUtils.EMPTY);
} else if (editingFilePath.startsWith(VcsHelper.VCS_CACHE_DIR)) {
editingFilePath = editingFilePath.replaceFirst(VcsHelper.VCS_CACHE_DIR, StringUtils.EMPTY);
}
if (editingFilePath.startsWith("/")) {
editingFilePath = editingFilePath.substring(1);
}
return editingFilePath;
}
public static boolean needDeleteVersion(VcsEntity entity) {
if (entity == null || !DesignerEnvManager.getEnvManager().getVcsConfigManager().isUseInterval()) {
return false;
}
return new Date().getTime() - entity.getTime().getTime() < DesignerEnvManager.getEnvManager().getVcsConfigManager().getSaveInterval() * MINUTE && StringUtils.isBlank(entity.getCommitMsg());
}
public static boolean needInit() {
PluginContext context = PluginManager.getContext(VCS_PLUGIN_ID);
return context == null || !context.isActive();
}
/**
* 版本控制
* @param jt
*/
public static void dealWithVcs(final JTemplate jt) {
new Thread(new Runnable() {
@Override
public void run() {
String fileName = VcsHelper.getEditingFilename();
VcsOperator operator = WorkContext.getCurrent().get(VcsOperator.class);
VcsEntity entity = operator.getFileVersionByIndex(fileName, 0);
int latestFileVersion = 0;
if (entity != null) {
latestFileVersion = entity.getVersion();
}
if (jt.getEditingFILE() instanceof VcsCacheFileNodeFile) {
operator.saveVersionFromCache(VcsHelper.CURRENT_USERNAME, fileName, StringUtils.EMPTY, latestFileVersion + 1);
String path = DesignerFrameFileDealerPane.getInstance().getSelectedOperation().getFilePath();
FileVersionTable.getInstance().updateModel(1, WorkContext.getCurrent().get(VcsOperator.class).getVersions(path.replaceFirst("/", "")));
} else {
operator.saveVersion(VcsHelper.CURRENT_USERNAME, fileName, StringUtils.EMPTY, latestFileVersion + 1);
}
VcsEntity oldEntity = WorkContext.getCurrent().get(VcsOperator.class).getFileVersionByIndex(fileName, 1);
if (VcsHelper.needDeleteVersion(oldEntity)) {
operator.deleteVersion(oldEntity.getFilename(), oldEntity.getVersion());
}
}
}).start();
}
}

99
designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/EditFileVersionDialog.java

@ -0,0 +1,99 @@
package com.fr.design.mainframe.vcs.ui;
import com.fr.design.dialog.UIDialog;
import com.fr.design.gui.ibutton.UIButton;
import com.fr.design.gui.icontainer.UIScrollPane;
import com.fr.design.gui.ilable.UILabel;
import com.fr.design.gui.itextarea.UITextArea;
import com.fr.design.layout.TableLayoutHelper;
import com.fr.design.mainframe.DesignerContext;
import com.fr.design.utils.gui.GUICoreUtils;
import com.fr.locale.InterProviderFactory;
import com.fr.report.ReportContext;
import com.fr.report.entity.VcsEntity;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* 编辑版本信息面板
*/
public class EditFileVersionDialog extends UIDialog {
private final UITextArea msgTestArea = new UITextArea();
private final UILabel versionLabel = new UILabel();
private VcsEntity entity;
public EditFileVersionDialog(VcsEntity entity) {
this(DesignerContext.getDesignerFrame());
this.entity = entity;
msgTestArea.setText(entity.getCommitMsg());
versionLabel.setText(String.valueOf(entity.getVersion()));
}
private EditFileVersionDialog(Frame parent) {
super(parent);
initComponents();
setModal(true);
setTitle(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Vcs_Save_Version"));
setSize(300, 220);
setResizable(false);
GUICoreUtils.centerWindow(this);
}
private void initComponents() {
JPanel fontPane = new JPanel(new BorderLayout());
fontPane.add(new UILabel(" " + com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Vcs_Version_Message") + ":"), BorderLayout.NORTH);
msgTestArea.setBorder(null);
UIScrollPane scrollPane = new UIScrollPane(msgTestArea);
Component[][] components = new Component[][]{
new Component[]{new UILabel(" " + com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Vcs_Version_Number") + ":"), versionLabel},
new Component[]{fontPane, scrollPane}
};
double[] rowSizes = new double[]{25, 100};
double[] columnSizes = new double[]{70, 200};
add(TableLayoutHelper.createTableLayoutPane(components, rowSizes, columnSizes), BorderLayout.CENTER);
JPanel buttonPane = new JPanel(new FlowLayout(FlowLayout.RIGHT));
add(buttonPane, BorderLayout.SOUTH);
UIButton ok = new UIButton(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_OK"));
UIButton cancel = new UIButton(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Utils_Design_Action_Cancel"));
buttonPane.add(ok);
buttonPane.add(cancel);
ok.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
entity.setCommitMsg(msgTestArea.getText());
ReportContext.getInstance().getVcsController().saveOrUpdateFileVersion(entity);
setVisible(false);
}
});
cancel.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
doCancel();
}
});
}
@Override
public void checkValid() throws Exception {
}
}

74
designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/FileVersionCellEditor.java

@ -0,0 +1,74 @@
package com.fr.design.mainframe.vcs.ui;
import com.fr.design.file.HistoryTemplateListCache;
import com.fr.design.file.MutilTempalteTabPane;
import com.fr.design.mainframe.DesignerContext;
import com.fr.design.mainframe.DesignerFrameFileDealerPane;
import com.fr.design.mainframe.JTemplate;
import com.fr.design.mainframe.vcs.common.VcsHelper;
import com.fr.design.mainframe.vcs.common.VcsCacheFileNodeFile;
import com.fr.file.filetree.FileNode;
import com.fr.report.entity.VcsEntity;
import com.fr.stable.StringUtils;
import com.fr.workspace.WorkContext;
import com.fr.workspace.server.vcs.VcsOperator;
import javax.swing.AbstractCellEditor;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.table.TableCellEditor;
import java.awt.Component;
public class FileVersionCellEditor extends AbstractCellEditor implements TableCellEditor {
private static final long serialVersionUID = -7299526575184810693L;
//第一行
private final JPanel firstRowPanel;
//其余行
private final FileVersionRowPanel renderAndEditor;
public FileVersionCellEditor() {
this.firstRowPanel = new FileVersionFirstRowPanel();
this.renderAndEditor = new FileVersionRowPanel();
}
@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
String fileOfVersion;
Component editor = row == 0 ? firstRowPanel : renderAndEditor;
if (isSelected) {
return editor;
} else if (row == 0) {
String path = DesignerFrameFileDealerPane.getInstance().getSelectedOperation().getFilePath();
fileOfVersion = WorkContext.getCurrent().get(VcsOperator.class).getFileOfCurrent(path.replaceFirst("/", ""));
} else {
renderAndEditor.update((VcsEntity) value);
fileOfVersion = WorkContext.getCurrent().get(VcsOperator.class).getFileOfFileVersion(((VcsEntity) value).getFilename(), ((VcsEntity) value).getVersion());
}
editor.setBackground(VcsHelper.TABLE_SELECT_BACKGROUND);
if (StringUtils.isNotEmpty(fileOfVersion)) {
//先关闭当前打开的模板版本
JTemplate jt = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate();
jt.stopEditing();
MutilTempalteTabPane.getInstance().setIsCloseCurrent(true);
MutilTempalteTabPane.getInstance().closeFormat(jt);
MutilTempalteTabPane.getInstance().closeSpecifiedTemplate(jt);
//再打开cache中的模板
DesignerContext.getDesignerFrame().openTemplate(new VcsCacheFileNodeFile(new FileNode(fileOfVersion, false)));
}
double height = editor.getPreferredSize().getHeight();
if (table.getRowHeight(row) != height) {
table.setRowHeight(row, (int) height);
}
return editor;
}
@Override
public Object getCellEditorValue() {
return renderAndEditor.getVcsEntity();
}
}

43
designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/FileVersionCellRender.java

@ -0,0 +1,43 @@
package com.fr.design.mainframe.vcs.ui;
import com.fr.report.entity.VcsEntity;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;
import java.awt.Component;
import static com.fr.design.constants.UIConstants.TREE_BACKGROUND;
import static com.fr.design.mainframe.vcs.common.VcsHelper.TABLE_SELECT_BACKGROUND;
public class FileVersionCellRender implements TableCellRenderer {
//第一行
private final JPanel firstRowPanel;
//其余行
private final FileVersionRowPanel render;
public FileVersionCellRender() {
this.render = new FileVersionRowPanel();
this.firstRowPanel = new FileVersionFirstRowPanel();
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
Component editor = row == 0 ? firstRowPanel : render;
// https://stackoverflow.com/questions/3054775/jtable-strange-behavior-from-getaccessiblechild-method-resulting-in-null-point
if (value != null) {
render.update((VcsEntity) value);
}
editor.setBackground(isSelected ? TABLE_SELECT_BACKGROUND : TREE_BACKGROUND);
double height = editor.getPreferredSize().getHeight();
if (table.getRowHeight(row) != height) {
table.setRowHeight(row, (int) height);
}
return editor;
}
}

94
designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/FileVersionDialog.java

@ -0,0 +1,94 @@
package com.fr.design.mainframe.vcs.ui;
import com.fr.design.dialog.UIDialog;
import com.fr.design.editor.editor.DateEditor;
import com.fr.design.gui.date.UIDatePicker;
import com.fr.design.gui.ibutton.UIButton;
import com.fr.design.gui.ilable.UILabel;
import com.fr.design.gui.itextfield.UITextField;
import com.fr.design.i18n.Toolkit;
import com.fr.design.mainframe.vcs.common.VcsHelper;
import com.fr.report.entity.VcsEntity;
import com.fr.stable.StringUtils;
import com.fr.workspace.WorkContext;
import com.fr.workspace.server.vcs.VcsOperator;
import javax.swing.AbstractAction;
import javax.swing.Box;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;
import java.util.List;
public class FileVersionDialog extends UIDialog {
public static final long DELAY = 24 * 60 * 60 * 1000;
private UIButton okBtn;
private UIButton cancelBtn;
private DateEditor dateEditor;
private UITextField textField;
public FileVersionDialog(Frame frame) {
super(frame);
setUndecorated(true);
setModal(true);
JPanel panel = new JPanel(new BorderLayout());
Box upBox = Box.createHorizontalBox();
upBox.setBorder(VcsHelper.EMPTY_BORDER);
upBox.add(new UILabel(Toolkit.i18nText("Fine-Design_Vcs_buildTime") + " "));
upBox.add(Box.createHorizontalGlue());
dateEditor = new DateEditor(new Date(), true, StringUtils.EMPTY, UIDatePicker.STYLE_CN_DATE1);
upBox.add(dateEditor);
Box downBox = Box.createHorizontalBox();
downBox.setBorder(VcsHelper.EMPTY_BORDER);
downBox.add(new UILabel(Toolkit.i18nText("Fine-Design_Vcs_CommitMsg") + " "));
textField = new UITextField();
downBox.add(textField);
JPanel buttonPane = new JPanel(new FlowLayout(FlowLayout.RIGHT));
okBtn = new UIButton(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_OK"));
cancelBtn = new UIButton(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Utils_Design_Action_Cancel"));
buttonPane.setBorder(VcsHelper.EMPTY_BORDER);
buttonPane.add(okBtn);
buttonPane.add(cancelBtn);
okBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
FileVersionDialog.this.setVisible(false);
Date date = dateEditor.getValue();
List<VcsEntity> vcsEntities = WorkContext.getCurrent().get(VcsOperator.class).getFilterVersions(date, new Date(date.getTime() + DELAY), textField.getText());
FileVersionTable.getInstance().updateModel(1, vcsEntities);
}
});
cancelBtn.addActionListener(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
FileVersionDialog.this.setVisible(false);
}
});
panel.add(upBox, BorderLayout.NORTH);
panel.add(downBox, BorderLayout.CENTER);
panel.add(buttonPane, BorderLayout.SOUTH);
add(panel);
setSize(new Dimension(230, 105));
centerWindow(this);
}
private void centerWindow(Window window) {
window.setLocation(0, 95);
}
@Override
public void checkValid() throws Exception {
}
}

21
designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/FileVersionFirstRowPanel.java

@ -0,0 +1,21 @@
package com.fr.design.mainframe.vcs.ui;
import com.fr.design.gui.ilable.UILabel;
import com.fr.design.i18n.Toolkit;
import javax.swing.Box;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import java.awt.BorderLayout;
public class FileVersionFirstRowPanel extends JPanel {
public FileVersionFirstRowPanel() {
super(new BorderLayout());
Box upPane = Box.createVerticalBox();
upPane.setBorder(new EmptyBorder(5, 10, 5, 10));
upPane.add(new UILabel(Toolkit.i18nText("Fine-Design_Vcs_Local_User")));
add(upPane, BorderLayout.CENTER);
}
}

150
designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/FileVersionRowPanel.java

@ -0,0 +1,150 @@
package com.fr.design.mainframe.vcs.ui;
import com.fr.design.gui.frpane.UITextPane;
import com.fr.design.gui.ibutton.UIButton;
import com.fr.design.gui.ilable.UILabel;
import com.fr.design.i18n.Toolkit;
import com.fr.design.mainframe.DesignerFrameFileDealerPane;
import com.fr.design.mainframe.vcs.common.VcsHelper;
import com.fr.log.FineLoggerFactory;
import com.fr.report.entity.VcsEntity;
import com.fr.stable.StringUtils;
import com.fr.workspace.WorkContext;
import com.fr.workspace.server.vcs.VcsOperator;
import javax.swing.Box;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.text.BadLocationException;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.SimpleDateFormat;
import java.util.Date;
public class FileVersionRowPanel extends JPanel {
private VcsEntity vcsEntity;
private UILabel versionLabel = new UILabel();
private UILabel usernameLabel = new UILabel(StringUtils.EMPTY, VcsHelper.VCS_USER_PNG, SwingConstants.LEFT);
private UITextPane msgLabel = new UITextPane();
private UILabel timeLabel = new UILabel();
private EditFileVersionDialog editDialog;
public FileVersionRowPanel() {
setLayout(new BorderLayout());
// version + username
Box upPane = Box.createHorizontalBox();
upPane.setBorder(VcsHelper.EMPTY_BORDER);
upPane.add(versionLabel);
upPane.add(Box.createHorizontalGlue());
// msg
msgLabel.setBorder(VcsHelper.EMPTY_BORDER);
msgLabel.setOpaque(false);
msgLabel.setBackground(new Color(0, 0, 0, 0));
msgLabel.setEditable(false);
// confirm + delete + edit
UIButton confirmBtn = new UIButton(VcsHelper.VCS_REVERT);
confirmBtn.set4ToolbarButton();
confirmBtn.setToolTipText(Toolkit.i18nText("Fine-Design_Vcs_Revert"));
confirmBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
if (JOptionPane.showConfirmDialog(null, Toolkit.i18nText("Fine-Design_Vcs_Version_Revert_Confirm"), Toolkit.i18nText("Fine-Design_Vcs_Version_Revert_Title"),
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
WorkContext.getCurrent().get(VcsOperator.class).rollbackTo(vcsEntity);
FileVersionsPanel.getInstance().exitVcs(vcsEntity.getFilename());
}
}
});
UIButton deleteBtn = new UIButton(VcsHelper.VCS_DELETE_PNG);
deleteBtn.set4ToolbarButton();
deleteBtn.setToolTipText(Toolkit.i18nText("Fine-Design_Vcs_Delete"));
deleteBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
if (JOptionPane.showConfirmDialog(null, Toolkit.i18nText("Fine-Design_Vcs_Delete-Confirm"), Toolkit.i18nText("Fine-Design_Vcs_Remove"),
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
try {
WorkContext.getCurrent().get(VcsOperator.class).deleteVersion(vcsEntity.getFilename(), vcsEntity.getVersion());
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage());
}
FileVersionTable table = (FileVersionTable) (FileVersionRowPanel.this.getParent());
String path = DesignerFrameFileDealerPane.getInstance().getSelectedOperation().getFilePath();
try {
table.updateModel(table.getSelectedRow() - 1, WorkContext.getCurrent().get(VcsOperator.class).getVersions(path.replaceFirst("/", "")));
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage());
}
}
}
});
UIButton editBtn = new UIButton(VcsHelper.VCS_EDIT_PNG);
editBtn.set4ToolbarButton();
editBtn.setToolTipText(Toolkit.i18nText("Fine-Design_Vcs_Edit"));
editBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
showEditDialog();
}
});
upPane.add(editBtn);
upPane.add(confirmBtn);
upPane.add(deleteBtn);
Box downPane = Box.createHorizontalBox();
downPane.add(usernameLabel);
downPane.setBorder(VcsHelper.EMPTY_BORDER_BOTTOM);
downPane.add(Box.createHorizontalGlue());
timeLabel.setForeground(VcsHelper.COPY_VERSION_BTN_COLOR);
downPane.add(timeLabel);
add(upPane, BorderLayout.NORTH);
add(msgLabel, BorderLayout.CENTER);
add(downPane, BorderLayout.SOUTH);
}
private void showEditDialog() {
this.editDialog = new EditFileVersionDialog(vcsEntity);
editDialog.setVisible(true);
update(vcsEntity);
}
public void update(final VcsEntity fileVersion) {
this.vcsEntity = fileVersion;
versionLabel.setText(String.format("V.%s", fileVersion.getVersion()));
usernameLabel.setText(fileVersion.getUsername());
msgLabel.setText(StringUtils.EMPTY);
timeLabel.setText(timeStr(fileVersion.getTime()));
try {
StyledDocument doc = msgLabel.getStyledDocument();
Style style = msgLabel.getLogicalStyle();
StyleConstants.setForeground(style, Color.BLACK);
doc.insertString(doc.getLength(), " " + fileVersion.getCommitMsg(), style);
} catch (BadLocationException e) {
FineLoggerFactory.getLogger().error(e.getMessage());
}
}
private String timeStr(Date time) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return simpleDateFormat.format(time);
}
public VcsEntity getVcsEntity() {
return vcsEntity;
}
}

79
designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/FileVersionTable.java

@ -0,0 +1,79 @@
package com.fr.design.mainframe.vcs.ui;
import com.fr.report.entity.VcsEntity;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.table.AbstractTableModel;
import java.util.ArrayList;
import java.util.List;
public class FileVersionTable extends JTable {
private static volatile FileVersionTable instance;
private FileVersionTable() {
super(new CellModel(new ArrayList<VcsEntity>()));
setDefaultRenderer(VcsEntity.class, new FileVersionCellRender());
setDefaultEditor(VcsEntity.class, new FileVersionCellEditor());
setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
setTableHeader(null);
setRowHeight(30);
}
public static FileVersionTable getInstance() {
if (instance == null) {
synchronized (FileVersionTable.class) {
if (instance == null) {
instance = new FileVersionTable();
}
}
}
return instance;
}
public void updateModel(int selectedRow, List<VcsEntity> entities) {
if (selectedRow > entities.size()) {
selectedRow = entities.size();
}
setModel(new CellModel(entities));
editCellAt(selectedRow, 0);
setRowSelectionInterval(selectedRow, selectedRow);
}
private static class CellModel extends AbstractTableModel {
private static final long serialVersionUID = -6078568799037607690L;
private List<VcsEntity> vcsEntities;
CellModel(List<VcsEntity> vcsEntities) {
this.vcsEntities = vcsEntities;
}
public Class getColumnClass(int columnIndex) {
return VcsEntity.class;
}
public int getColumnCount() {
return 1;
}
public int getRowCount() {
return (vcsEntities == null) ? 0 : vcsEntities.size() + 1;
}
public Object getValueAt(int rowIndex, int columnIndex) {
if (rowIndex == 0) {
return null;
}
return (vcsEntities == null) ? null : vcsEntities.get(rowIndex - 1);
}
public boolean isCellEditable(int columnIndex, int rowIndex) {
return true;
}
}
}

200
designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/FileVersionsPanel.java

@ -0,0 +1,200 @@
package com.fr.design.mainframe.vcs.ui;
import com.fr.base.GraphHelper;
import com.fr.design.base.mode.DesignModeContext;
import com.fr.design.dialog.BasicPane;
import com.fr.design.file.HistoryTemplateListCache;
import com.fr.design.file.MutilTempalteTabPane;
import com.fr.design.gui.ibutton.UIButton;
import com.fr.design.gui.icontainer.UIScrollPane;
import com.fr.design.gui.ilable.UILabel;
import com.fr.design.gui.itoolbar.UIToolbar;
import com.fr.design.mainframe.DesignerContext;
import com.fr.design.mainframe.DesignerFrameFileDealerPane;
import com.fr.design.mainframe.JTemplate;
import com.fr.design.mainframe.ToolBarNewTemplatePane;
import com.fr.design.mainframe.WestRegionContainerPane;
import com.fr.design.mainframe.vcs.common.VcsHelper;
import com.fr.design.menu.ToolBarDef;
import com.fr.file.FileNodeFILE;
import com.fr.file.filetree.FileNode;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.StableUtils;
import com.fr.stable.project.ProjectConstants;
import com.fr.workspace.WorkContext;
import com.fr.workspace.server.vcs.VcsOperator;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.SwingConstants;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class FileVersionsPanel extends BasicPane {
private static final String ELLIPSIS = "...";
private static volatile FileVersionsPanel instance;
private UILabel titleLabel;
private String templatePath;
private UIButton filterBtn;
private FileVersionDialog versionDialog;
private FileVersionsPanel() {
initComponents();
}
public static FileVersionsPanel getInstance() {
if (instance == null) {
synchronized (FileVersionsPanel.class) {
if (instance == null) {
instance = new FileVersionsPanel();
}
}
}
return instance;
}
private void initComponents() {
setLayout(new BorderLayout());
UIToolbar toolbar = ToolBarDef.createJToolBar();
toolbar.setBorder(BorderFactory.createEmptyBorder(2, 0, 2, 0));
toolbar.setBorderPainted(true);
Box upPane = Box.createHorizontalBox();
UIButton backBtn = new UIButton(VcsHelper.VCS_BACK_PNG);
backBtn.set4ToolbarButton();
backBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
exitVcs(templatePath);
}
});
toolbar.add(backBtn);
filterBtn = new UIButton(VcsHelper.VCS_FILTER_PNG);
filterBtn.set4ToolbarButton();
filterBtn.setHorizontalAlignment(SwingConstants.RIGHT);
filterBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
showFilterPane();
}
});
titleLabel = new UILabel() {
@Override
public Dimension getMaximumSize() {
return new Dimension(257, 21);
}
};
upPane.add(titleLabel);
upPane.add(Box.createHorizontalGlue());
upPane.add(filterBtn);
toolbar.add(Box.createHorizontalGlue());
toolbar.add(upPane);
add(toolbar, BorderLayout.NORTH);
UIScrollPane jScrollPane = new UIScrollPane(FileVersionTable.getInstance());
add(jScrollPane, BorderLayout.CENTER);
}
private void showFilterPane() {
versionDialog = new FileVersionDialog(DesignerContext.getDesignerFrame());
versionDialog.setVisible(true);
}
/**
* 退出版本管理并且打开模板
*
* @param path 被管理的模板的名字
*/
public void exitVcs(String path) {
// 关闭当前打开的版本
JTemplate<?, ?> jt = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate();
MutilTempalteTabPane.getInstance().setIsCloseCurrent(true);
MutilTempalteTabPane.getInstance().closeFormat(jt);
MutilTempalteTabPane.getInstance().closeSpecifiedTemplate(jt);
updateDesignerFrame(true);
final String selectedFilePath = StableUtils.pathJoin(ProjectConstants.REPORTLETS_NAME, path);
DesignerContext.getDesignerFrame().openTemplate(new FileNodeFILE(new FileNode(selectedFilePath, false)));
}
private void refreshVersionTablePane() {
templatePath = DesignerFrameFileDealerPane.getInstance().getSelectedOperation().getFilePath();
String[] paths = StableUtils.pathSplit(templatePath);
String filename = paths[paths.length - 1];
int width = FileVersionTable.getInstance().getWidth() - 40;
if (getStringWidth(filename) > width) {
filename = getEllipsisName(filename, width);
}
titleLabel.setText(filename);
String path = DesignerFrameFileDealerPane.getInstance().getSelectedOperation().getFilePath();
FileVersionTable.getInstance().updateModel(1, WorkContext.getCurrent().get(VcsOperator.class).getVersions(path.replaceFirst("/", "")));
}
public void showFileVersionsPane() {
updateDesignerFrame(false);
refreshVersionTablePane();
}
@Override
protected String title4PopupWindow() {
return null;
}
private void updateDesignerFrame(boolean isExit) {
// 左上侧面板
WestRegionContainerPane.getInstance().replaceUpPane(
isExit ? DesignerFrameFileDealerPane.getInstance() : this);
DesignModeContext.switchTo(isExit ? com.fr.design.base.mode.DesignerMode.NORMAL : com.fr.design.base.mode.DesignerMode.VCS);
// MutilTempalteTabPane & NewTemplatePane 是否可点
ToolBarNewTemplatePane.getInstance().setButtonGray(!isExit);
JTemplate<?, ?> currentEditingTemplate = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate();
if (currentEditingTemplate.isJWorkBook()) {
DesignerContext.getDesignerFrame().resetToolkitByPlus(currentEditingTemplate);
}
}
private int getStringWidth(String str) {
return GraphHelper.getFontMetrics(this.getFont()).stringWidth(str);
}
private String getEllipsisName(String name, int maxStringlength) {
//若是名字长度大于能显示的长度,那能显示的文字的最大长度还要减去省略号的最大长度
// int maxellipsislength = maxStringlength - ELLIPSIS.length();
int ellipsisWidth = getStringWidth(ELLIPSIS);
int leftkeyPoint = 0;
int rightKeyPoint = name.length() - 1;
int leftStrWidth = 0;
int rightStrWidth = 0;
while (leftStrWidth + rightStrWidth + ellipsisWidth < maxStringlength) {
if (leftStrWidth <= rightStrWidth) {
leftkeyPoint++;
} else {
rightKeyPoint--;
}
leftStrWidth = getStringWidth(name.substring(0, leftkeyPoint));
rightStrWidth = getStringWidth(name.substring(rightKeyPoint));
if (leftStrWidth + rightStrWidth + ellipsisWidth > maxStringlength) {
if (leftStrWidth <= rightStrWidth) {
rightKeyPoint++;
}
break;
}
}
return name.substring(0, leftkeyPoint) + ELLIPSIS + name.substring(rightKeyPoint);
}
}

6
designer-base/src/main/java/com/fr/design/onlineupdate/actions/FileDownloader.java → designer-base/src/main/java/com/fr/design/update/actions/FileDownloader.java

@ -1,9 +1,9 @@
package com.fr.design.onlineupdate.actions;
package com.fr.design.update.actions;
import com.fr.design.onlineupdate.domain.UpdateConstants;
import com.fr.design.update.domain.UpdateConstants;
import com.fr.locale.InterProviderFactory;
import com.fr.log.FineLoggerFactory;
import com.fr.design.onlineupdate.domain.DownloadItem;
import com.fr.design.update.domain.DownloadItem;
import com.fr.stable.ArrayUtils;
import com.fr.stable.StableUtils;

5
designer-base/src/main/java/com/fr/design/onlineupdate/actions/SoftwareUpdateAction.java → designer-base/src/main/java/com/fr/design/update/actions/SoftwareUpdateAction.java

@ -1,10 +1,9 @@
package com.fr.design.onlineupdate.actions;
package com.fr.design.update.actions;
import com.fr.base.BaseUtils;
import com.fr.design.actions.UpdateAction;
import com.fr.design.mainframe.DesignerContext;
import com.fr.design.onlineupdate.ui.dialog.UpdateMainDialog;
import com.fr.locale.InterProviderFactory;
import com.fr.design.update.ui.dialog.UpdateMainDialog;
import java.awt.event.ActionEvent;

2
designer-base/src/main/java/com/fr/design/onlineupdate/domain/DownloadItem.java → designer-base/src/main/java/com/fr/design/update/domain/DownloadItem.java

@ -1,4 +1,4 @@
package com.fr.design.onlineupdate.domain;
package com.fr.design.update.domain;
import com.fr.general.ComparatorUtils;
import com.fr.json.JSONObject;

2
designer-base/src/main/java/com/fr/design/onlineupdate/domain/UpdateConstants.java → designer-base/src/main/java/com/fr/design/update/domain/UpdateConstants.java

@ -1,4 +1,4 @@
package com.fr.design.onlineupdate.domain;
package com.fr.design.update.domain;
/**
* Created by XINZAI on 2018/8/21.

2
designer-base/src/main/java/com/fr/design/onlineupdate/domain/UpdateInfoCachePropertyManager.java → designer-base/src/main/java/com/fr/design/update/domain/UpdateInfoCachePropertyManager.java

@ -1,4 +1,4 @@
package com.fr.design.onlineupdate.domain;
package com.fr.design.update.domain;
import com.fr.log.FineLoggerFactory;

2
designer-base/src/main/java/com/fr/design/onlineupdate/factory/DirectoryOperationFactory.java → designer-base/src/main/java/com/fr/design/update/factory/DirectoryOperationFactory.java

@ -1,4 +1,4 @@
package com.fr.design.onlineupdate.factory;
package com.fr.design.update.factory;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.ArrayUtils;

61
designer-base/src/main/java/com/fr/design/update/push/DesignerPushUpdateConfigManager.java

@ -0,0 +1,61 @@
package com.fr.design.update.push;
import com.fr.stable.StringUtils;
import com.fr.stable.xml.XMLPrintWriter;
import com.fr.stable.xml.XMLReadable;
import com.fr.stable.xml.XMLWriter;
import com.fr.stable.xml.XMLableReader;
/**
* 持久化与设计器自动推送更新相关的配置
* Created by plough on 2019/4/8.
*/
public class DesignerPushUpdateConfigManager implements XMLReadable, XMLWriter {
public static final String XML_TAG = "DesignerPushUpdateConfigManager";
private static DesignerPushUpdateConfigManager singleton;
private boolean autoPushUpdateEnabled = true; // 是否开启自动推送更新
private String lastIgnoredVersion = StringUtils.EMPTY; // 最近一次跳过的更新版本
private DesignerPushUpdateConfigManager() {
}
public static DesignerPushUpdateConfigManager getInstance() {
if (singleton == null) {
singleton = new DesignerPushUpdateConfigManager();
}
return singleton;
}
@Override
public void readXML(XMLableReader reader) {
if (reader.isAttr()) {
this.setAutoPushUpdateEnabled(reader.getAttrAsBoolean("autoPushUpdateEnabled", true));
this.setLastIgnoredVersion(reader.getAttrAsString("lastIgnoredVersion", StringUtils.EMPTY));
}
}
@Override
public void writeXML(XMLPrintWriter writer) {
writer.startTAG(XML_TAG);
writer.attr("autoPushUpdateEnabled", autoPushUpdateEnabled);
writer.attr("lastIgnoredVersion", lastIgnoredVersion);
writer.end();
}
public boolean isAutoPushUpdateEnabled() {
return autoPushUpdateEnabled;
}
public void setAutoPushUpdateEnabled(boolean autoPushUpdateEnabled) {
this.autoPushUpdateEnabled = autoPushUpdateEnabled;
}
public String getLastIgnoredVersion() {
return lastIgnoredVersion;
}
public void setLastIgnoredVersion(String lastIgnoredVersion) {
this.lastIgnoredVersion = lastIgnoredVersion;
}
}

190
designer-base/src/main/java/com/fr/design/update/push/DesignerPushUpdateDialog.java

@ -0,0 +1,190 @@
package com.fr.design.update.push;
import com.fr.design.dialog.UIDialog;
import com.fr.design.ui.ModernUIPane;
import com.fr.design.utils.BrowseUtils;
import com.fr.design.utils.gui.GUICoreUtils;
import com.fr.intelli.record.FocusPoint;
import com.fr.intelli.record.MetricRegistry;
import com.fr.intelli.record.Original;
import com.fr.stable.StringUtils;
import com.fr.web.struct.AssembleComponent;
import com.fr.web.struct.Atom;
import com.fr.web.struct.browser.RequestClient;
import com.fr.web.struct.category.ScriptPath;
import com.fr.web.struct.category.StylePath;
import com.fr.web.struct.impl.FineUI;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Frame;
/**
* Created by plough on 2019/4/10.
*/
class DesignerPushUpdateDialog extends UIDialog {
public static final Dimension DEFAULT = new Dimension(640, 360);
private ModernUIPane<Model> jsPane;
private DesignerPushUpdateDialog(Frame parent) {
super(parent);
setModal(true);
initComponents();
}
static void createAndShow(Frame parent, DesignerUpdateInfo updateInfo) {
DesignerPushUpdateDialog dialog = new DesignerPushUpdateDialog(parent);
dialog.populate(updateInfo);
dialog.showDialog();
}
private void initComponents() {
JPanel contentPane = (JPanel) getContentPane();
contentPane.setLayout(new BorderLayout());
jsPane = new ModernUIPane.Builder<Model>()
.withComponent(new AssembleComponent() {
@Override
public ScriptPath script(RequestClient req) {
return ScriptPath.build("/com/fr/design/ui/update/push/pushUpdate.js");
}
@Override
public StylePath style(RequestClient req) {
return StylePath.build("/com/fr/design/ui/update/push/pushUpdate.css");
}
@Override
public Atom[] refer() {
return new Atom[]{FineUI.KEY};
}
}).namespace("Pool").build();
contentPane.add(jsPane);
}
private void populate(DesignerUpdateInfo updateInfo) {
Model model = createModel(updateInfo);
jsPane.populate(model);
}
private Model createModel(DesignerUpdateInfo updateInfo) {
Model model = new Model();
model.setVersion(updateInfo.getPushVersion());
model.setContent(updateInfo.getPushContent());
model.setMoreInfoUrl(updateInfo.getMoreInfoUrl());
model.setBackgroundUrl(updateInfo.getBackgroundUrl());
return model;
}
@Override
public void checkValid() throws Exception {
// do nothing
}
/**
* 显示窗口
*/
private void showDialog() {
setSize(DEFAULT);
setUndecorated(true);
GUICoreUtils.centerWindow(this);
setVisible(true);
}
public class Model {
private String version;
private String content;
private String moreInfoUrl;
private String backgroundUrl;
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public void browseMoreInfoUrl() {
if (StringUtils.isNotEmpty(moreInfoUrl)) {
BrowseUtils.browser(moreInfoUrl);
}
}
public void setMoreInfoUrl(String moreInfoUrl) {
this.moreInfoUrl = moreInfoUrl;
}
public String getBackgroundUrl() {
return backgroundUrl;
}
public void setBackgroundUrl(String backgroundUrl) {
this.backgroundUrl = backgroundUrl;
}
public void updateNow() {
DesignerPushUpdateManager.getInstance().doUpdate();
FocusPointManager.submit(FocusPointManager.OperateType.UPDATE);
exit();
}
public void remindNextTime() {
FocusPointManager.submit(FocusPointManager.OperateType.REMIND_NEXT_TIME);
exit();
}
public void skipThisVersion() {
DesignerPushUpdateManager.getInstance().skipCurrentPushVersion();
FocusPointManager.submit(FocusPointManager.OperateType.SKIP);
exit();
}
public void closeWindow() {
FocusPointManager.submit(FocusPointManager.OperateType.CLOSE_WINDOW);
exit();
}
public String i18nText(String key) {
return com.fr.design.i18n.Toolkit.i18nText(key);
}
private void exit() {
DesignerPushUpdateDialog.this.dialogExit();
}
}
private static class FocusPointManager {
private static final String ID = "com.fr.update.push";
private static final String TITLE = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Push_Update_Focus_Point");
private enum OperateType {
CLOSE_WINDOW(0), UPDATE(1), REMIND_NEXT_TIME(2), SKIP(3);
private int index;
OperateType(int index) {
this.index = index;
}
private String toText() {
return String.valueOf(this.index);
}
}
private static void submit(OperateType opType) {
FocusPoint focusPoint = FocusPoint.create(ID, opType.toText(), Original.EMBED);
focusPoint.setTitle(TITLE);
MetricRegistry.getMetric().submit(focusPoint);
}
}
}

168
designer-base/src/main/java/com/fr/design/update/push/DesignerPushUpdateManager.java

@ -0,0 +1,168 @@
package com.fr.design.update.push;
import com.fr.design.event.DesignerOpenedListener;
import com.fr.design.mainframe.DesignerContext;
import com.fr.design.mainframe.DesignerFrame;
import com.fr.design.update.ui.dialog.UpdateMainDialog;
import com.fr.general.CloudCenter;
import com.fr.general.GeneralContext;
import com.fr.general.GeneralUtils;
import com.fr.general.http.HttpToolbox;
import com.fr.json.JSONObject;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.StringUtils;
import com.fr.workspace.WorkContext;
/**
* Created by plough on 2019/4/8.
*/
public class DesignerPushUpdateManager {
private static final String SPLIT_CHAR = "-";
private static DesignerPushUpdateManager singleton;
private DesignerUpdateInfo updateInfo;
static {
if (DesignerPushUpdateConfigManager.getInstance().isAutoPushUpdateEnabled()) {
DesignerContext.getDesignerFrame().addDesignerOpenedListener(new DesignerOpenedListener() {
@Override
public void designerOpened() {
getInstance().checkAndPop();
}
});
}
}
private DesignerPushUpdateManager() {
}
public static DesignerPushUpdateManager getInstance() {
if (singleton == null) {
singleton = new DesignerPushUpdateManager();
}
return singleton;
}
private void initUpdateInfo(String currentVersion, String latestVersion) {
String lastIgnoredVersion = DesignerPushUpdateConfigManager.getInstance().getLastIgnoredVersion();
String updatePushInfo = CloudCenter.getInstance().acquireUrlByKind("update.push");
JSONObject pushData = new JSONObject(updatePushInfo);
updateInfo = new DesignerUpdateInfo(currentVersion, latestVersion, lastIgnoredVersion, pushData);
}
private String getFullLatestVersion() {
try {
String res = HttpToolbox.get(CloudCenter.getInstance().acquireUrlByKind("jar10.update"));
return new JSONObject(res).optString("buildNO");
} catch (Throwable e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
return StringUtils.EMPTY;
}
private String getVersionByFullNO(String fullNO) {
if (fullNO.contains(SPLIT_CHAR)) {
fullNO = fullNO.substring(fullNO.lastIndexOf(SPLIT_CHAR) + 1);
}
return fullNO;
}
private String getPrefixByFullNO(String fullNO) {
if (fullNO.contains(SPLIT_CHAR)) {
return fullNO.substring(0, fullNO.lastIndexOf(SPLIT_CHAR));
}
return StringUtils.EMPTY;
}
/**
* "自动更新推送"选项是否生效
*/
public boolean isAutoPushUpdateSupported() {
boolean isLocalEnv = WorkContext.getCurrent().isLocal();
boolean isChineseEnv = GeneralContext.isChineseEnv();
return isAutoPushUpdateSupported(isLocalEnv, isChineseEnv);
}
private boolean isAutoPushUpdateSupported(boolean isLocalEnv, boolean isChineseEnv) {
// 远程设计和非中文环境,都不生效
return isLocalEnv && isChineseEnv;
}
/**
* 检查更新如果有合适的更新版本则弹窗
*/
private void checkAndPop() {
new Thread() {
@Override
public void run() {
if (!shouldPopUp()) {
FineLoggerFactory.getLogger().debug("skip push update");
return;
}
final DesignerFrame designerFrame = DesignerContext.getDesignerFrame();
DesignerPushUpdateDialog.createAndShow(designerFrame, updateInfo);
}
}.start();
}
private boolean shouldPopUp() {
if (updateInfo == null) {
String fullCurrentVersion = GeneralUtils.readFullBuildNO();
String fullLatestVersion = getFullLatestVersion();
boolean isValidJarVersion = isValidJarVersion(fullCurrentVersion, fullLatestVersion);
if (!isValidJarVersion) {
FineLoggerFactory.getLogger().info("Jar version is not valid for push update.");
return false;
} else {
String currentVersion = getVersionByFullNO(fullCurrentVersion);
String latestVersion = getVersionByFullNO(fullLatestVersion);
initUpdateInfo(currentVersion, latestVersion);
}
}
return isAutoPushUpdateSupported() && updateInfo.hasNewPushVersion();
}
private boolean isValidJarVersion(String fullCurrentVersion, String fullLatestVersion) {
// todo: 目前设定的逻辑是 feature/release/stable 都弹,且不区分版本号。后期肯定要变的,注释代码先留着
// // 无效的情况:
// // 1. 版本号格式有误
// // 2. 当前用的是 release 或 feature 的 jar 包
// // 3. 代码启动的
// String prefix = getPrefixByFullNO(fullLatestVersion);
// return StringUtils.isNotEmpty(prefix) && fullCurrentVersion.startsWith(prefix);
// 无效的情况:
// 1. 版本号格式有误(正常情况下都有前缀,只有异常的时候才可能出现)
// 2. 代码启动的(fullCurrentVersion 为"不是安装版本")
String prefix = getPrefixByFullNO(fullLatestVersion);
return StringUtils.isNotEmpty(prefix) && fullCurrentVersion.contains(SPLIT_CHAR);
}
/**
* 跳转到更新升级窗口并自动开始更新
*/
void doUpdate() {
new Thread() {
@Override
public void run() {
UpdateMainDialog dialog = new UpdateMainDialog(DesignerContext.getDesignerFrame());
dialog.setAutoUpdateAfterInit();
dialog.showDialog();
}
}.start();
}
/**
* 跳过当前的推送版本
*/
void skipCurrentPushVersion() {
if (updateInfo == null) {
return;
}
DesignerPushUpdateConfigManager.getInstance().setLastIgnoredVersion(updateInfo.getPushVersion());
}
}

91
designer-base/src/main/java/com/fr/design/update/push/DesignerUpdateInfo.java

@ -0,0 +1,91 @@
package com.fr.design.update.push;
import com.fr.general.ComparatorUtils;
import com.fr.json.JSONObject;
import com.fr.stable.StringUtils;
import java.security.InvalidParameterException;
/**
* Created by plough on 2019/4/8.
*/
class DesignerUpdateInfo {
private static final String KEY_VERSION = "version";
private static final String KEY_CONTENT = "content";
private static final String KEY_BACKGROUND_URL = "background";
private static final String KEY_MORE_INFO_URL = "more";
private final String currentVersion; // 当前版本
private final String latestVersion; // 最新版本
private final String lastIgnoredVersion; // 最近一次跳过的版本
private final String pushVersion; // 推送版本
private final String pushContent; // 推送更新内容
private final String backgroundUrl; // 推送背景图片 url
private final String moreInfoUrl; // 更多新特性
DesignerUpdateInfo(String currentVersion, String latestVersion, String lastIgnoredVersion, JSONObject pushData) {
this.currentVersion = currentVersion;
this.latestVersion = latestVersion;
this.lastIgnoredVersion = lastIgnoredVersion;
this.pushVersion = pushData.optString(KEY_VERSION);
this.pushContent = pushData.optString(KEY_CONTENT);
this.backgroundUrl = pushData.optString(KEY_BACKGROUND_URL);
this.moreInfoUrl = pushData.optString(KEY_MORE_INFO_URL);
// 简单做下参数校验
if (hasEmptyField()) {
throw new InvalidParameterException();
}
}
private boolean hasEmptyField() {
// lastIgnoredVersion 可以为空
return StringUtils.isEmpty(currentVersion)
|| StringUtils.isEmpty(latestVersion)
|| StringUtils.isEmpty(pushVersion)
|| StringUtils.isEmpty(pushContent)
|| StringUtils.isEmpty(backgroundUrl)
|| StringUtils.isEmpty(moreInfoUrl);
}
String getCurrentVersion() {
return currentVersion;
}
String getLatestVersion() {
return latestVersion;
}
String getLastIgnoredVersion() {
return lastIgnoredVersion;
}
String getPushVersion() {
return pushVersion;
}
String getPushContent() {
return pushContent;
}
String getBackgroundUrl() {
return backgroundUrl;
}
String getMoreInfoUrl() {
return moreInfoUrl;
}
boolean hasNewPushVersion() {
boolean result = ComparatorUtils.compare(pushVersion, currentVersion) > 0
&& ComparatorUtils.compare(pushVersion, latestVersion) <= 0;
if (StringUtils.isNotEmpty(lastIgnoredVersion)) {
result = result && ComparatorUtils.compare(pushVersion, lastIgnoredVersion) > 0;
}
return result;
}
}

2
designer-base/src/main/java/com/fr/design/onlineupdate/ui/dialog/EncodingDetect.java → designer-base/src/main/java/com/fr/design/update/ui/dialog/EncodingDetect.java

@ -1,4 +1,4 @@
package com.fr.design.onlineupdate.ui.dialog;
package com.fr.design.update.ui.dialog;
import java.io.File;

9
designer-base/src/main/java/com/fr/design/onlineupdate/ui/dialog/RestoreDialog.java → designer-base/src/main/java/com/fr/design/update/ui/dialog/RestoreDialog.java

@ -1,13 +1,12 @@
package com.fr.design.onlineupdate.ui.dialog;
package com.fr.design.update.ui.dialog;
import com.fr.design.gui.ibutton.UIButton;
import com.fr.design.layout.FRGUIPaneFactory;
import com.fr.design.mainframe.DesignerContext;
import com.fr.design.onlineupdate.domain.UpdateConstants;
import com.fr.design.onlineupdate.factory.DirectoryOperationFactory;
import com.fr.design.onlineupdate.ui.widget.ColorfulCellRender;
import com.fr.design.update.domain.UpdateConstants;
import com.fr.design.update.factory.DirectoryOperationFactory;
import com.fr.design.update.ui.widget.ColorfulCellRender;
import com.fr.design.utils.gui.GUICoreUtils;
import com.fr.locale.InterProviderFactory;
import com.fr.stable.ArrayUtils;
import com.fr.stable.StableUtils;

49
designer-base/src/main/java/com/fr/design/onlineupdate/ui/dialog/UpdateMainDialog.java → designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java

@ -1,4 +1,4 @@
package com.fr.design.onlineupdate.ui.dialog;
package com.fr.design.update.ui.dialog;
import com.fr.design.RestartHelper;
import com.fr.design.constants.LayoutConstants;
@ -10,17 +10,17 @@ import com.fr.design.gui.itextfield.UITextField;
import com.fr.design.layout.TableLayout;
import com.fr.design.layout.TableLayoutHelper;
import com.fr.design.mainframe.DesignerContext;
import com.fr.design.onlineupdate.actions.FileDownloader;
import com.fr.design.onlineupdate.domain.DownloadItem;
import com.fr.design.onlineupdate.domain.UpdateConstants;
import com.fr.design.onlineupdate.domain.UpdateInfoCachePropertyManager;
import com.fr.design.onlineupdate.factory.DirectoryOperationFactory;
import com.fr.design.onlineupdate.ui.widget.LoadingLabel;
import com.fr.design.onlineupdate.ui.widget.UpdateActionLabel;
import com.fr.design.onlineupdate.ui.widget.UpdateInfoTable;
import com.fr.design.onlineupdate.ui.widget.UpdateInfoTableCellRender;
import com.fr.design.onlineupdate.ui.widget.UpdateInfoTableModel;
import com.fr.design.onlineupdate.ui.widget.UpdateInfoTextAreaCellRender;
import com.fr.design.update.actions.FileDownloader;
import com.fr.design.update.domain.DownloadItem;
import com.fr.design.update.domain.UpdateConstants;
import com.fr.design.update.domain.UpdateInfoCachePropertyManager;
import com.fr.design.update.factory.DirectoryOperationFactory;
import com.fr.design.update.ui.widget.LoadingLabel;
import com.fr.design.update.ui.widget.UpdateActionLabel;
import com.fr.design.update.ui.widget.UpdateInfoTable;
import com.fr.design.update.ui.widget.UpdateInfoTableCellRender;
import com.fr.design.update.ui.widget.UpdateInfoTableModel;
import com.fr.design.update.ui.widget.UpdateInfoTextAreaCellRender;
import com.fr.design.utils.gui.GUICoreUtils;
import com.fr.general.ComparatorUtils;
import com.fr.general.DateUtils;
@ -58,7 +58,14 @@ import java.awt.Dimension;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@ -136,6 +143,8 @@ public class UpdateMainDialog extends UIDialog {
private String lastUpdateCacheTime;
private String lastUpdateCacheState = UPDATE_CACHE_STATE_FAIL;
private boolean autoUpdateAfterInit = false; // 是否在加载结束后,自动开始更新
public UpdateMainDialog(Dialog parent) {
super(parent);
initComponents();
@ -147,6 +156,13 @@ public class UpdateMainDialog extends UIDialog {
initComponents();
}
/**
* 等待面板初始化结束后点击"更新"按钮
*/
public void setAutoUpdateAfterInit() {
autoUpdateAfterInit = true;
}
private void initUpdateActionPane() {
double[] rowUpdateSubContentPaneSize = {UPDATE_CONTENT_PANE_ROW_SIZE, TableLayout.PREFERRED, UPDATE_CONTENT_PANE_ROW_SIZE};
double[] rowUpdateContentPaneSize = {TableLayout.PREFERRED};
@ -427,6 +443,7 @@ public class UpdateMainDialog extends UIDialog {
getUpdateInfoSuccess = true;
//step4:update cache file,start from cacheRecordTime,end latest server jartime
updateCachedInfoFile(jsonArray);
afterInit();
} catch (Exception e) {
getUpdateInfoSuccess = true;
FineLoggerFactory.getLogger().error(e.getMessage());
@ -435,6 +452,12 @@ public class UpdateMainDialog extends UIDialog {
};
}
private void afterInit() {
if (autoUpdateAfterInit) {
updateButton.doClick();
}
}
//从文件中读取缓存的更新信息
private void getCachedUpdateInfo(String keyword) throws Exception {
String cacheInfoPath = getUpdateCacheInfo();

2
designer-base/src/main/java/com/fr/design/onlineupdate/ui/widget/ColorfulCellRender.java → designer-base/src/main/java/com/fr/design/update/ui/widget/ColorfulCellRender.java

@ -1,4 +1,4 @@
package com.fr.design.onlineupdate.ui.widget;
package com.fr.design.update.ui.widget;
import javax.swing.JList;
import javax.swing.JPanel;

2
designer-base/src/main/java/com/fr/design/onlineupdate/ui/widget/LoadingLabel.java → designer-base/src/main/java/com/fr/design/update/ui/widget/LoadingLabel.java

@ -1,4 +1,4 @@
package com.fr.design.onlineupdate.ui.widget;
package com.fr.design.update.ui.widget;
import com.fr.design.gui.ilable.UILabel;
import com.fr.general.IOUtils;

2
designer-base/src/main/java/com/fr/design/onlineupdate/ui/widget/UpdateActionLabel.java → designer-base/src/main/java/com/fr/design/update/ui/widget/UpdateActionLabel.java

@ -1,4 +1,4 @@
package com.fr.design.onlineupdate.ui.widget;
package com.fr.design.update.ui.widget;
import com.fr.design.gui.ilable.UILabel;

2
designer-base/src/main/java/com/fr/design/onlineupdate/ui/widget/UpdateInfoTable.java → designer-base/src/main/java/com/fr/design/update/ui/widget/UpdateInfoTable.java

@ -1,4 +1,4 @@
package com.fr.design.onlineupdate.ui.widget;
package com.fr.design.update.ui.widget;
import javax.swing.JTable;
import javax.swing.table.TableModel;

2
designer-base/src/main/java/com/fr/design/onlineupdate/ui/widget/UpdateInfoTableCellRender.java → designer-base/src/main/java/com/fr/design/update/ui/widget/UpdateInfoTableCellRender.java

@ -1,4 +1,4 @@
package com.fr.design.onlineupdate.ui.widget;
package com.fr.design.update.ui.widget;
import com.fr.general.ComparatorUtils;
import com.fr.stable.StringUtils;

2
designer-base/src/main/java/com/fr/design/onlineupdate/ui/widget/UpdateInfoTableModel.java → designer-base/src/main/java/com/fr/design/update/ui/widget/UpdateInfoTableModel.java

@ -1,4 +1,4 @@
package com.fr.design.onlineupdate.ui.widget;
package com.fr.design.update.ui.widget;
import javax.swing.table.AbstractTableModel;
import java.util.List;

2
designer-base/src/main/java/com/fr/design/onlineupdate/ui/widget/UpdateInfoTextAreaCellRender.java → designer-base/src/main/java/com/fr/design/update/ui/widget/UpdateInfoTextAreaCellRender.java

@ -1,4 +1,4 @@
package com.fr.design.onlineupdate.ui.widget;
package com.fr.design.update.ui.widget;
import javax.swing.BorderFactory;
import javax.swing.JTable;

58
designer-base/src/main/resources/com/fr/aspectj/designerbase/TemplateProcessTracker.aj

@ -1,58 +0,0 @@
package com.fr.aspectj.designerbase;
/**
* 记录模板过程
* Created by plough on 2017/3/3.
*/
import org.aspectj.lang.reflect.SourceLocation;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
public aspect TemplateProcessTracker {
//声明一个pointcut,匹配你需要的方法
pointcut onMouseClicked(MouseEvent e):
execution(* mouseClicked(MouseEvent)) && args(e);
pointcut onMousePressed(MouseEvent e):
execution(* mousePressed(MouseEvent)) && args(e);
pointcut onMouseReleased(MouseEvent e):
execution(* mouseReleased(MouseEvent)) && args(e);
pointcut onActionPerformed(ActionEvent e):
execution(* actionPerformed(ActionEvent)) && args(e);
pointcut onSetValueAt(Object v, int r, int c):
execution(* setValueAt(java.lang.Object, int, int)) && args(v, r, c);
//before表示之前的意思
//这整个表示在MouseAdapter的public void mouseXXX(MouseEvent)方法调用之前,你想要执行的代码
before(MouseEvent e): onMouseClicked(e) || onMousePressed(e) || onMouseReleased(e) {
SourceLocation sl = thisJoinPoint.getSourceLocation();//切面对应的代码位置
//String log = String.format("%s:\n%s\n%s\n%s\n\n", new Date(), sl, e, e.getSource());
String log = "";
//TemplateInfoCollector.appendProcess(log);
}
//同上
before(ActionEvent e): onActionPerformed(e) {
SourceLocation sl = thisJoinPoint.getSourceLocation();
// !within(LogHandlerBar) 没用, 手动过滤
if (e != null && e.getSource().toString().contains("javax.swing.Timer")) {
return;
}
//String log = String.format("%s:\n%s\n%s\n%s\n\n", new Date(), sl, e, e.getSource());
String log = "";
//TemplateInfoCollector.appendProcess(log);
}
//同上
before(Object v, int r, int c): onSetValueAt(v, r, c) {
SourceLocation sl = thisJoinPoint.getSourceLocation();
//String log getSourceLocation= String.format("%s:\n%s\nset value: %s at (%d, %d)\n\n", new Date(), sl, v, r, c);
String log = "";
// TemplateInfoCollector.appendProcess(log);
}
}

BIN
designer-base/src/main/resources/com/fr/design/images/vcs/icon_back_normal@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 804 B

BIN
designer-base/src/main/resources/com/fr/design/images/vcs/icon_delete.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

BIN
designer-base/src/main/resources/com/fr/design/images/vcs/icon_delete@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

BIN
designer-base/src/main/resources/com/fr/design/images/vcs/icon_edit.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

BIN
designer-base/src/main/resources/com/fr/design/images/vcs/icon_edit@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 B

BIN
designer-base/src/main/resources/com/fr/design/images/vcs/icon_filter@1x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 B

BIN
designer-base/src/main/resources/com/fr/design/images/vcs/icon_filter@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 537 B

BIN
designer-base/src/main/resources/com/fr/design/images/vcs/icon_list_disabled.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 B

BIN
designer-base/src/main/resources/com/fr/design/images/vcs/icon_list_disabled@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 605 B

BIN
designer-base/src/main/resources/com/fr/design/images/vcs/icon_list_normal@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

BIN
designer-base/src/main/resources/com/fr/design/images/vcs/icon_revert.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 B

BIN
designer-base/src/main/resources/com/fr/design/images/vcs/icon_revert@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 971 B

BIN
designer-base/src/main/resources/com/fr/design/images/vcs/icon_save_disabled.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 467 B

BIN
designer-base/src/main/resources/com/fr/design/images/vcs/icon_save_disabled@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 926 B

BIN
designer-base/src/main/resources/com/fr/design/images/vcs/icon_save_normal@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 908 B

BIN
designer-base/src/main/resources/com/fr/design/images/vcs/icon_user@1x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

BIN
designer-base/src/main/resources/com/fr/design/images/vcs/icon_user@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 668 B

BIN
designer-base/src/main/resources/com/fr/design/images/vcs/vcs_back.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 B

BIN
designer-base/src/main/resources/com/fr/design/images/vcs/vcs_list.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

BIN
designer-base/src/main/resources/com/fr/design/images/vcs/vcs_save.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 B

86
designer-base/src/main/resources/com/fr/design/ui/update/push/pushUpdate.css

@ -0,0 +1,86 @@
body {
padding-left: 30px;
padding-top: 30px;
color: white;
background-size: 100% 100% !important;
-moz-background-size: 100% 100% !important;
}
.close-btn {
position: fixed !important;
font-size: 15px !important;
color: white !important;
top: 8px;
right: 10px;
width: 19px;
}
.close-btn .b-font {
height: 19px !important;
line-height: 19px !important;
}
.close-btn .b-font:before {
color: white !important;
}
.title {
font-family: PingFangSC-Semibold;
font-size: 24px;
letter-spacing: 1.5px;
line-height: 24px;
}
.version {
font-family: PingFangSC-Semibold;
font-size: 12px;
line-height: 14px;
margin-top: 6px;
}
.desc {
margin-top: 40px;
font-family: PingFangSC-Regular;
font-size: 13px;
width: 540px;
margin-left: 19px;
text-indent: -19px;
overflow: visible !important;
}
.desc .bi-label {
overflow: visible !important;
}
.moreInfo {
margin-top: 20px;
font-family: PingFangSC-Regular;
font-size: 13px;
text-decoration: underline;
}
.buttonGroup {
position: absolute !important;
bottom: 60px;
}
.buttonGroup .bi-button {
font-family: PingFangSC-Medium;
font-size: 11px;
text-align: center;
line-height: 12px;
box-shadow: 0 2px 4px 0 rgba(0,82,169,0.20);
border-radius: 12px;
}
.buttonGroup .button-ignore {
background-color: white !important;
border: 1px solid white !important;
color: #51A6FF;
}
.buttonGroup .button-common {
background-color: #51A6FF !important;
border: 1px solid #51A6FF !important;
}

149
designer-base/src/main/resources/com/fr/design/ui/update/push/pushUpdate.js

@ -0,0 +1,149 @@
var MAX_DESC_NUM = 5; // 最多显示5条信息
function i18nText(key) {
return Pool.data.i18nText(key);
}
function getDescArea(descList) {
var descItems = [];
for (var i in descList) {
var num = parseInt(i) + 1;
if (num > MAX_DESC_NUM) {
break;
}
descItems.push({
type: "bi.label",
text: num + ")" + descList[i],
whiteSpace: "pre-wrap",
textAlign: "left"
})
}
return BI.createWidget({
type: "bi.vertical",
cls: "desc",
items: descItems
});
}
function getTitleArea() {
return BI.createWidget({
type: "bi.vertical",
items: [
{
type: "bi.label",
text: i18nText("Fine-Design_Find_New_Version"),
cls: "title",
textAlign: "left"
},
{
type: "bi.label",
text: Pool.data.getVersion(),
cls: "version",
textAlign: "left"
}
]
});
}
function getButtonGroup() {
return BI.createWidget({
type: 'bi.left',
cls: "buttonGroup",
items: [
{
type: 'bi.button',
text: i18nText("Fine-Design_Update_Now"),
level: 'common',
height: 24,
handler: function () {
Pool.data.updateNow();
}
},
{
el: {
type: 'bi.button',
text: i18nText("Fine-Design_Remind_Me_Next_Time"),
level: 'ignore',
height: 24,
handler: function () {
Pool.data.remindNextTime();
}
},
lgap: 10
},
{
el: {
type: 'bi.button',
text: i18nText("Fine-Design_Skip_This_Version"),
level: 'ignore',
height: 24,
handler: function () {
Pool.data.skipThisVersion();
}
},
lgap: 10
}
]
});
}
function getMoreInfo() {
return BI.createWidget({
type: "bi.text_button",
text: i18nText("Fine-Design_See_More_New_Features"),
cls: "moreInfo",
textAlign: "left",
handler: function () {
// 为了使用系统的浏览器,只能从 Java 那边打开网页
Pool.data.browseMoreInfoUrl();
}
});
}
function getCloseButton() {
return BI.createWidget({
type: "bi.button",
text: "",
iconCls: "close-font",
cls: "close-btn",
clear: true,
handler: function () {
Pool.data.closeWindow();
}
});
}
function getShowItems() {
var title = getTitleArea();
var closeButton = getCloseButton();
var descList = Pool.data.getContent().split("\n");
var descArea = getDescArea(descList);
var moreInfo = getMoreInfo();
var buttonGroup = getButtonGroup();
var showItems = [title, closeButton, descArea];
if (descList.length > MAX_DESC_NUM) {
showItems.push(moreInfo);
}
showItems.push(buttonGroup);
return showItems;
}
window.addEventListener("load", function (ev) {
var showItems = getShowItems();
var container = BI.createWidget({
type:"bi.vertical",
element: "body",
cls: "container",
items: showItems
});
container.element.css("background", "url(" + Pool.data.getBackgroundUrl() + ")");
});

108
designer-base/src/test/java/com/fr/design/mainframe/template/info/DesignerOpenHistoryTest.java

@ -0,0 +1,108 @@
package com.fr.design.mainframe.template.info;
import com.fr.invoke.Reflect;
import com.fr.stable.StringUtils;
import com.fr.stable.xml.XMLPrintWriter;
import com.fr.stable.xml.XMLableReader;
import com.fr.third.javax.xml.stream.XMLStreamException;
import org.junit.Before;
import org.junit.Test;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* Created by plough on 2019/4/21.
*/
public class DesignerOpenHistoryTest {
private DesignerOpenHistory openHistory;
private String[] mockHistory = new String[] {
"2019-04-08", "2019-04-03", "2019-03-29"
};
@Before
public void setUp() {
openHistory = DesignerOpenHistory.getInstance();
Reflect.on(openHistory).set("history", mockHistory);
}
@Test
public void testReadAndWrite() throws XMLStreamException {
// 写入 xml
StringWriter sw = new StringWriter();
XMLPrintWriter writer = XMLPrintWriter.create(new PrintWriter(sw));
openHistory.writeXML(writer);
writer.flush();
writer.close();
String xmlText = sw.getBuffer().toString();
// 临时修改配置
Reflect.on(openHistory).set("history", new String[] {"", "", ""});
// 从 xml 中读取
StringReader sr = new StringReader(xmlText);
XMLableReader xmlReader = XMLableReader.createXMLableReader(sr);
xmlReader.readXMLObject(openHistory);
// 验证:与写入时的配置一致
assertArrayEquals(mockHistory, (String[])Reflect.on(openHistory).field("history").get());
}
@Test
public void testToString() {
assertEquals("2019-04-08,2019-04-03,2019-03-29", openHistory.toString());
}
@Test
public void testParseString() {
String[] mockDates = {"2020-04-08", "2019-04-03", "2016-03-29"};
Reflect.on(openHistory).call("parseString", StringUtils.join(",", mockDates));
assertArrayEquals(mockDates, (String[])Reflect.on(openHistory).field("history").get());
}
@Test
public void testGetHistorySpanDayCount() {
assertEquals(11, openHistory.getHistorySpanDayCount());
Reflect.on(openHistory).set("history", new String[] {"2019-05-03", "2019-05-02", ""});
assertEquals(2, openHistory.getHistorySpanDayCount());
Reflect.on(openHistory).set("history", new String[] {"2019-05-03", "", ""});
assertEquals(1, openHistory.getHistorySpanDayCount());
try {
Reflect.on(openHistory).set("history", new String[] {"", "", ""});
fail("should not be here");
} catch (AssertionError ignore) {
}
}
@Test
public void testHasOpenedToday() {
assertFalse(openHistory.hasOpenedToday());
Reflect.on(openHistory).set("history", new String[] {getToday(), "2019-02-02", ""});
assertTrue(openHistory.hasOpenedToday());
}
@Test
public void testUpdate() {
openHistory.update();
String[] arr = { getToday(), "2019-04-08", "2019-04-03" };
assertArrayEquals(arr, (String[])Reflect.on(openHistory).field("history").get());
}
private String getToday() {
return new SimpleDateFormat("yyyy-MM-dd").format(Calendar.getInstance().getTime());
}
}

35
designer-base/src/test/java/com/fr/design/mainframe/template/info/SendHelperTest.java

@ -0,0 +1,35 @@
package com.fr.design.mainframe.template.info;
import com.fr.invoke.Reflect;
import com.fr.stable.xml.XMLableReader;
import com.fr.third.javax.xml.stream.XMLStreamException;
import java.io.StringReader;
import static org.junit.Assert.assertTrue;
/**
* Created by plough on 2019/4/25.
*/
public class SendHelperTest {
private static final String CONSUMING_URL = "http://cloud.fanruan.com/api/monitor/record_of_reports_consuming/single";
private static final String PROCESS_URL = "http://cloud.fanruan.com/api/monitor/record_of_reports_process/single";
private static final String NORMAL_INFO = "<TemplateInfo templateID=\"16a988ce-8529-42f5-b17c-2ee849355071\" day_count=\"9\">\n" +
"<processMap process=\"\" float_count=\"0\" widget_count=\"0\" cell_count=\"1\" block_count=\"0\" report_type=\"0\"/>\n" +
"<consumingMap activitykey=\"2e0ea413-fa9c241e0-9723-4354fce51e81\" jar_time=\"不是安装版本\" create_time=\"2019-03-26 16:13\" uuid=\"476ca2cc-f789-4c5d-8e89-ef146580775c\" time_consume=\"129\" version=\"10.0\" username=\"plough\"/>\n" +
"</TemplateInfo>";
// 只在调试的时候运行,不需要每次都自动运行
public static void main(String[] args) throws XMLStreamException {
StringReader sr = new StringReader(NORMAL_INFO);
XMLableReader xmlReader = XMLableReader.createXMLableReader(sr);
TemplateInfo templateInfo = TemplateInfo.newInstanceByRead(xmlReader);
boolean res = Reflect.on(SendHelper.class).call("sendSingleTemplateInfo", CONSUMING_URL, templateInfo.getConsumingMapJsonString()).get();
assertTrue(res);
boolean res2 = Reflect.on(SendHelper.class).call("sendSingleTemplateInfo", PROCESS_URL, templateInfo.getProcessMapJsonString()).get();
assertTrue(res2);
}
}

195
designer-base/src/test/java/com/fr/design/mainframe/template/info/TemplateInfoCollectorTest.java

@ -0,0 +1,195 @@
package com.fr.design.mainframe.template.info;
import com.fr.config.MarketConfig;
import com.fr.general.GeneralUtils;
import com.fr.invoke.Reflect;
import com.fr.stable.ProductConstants;
import com.fr.stable.StringUtils;
import com.fr.third.org.apache.commons.io.FileUtils;
import org.easymock.EasyMock;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
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 java.io.File;
import java.io.IOException;
import java.util.Map;
import static com.fr.design.mainframe.template.info.TemplateInfoTestHelper.assertJsonStringEquals;
import static com.fr.design.mainframe.template.info.TemplateInfoTestHelper.setUpMockForNewInstance;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* Created by plough on 2019/4/18.
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest({ProductConstants.class, MarketConfig.class, ProductConstants.class, GeneralUtils.class})
public class TemplateInfoCollectorTest {
private String filePath;
private String initialFileContent;
private TemplateProcessInfo mockProcessInfo;
@Before
public void setUp() throws IOException {
PowerMock.mockStatic(ProductConstants.class);
filePath = getClass().getResource("tpl.info").getPath();
String dirPath = filePath.substring(0, filePath.indexOf("tpl.info"));
EasyMock.expect(ProductConstants.getEnvHome()).andReturn(dirPath).anyTimes();
EasyMock.replay();
PowerMock.replayAll();
mockProcessInfo = EasyMock.mock(TemplateProcessInfo.class);
EasyMock.expect(mockProcessInfo.getBlockCount()).andReturn(3).anyTimes();
EasyMock.expect(mockProcessInfo.getCellCount()).andReturn(13).anyTimes();
EasyMock.expect(mockProcessInfo.getFloatCount()).andReturn(1).anyTimes();
EasyMock.expect(mockProcessInfo.getReportType()).andReturn(0).anyTimes();
EasyMock.expect(mockProcessInfo.getWidgetCount()).andReturn(0).anyTimes();
EasyMock.replay(mockProcessInfo);
// 缓存 tpl.info
initialFileContent = FileUtils.readFileToString(new File(filePath), "utf-8");
Reflect.on(TemplateInfoCollector.class).set("instance", null);
}
@After
public void tearDown() throws IOException {
// 恢复 tpl.info
FileUtils.writeStringToFile(new File(filePath), initialFileContent, "utf-8");
}
@Test
public void testReadXML() {
assertEquals(",,", DesignerOpenHistory.getInstance().toString());
TemplateInfoCollector collector = TemplateInfoCollector.getInstance();
assertEquals(7, ((Map) Reflect.on(collector).field("templateInfoMap").get()).size());
assertEquals("2019-04-08,2019-04-03,2019-03-29", DesignerOpenHistory.getInstance().toString());
}
@Test
public void testCollectInfo() {
TemplateInfoCollector collector = TemplateInfoCollector.getInstance();
String templateID = "16a988ce-8529-42f5-b17c-2ee849355071";
int timeConsume = 200;
collector.collectInfo(templateID, StringUtils.EMPTY, mockProcessInfo, timeConsume);
// 检查是否写入成功
collector.loadFromFile();
TemplateInfo templateInfo = collector.getOrCreateTemplateInfoByID(templateID);
assertJsonStringEquals("{\"process\":\"\",\"float_count\":1,\"widget_count\":0," +
"\"cell_count\":13,\"block_count\":3,\"report_type\":0," +
"\"templateID\":\"16a988ce-8529-42f5-b17c-2ee849355071\"}",templateInfo.getProcessMapJsonString());
assertJsonStringEquals("{\"activitykey\":\"2e0ea413-fa9c241e0-9723-4354fce51e81\"," +
"\"jar_time\":\"不是安装版本\",\"create_time\":\"2019-03-26 16:13\"," +
"\"templateID\":\"16a988ce-8529-42f5-b17c-2ee849355071\",\"originID\":\"\"," +
"\"uuid\":\"476ca2cc-f789-4c5d-8e89-ef146580775c\",\"time_consume\":329,\"originTime\":0," +
"\"version\":\"10.0\",\"username\":\"plough\"}", templateInfo.getConsumingMapJsonString());
}
@Test
public void testCollectInfoForNewTemplate() throws Exception {
setUpMockForNewInstance();
TemplateInfoCollector collector = TemplateInfoCollector.getInstance();
String templateID = "73a97777-8jnk-47cd-b57c-2ee89991279796";
int timeConsume = 200;
collector.collectInfo(templateID, StringUtils.EMPTY, mockProcessInfo, timeConsume);
// 检查是否写入成功
collector.loadFromFile();
assertTrue(collector.contains(templateID));
TemplateInfo templateInfo = collector.getOrCreateTemplateInfoByID(templateID);
assertEquals(templateID, templateInfo.getTemplateID());
assertJsonStringEquals("{\"process\":\"\",\"float_count\":1,\"widget_count\":0," +
"\"cell_count\":13,\"block_count\":3,\"report_type\":0," +
"\"templateID\":\"73a97777-8jnk-47cd-b57c-2ee89991279796\"}",templateInfo.getProcessMapJsonString());
Map<String, Object> consumingMap = Reflect.on(templateInfo).field("consumingMap").get();
assertEquals(templateID, consumingMap.get("templateID"));
assertEquals(StringUtils.EMPTY, consumingMap.get("originID"));
assertEquals(200, consumingMap.get("time_consume"));
assertEquals(0, consumingMap.get("originTime"));
}
@Test
public void testCollectInfoWhenSaveAs() throws Exception {
setUpMockForNewInstance();
TemplateInfoCollector collector = TemplateInfoCollector.getInstance();
String templateID = "423238d4-5223-22vj-vlsj-42jc49245iw3";
String originID = "16a988ce-8529-42f5-b17c-2ee849355071";
int timeConsume = 200;
collector.collectInfo(templateID, originID, mockProcessInfo, timeConsume);
// 检查是否写入成功
collector.loadFromFile();
TemplateInfo templateInfo = collector.getOrCreateTemplateInfoByID(templateID);
assertJsonStringEquals("{\"process\":\"\",\"float_count\":1,\"widget_count\":0," +
"\"cell_count\":13,\"block_count\":3,\"report_type\":0," +
"\"templateID\":\"423238d4-5223-22vj-vlsj-42jc49245iw3\"}", templateInfo.getProcessMapJsonString());
Map<String, Object> consumingMap = Reflect.on(templateInfo).field("consumingMap").get();
assertEquals(templateID, consumingMap.get("templateID"));
assertEquals(originID, consumingMap.get("originID"));
assertEquals(329, consumingMap.get("time_consume"));
assertEquals(129, consumingMap.get("originTime"));
}
@Test
public void testCollectInfoWhenSaveAsWithNoTrackOriginID() throws Exception {
setUpMockForNewInstance();
TemplateInfoCollector collector = TemplateInfoCollector.getInstance();
String templateID = "423238d4-5223-22vj-vlsj-42jc49245iw3";
String originID = "3kha8jcs-31xw-42f5-h2ww-2ee84935312z";
int timeConsume = 200;
collector.collectInfo(templateID, originID, mockProcessInfo, timeConsume);
TemplateInfo templateInfo = collector.getOrCreateTemplateInfoByID(templateID);
assertEquals(templateID, templateInfo.getTemplateID());
assertEquals(originID, templateInfo.getOriginID());
Map<String, Object> consumingMap = Reflect.on(templateInfo).field("consumingMap").get();
assertEquals(templateID, consumingMap.get("templateID"));
assertEquals(originID, consumingMap.get("originID"));
assertEquals(200, consumingMap.get("time_consume"));
assertEquals(0, consumingMap.get("originTime"));
}
@Test
public void testAddIdleDateCount() {
String templateID = "16a988ce-8529-42f5-b17c-2ee849355071";
TemplateInfoCollector collecter = TemplateInfoCollector.getInstance();
TemplateInfo templateInfo = collecter.getOrCreateTemplateInfoByID(templateID);
assertEquals(9, templateInfo.getIdleDayCount());
Reflect.on(collecter).call("addIdleDayCount");
assertEquals(10, templateInfo.getIdleDayCount());
// 同一天内多次调用无效
Reflect.on(collecter).call("addIdleDayCount");
assertEquals(10, templateInfo.getIdleDayCount());
}
}

125
designer-base/src/test/java/com/fr/design/mainframe/template/info/TemplateInfoTest.java

@ -0,0 +1,125 @@
package com.fr.design.mainframe.template.info;
import com.fr.config.MarketConfig;
import com.fr.general.GeneralUtils;
import com.fr.invoke.Reflect;
import com.fr.stable.ProductConstants;
import com.fr.stable.StringUtils;
import com.fr.stable.xml.XMLableReader;
import com.fr.third.javax.xml.stream.XMLStreamException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.io.StringReader;
import java.util.Map;
import static com.fr.design.mainframe.template.info.TemplateInfoTestHelper.assertJsonStringEquals;
import static com.fr.design.mainframe.template.info.TemplateInfoTestHelper.setUpMockForNewInstance;
import static org.junit.Assert.assertEquals;
/**
* Created by plough on 2019/4/19.
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest({MarketConfig.class, ProductConstants.class, GeneralUtils.class})
public class TemplateInfoTest {
private static final String NORMAL_INFO = "<TemplateInfo templateID=\"16a988ce-8529-42f5-b17c-2ee849355071\" day_count=\"9\">\n" +
"<processMap process=\"\" float_count=\"0\" widget_count=\"0\" cell_count=\"1\" block_count=\"0\" report_type=\"0\"/>\n" +
"<consumingMap activitykey=\"2e0ea413-fa9c241e0-9723-4354fce51e81\" jar_time=\"不是安装版本\" create_time=\"2019-03-26 16:13\" uuid=\"476ca2cc-f789-4c5d-8e89-ef146580775c\" time_consume=\"129\" version=\"10.0\" username=\"plough\"/>\n" +
"</TemplateInfo>";
private static final String SAVE_AS_INFO = "<TemplateInfo templateID=\"49avd2c4-1104-92j2-wx24-3dd0k2136080\" originID=\"16a988ce-8529-42f5-b17c-2ee849355071\" day_count=\"9\">\n" +
"<processMap process=\"\" float_count=\"0\" widget_count=\"0\" cell_count=\"1\" block_count=\"0\" report_type=\"0\"/>\n" +
"<consumingMap activitykey=\"2e0ea413-fa9c241e0-9723-4354fce51e81\" jar_time=\"不是安装版本\" create_time=\"2019-03-26 16:13\" uuid=\"476ca2cc-f789-4c5d-8e89-ef146580775c\" time_consume=\"429\" originTime=\"129\" version=\"10.0\" username=\"plough\"/>\n" +
"</TemplateInfo>";
private TemplateInfo templateInfo;
private TemplateInfo templateInfoSaveAs; // 另存为的模版记录
@Before
public void setUp() throws XMLStreamException {
templateInfo = createTemplateInfo(NORMAL_INFO);
templateInfoSaveAs = createTemplateInfo(SAVE_AS_INFO);
}
@Test
public void testNewInstance() throws Exception {
setUpMockForNewInstance();
String templateID = "24avc8n2-1iq8-iuj2-wx24-8yy0i8132302";
TemplateInfo templateInfo = TemplateInfo.newInstance(templateID);
assertEquals(templateID, templateInfo.getTemplateID());
assertEquals(StringUtils.EMPTY, Reflect.on(templateInfo).field("originID").get());
assertEquals(0, Reflect.on(templateInfo).field("idleDayCount").get());
assertEquals("{}", templateInfo.getProcessMapJsonString());
Map<String, Object> consumingMap = Reflect.on(templateInfo).field("consumingMap").get();
assertEquals(templateID, consumingMap.get("templateID"));
assertEquals(0, consumingMap.get("originTime"));
assertEquals(StringUtils.EMPTY, consumingMap.get("originID"));
assertEquals(0, consumingMap.get("time_consume"));
assertEquals("不是安装版本", consumingMap.get("jar_time"));
assertEquals("plough", consumingMap.get("username"));
assertEquals("10.0", consumingMap.get("version"));
}
@Test
public void testNewInstanceWithMoreArgs() throws Exception {
setUpMockForNewInstance();
String templateID = "24121212-u2c8-ncd2-82nx-8ud0i8138888";
String originID = "24avc8n2-1iq8-iuj2-wx24-8yy0i8132302";
int originTime = 100;
TemplateInfo templateInfo = TemplateInfo.newInstance(templateID, originID, originTime);
assertEquals(templateID, templateInfo.getTemplateID());
assertEquals(originID, Reflect.on(templateInfo).field("originID").get());
assertEquals(0, Reflect.on(templateInfo).field("idleDayCount").get());
assertEquals("{}", templateInfo.getProcessMapJsonString());
Map<String, Object> consumingMap = Reflect.on(templateInfo).field("consumingMap").get();
assertEquals(templateID, consumingMap.get("templateID"));
assertEquals(originTime, consumingMap.get("originTime"));
assertEquals(originID, consumingMap.get("originID"));
assertEquals(originTime, consumingMap.get("time_consume"));
assertEquals("不是安装版本", consumingMap.get("jar_time"));
assertEquals("plough", consumingMap.get("username"));
assertEquals("10.0", consumingMap.get("version"));
}
@Test
public void testGetTemplateID() {
assertEquals("16a988ce-8529-42f5-b17c-2ee849355071", templateInfo.getTemplateID());
assertEquals("49avd2c4-1104-92j2-wx24-3dd0k2136080", templateInfoSaveAs.getTemplateID());
}
@Test
public void testGetConsumingMapJsonString() {
assertJsonStringEquals("{\"activitykey\":\"2e0ea413-fa9c241e0-9723-4354fce51e81\",\"jar_time\":\"不是安装版本\"," +
"\"create_time\":\"2019-03-26 16:13\",\"templateID\":\"16a988ce-8529-42f5-b17c-2ee849355071\",\"originID\":\"\"," +
"\"uuid\":\"476ca2cc-f789-4c5d-8e89-ef146580775c\",\"time_consume\":129,\"originTime\":0,\"version\":\"10.0\"," +
"\"username\":\"plough\"}", templateInfo.getConsumingMapJsonString());
assertJsonStringEquals("{\"activitykey\":\"2e0ea413-fa9c241e0-9723-4354fce51e81\",\"jar_time\":\"不是安装版本\"," +
"\"create_time\":\"2019-03-26 16:13\",\"templateID\":\"49avd2c4-1104-92j2-wx24-3dd0k2136080\",\"originID\":\"16a988ce-8529-42f5-b17c-2ee849355071\"," +
"\"uuid\":\"476ca2cc-f789-4c5d-8e89-ef146580775c\",\"time_consume\":429,\"originTime\":129,\"version\":\"10.0\"," +
"\"username\":\"plough\"}", templateInfoSaveAs.getConsumingMapJsonString());
}
@Test
public void testGetProcessMapJsonString() {
assertJsonStringEquals("{\"process\":\"\",\"float_count\":0,\"widget_count\":0,\"cell_count\":1," +
"\"block_count\":0,\"report_type\":0,\"templateID\":\"16a988ce-8529-42f5-b17c-2ee849355071\"}", templateInfo.getProcessMapJsonString());
assertJsonStringEquals("{\"process\":\"\",\"float_count\":0,\"widget_count\":0,\"cell_count\":1," +
"\"block_count\":0,\"report_type\":0,\"templateID\":\"49avd2c4-1104-92j2-wx24-3dd0k2136080\"}", templateInfoSaveAs.getProcessMapJsonString());
}
private TemplateInfo createTemplateInfo(String xmlContent) throws XMLStreamException {
StringReader sr = new StringReader(xmlContent);
XMLableReader xmlReader = XMLableReader.createXMLableReader(sr);
return TemplateInfo.newInstanceByRead(xmlReader);
}
}

49
designer-base/src/test/java/com/fr/design/mainframe/template/info/TemplateInfoTestHelper.java

@ -0,0 +1,49 @@
package com.fr.design.mainframe.template.info;
import com.fr.config.MarketConfig;
import com.fr.general.ComparatorUtils;
import com.fr.general.GeneralUtils;
import com.fr.json.JSONObject;
import com.fr.stable.ProductConstants;
import org.easymock.EasyMock;
import org.powermock.api.easymock.PowerMock;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import static org.junit.Assert.assertTrue;
/**
* Created by plough on 2019/4/19.
*/
class TemplateInfoTestHelper {
static void assertJsonStringEquals(String jo1, String jo2) {
// HashMap 是无序的,所以不能直接比较它生成的 json 字符串
assertTrue(ComparatorUtils.equals(new JSONObject(jo1), new JSONObject(jo2)));
}
private static void setFinalStatic(Field field, Object newValue) throws Exception {
field.setAccessible(true);
// remove final modifier from field
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);
}
static void setUpMockForNewInstance() throws Exception {
MarketConfig mockMarketConfig = EasyMock.mock(MarketConfig.class);
EasyMock.expect(mockMarketConfig.getBbsUsername()).andReturn("plough").anyTimes();
PowerMock.mockStatic(MarketConfig.class);
EasyMock.expect(MarketConfig.getInstance()).andReturn(mockMarketConfig).anyTimes();
PowerMock.mockStatic(GeneralUtils.class);
EasyMock.expect(GeneralUtils.readBuildNO()).andReturn("不是安装版本").anyTimes();
setFinalStatic(ProductConstants.class.getDeclaredField("VERSION"), "10.0");
EasyMock.replay(mockMarketConfig);
PowerMock.replayAll();
}
}

62
designer-base/src/test/java/com/fr/design/mainframe/template/info/TimeConsumeTimerTest.java

@ -0,0 +1,62 @@
package com.fr.design.mainframe.template.info;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Created by plough on 2019/4/19.
*/
public class TimeConsumeTimerTest {
@Test
public void testNotEnabled() throws InterruptedException {
TimeConsumeTimer consumeTimer = new TimeConsumeTimer();
consumeTimer.start();
Thread.sleep(1100);
consumeTimer.stop();
assertEquals(0, consumeTimer.popTime());
}
@Test
public void testEnabled() throws InterruptedException {
TimeConsumeTimer consumeTimer = new TimeConsumeTimer();
consumeTimer.setEnabled(true);
consumeTimer.start();
Thread.sleep(1100);
consumeTimer.stop();
assertEquals(1, consumeTimer.popTime());
assertEquals(0, consumeTimer.popTime());
}
@Test
public void testMultiTimes() throws InterruptedException {
TimeConsumeTimer consumeTimer = new TimeConsumeTimer();
consumeTimer.setEnabled(true);
consumeTimer.start();
Thread.sleep(1010);
consumeTimer.stop();
Thread.sleep(2000);
consumeTimer.start();
Thread.sleep(1010);
assertEquals(2, consumeTimer.popTime());
}
@Test
public void testStartMultiTime() throws InterruptedException {
TimeConsumeTimer consumeTimer = new TimeConsumeTimer();
consumeTimer.setEnabled(true);
consumeTimer.start();
Thread.sleep(1010);
consumeTimer.start();
Thread.sleep(1010);
consumeTimer.start();
Thread.sleep(1010);
assertEquals(3, consumeTimer.popTime());
}
}

72
designer-base/src/test/java/com/fr/design/update/push/DesignerPushUpdateConfigManagerTest.java

@ -0,0 +1,72 @@
package com.fr.design.update.push;
import com.fr.stable.StringUtils;
import com.fr.stable.xml.XMLPrintWriter;
import com.fr.stable.xml.XMLableReader;
import com.fr.third.javax.xml.stream.XMLStreamException;
import org.junit.Test;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
/**
* Created by plough on 2019/4/8.
*/
public class DesignerPushUpdateConfigManagerTest {
@Test
public void testSingleton() {
DesignerPushUpdateConfigManager m1 = DesignerPushUpdateConfigManager.getInstance();
DesignerPushUpdateConfigManager m2 = DesignerPushUpdateConfigManager.getInstance();
assertSame(m1, m2);
}
@Test
public void testDefaultValue() throws XMLStreamException {
DesignerPushUpdateConfigManager configManager = DesignerPushUpdateConfigManager.getInstance();
XMLableReader xmlReader = XMLableReader.createXMLableReader(new StringReader("<xml></xml>"));
xmlReader.readXMLObject(configManager);
assertEquals(StringUtils.EMPTY, configManager.getLastIgnoredVersion());
assertTrue(configManager.isAutoPushUpdateEnabled());
}
@Test
public void testReadAndWrite() throws XMLStreamException {
final String initLastIngnoredVersion = "1.1.2";
final boolean initAutoPushEnabled = false;
DesignerPushUpdateConfigManager configManager = DesignerPushUpdateConfigManager.getInstance();
configManager.setLastIgnoredVersion(initLastIngnoredVersion);
configManager.setAutoPushUpdateEnabled(initAutoPushEnabled);
// 写入 xml
StringWriter sw = new StringWriter();
XMLPrintWriter writer = XMLPrintWriter.create(new PrintWriter(sw));
configManager.writeXML(writer);
writer.flush();
writer.close();
String xml_str = sw.getBuffer().toString();
// 临时修改配置
configManager.setAutoPushUpdateEnabled(true);
configManager.setLastIgnoredVersion("0.20.1");
// 从 xml 中读取
StringReader sr = new StringReader(xml_str);
XMLableReader xmlReader = XMLableReader.createXMLableReader(sr);
xmlReader.readXMLObject(configManager);
// 验证:与写入时的配置一致
assertEquals(initLastIngnoredVersion, configManager.getLastIgnoredVersion());
assertEquals(initAutoPushEnabled, configManager.isAutoPushUpdateEnabled());
}
}

23
designer-base/src/test/java/com/fr/design/update/push/DesignerPushUpdateDialogTest.java

@ -0,0 +1,23 @@
package com.fr.design.update.push;
import com.fr.design.DesignerEnvManager;
import com.fr.json.JSONObject;
/**
* Created by plough on 2019/4/10.
*/
public class DesignerPushUpdateDialogTest {
public static void main(String[] args) {
DesignerEnvManager.getEnvManager().setOpenDebug(true);
JSONObject jo = JSONObject.create();
jo.put("version", "2019.03.06.04.02.43.6");
jo.put("content", "设计器改进:去除右击弹框,让操作过程更流畅;增加报表块缩放功能,利于从全局角度整体设计报表\n插件重构:插件支持热部署,即装即用,不再需要重启服务器;\nsapbw:可用于bwcube和bwquery;\n私有云认证:可在客户本地部署私有云认证服务器,业务服务器可到此服务器进行认证;\n开放:打通简道云,可以在简道云里创建项目,并将数据同步到客户的私有库\nshould not display");
jo.put("more", "http://baidu.com");
jo.put("background", "http://updateten.finereport.com/fr.png");
DesignerUpdateInfo mockUpdateInfo = new DesignerUpdateInfo("111.22.11", "2211.231.1", "11.23.1", jo);
DesignerPushUpdateDialog.createAndShow(null, mockUpdateInfo);
}
}

76
designer-base/src/test/java/com/fr/design/update/push/DesignerPushUpdateManagerTest.java

@ -0,0 +1,76 @@
package com.fr.design.update.push;
import com.fr.design.event.DesignerOpenedListener;
import com.fr.design.mainframe.DesignerContext;
import com.fr.design.mainframe.DesignerFrame;
import com.fr.invoke.Reflect;
import com.fr.stable.StringUtils;
import org.easymock.EasyMock;
import org.junit.BeforeClass;
import org.junit.Test;
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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
/**
* Created by plough on 2019/4/8.
*/
@RunWith(value = PowerMockRunner.class)
@PrepareForTest(DesignerContext.class)
public class DesignerPushUpdateManagerTest {
@BeforeClass
public static void setUp() {
DesignerFrame mockFrame = EasyMock.mock(DesignerFrame.class);
mockFrame.addDesignerOpenedListener(EasyMock.anyObject(DesignerOpenedListener.class));
EasyMock.replay(mockFrame);
PowerMock.mockStatic(DesignerContext.class);
EasyMock.expect(DesignerContext.getDesignerFrame()).andReturn(mockFrame).anyTimes();
PowerMock.replayAll();
}
@Test
public void testSingleton() {
DesignerPushUpdateManager m1 = DesignerPushUpdateManager.getInstance();
DesignerPushUpdateManager m2 = DesignerPushUpdateManager.getInstance();
assertSame(m1, m2);
}
@Test
public void testIsAutoPushUpdateSupported() {
// 中文环境 + 本地设计 -> true
DesignerPushUpdateManager pushUpdateManager = DesignerPushUpdateManager.getInstance();
assertEquals(true, Reflect.on(pushUpdateManager).call("isAutoPushUpdateSupported", true, true).get());
// 非中文环境 || 远程设计 -> false
assertEquals(false, Reflect.on(pushUpdateManager).call("isAutoPushUpdateSupported", false, true).get());
assertEquals(false, Reflect.on(pushUpdateManager).call("isAutoPushUpdateSupported", true, false).get());
assertEquals(false, Reflect.on(pushUpdateManager).call("isAutoPushUpdateSupported", false, false).get());
}
@Test
public void testSkipCurrentPushVersion() {
DesignerPushUpdateManager pushUpdateManager = DesignerPushUpdateManager.getInstance();
// 1. updateInfo 为 null 的情况
pushUpdateManager.skipCurrentPushVersion();
assertEquals(StringUtils.EMPTY, DesignerPushUpdateConfigManager.getInstance().getLastIgnoredVersion());
// 2. updateInfo 有值的情况
final String PUSH_VERSION = "stable-2019.02.03.12.44.22";
DesignerUpdateInfo mockInfo = EasyMock.mock(DesignerUpdateInfo.class);
EasyMock.expect(mockInfo.getPushVersion()).andReturn(PUSH_VERSION).anyTimes();
Reflect.on(pushUpdateManager).set("updateInfo", mockInfo);
EasyMock.replay(mockInfo);
pushUpdateManager.skipCurrentPushVersion();
assertEquals(PUSH_VERSION, DesignerPushUpdateConfigManager.getInstance().getLastIgnoredVersion());
}
}

97
designer-base/src/test/java/com/fr/design/update/push/DesignerUpdateInfoTest.java

@ -0,0 +1,97 @@
package com.fr.design.update.push;
import com.fr.json.JSONObject;
import com.fr.stable.StringUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.security.InvalidParameterException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* Created by plough on 2019/4/9.
*/
public class DesignerUpdateInfoTest {
private static final String CURRENT_VERSION = "2018.09.03.xx";
private static final String LATEST_VERSION = "2019.04.03.yy";
private static final String LAST_IGNORED_VERSION = "2019.02.03.yy";
private static final String PUSH_VERSION = "2019.01.03.21.11";
private static final String PUSH_CONTENT = "the update desc content";
private static final String PUSH_BACKGROUND = "http://image.fr.com/123.jpg";
private static final String PUSH_MORE = "http://help.finereport.com/xxx";
private DesignerUpdateInfo updateInfo;
@Before
public void setUp() {
JSONObject pushData = JSONObject.create();
pushData.put("version", PUSH_VERSION);
pushData.put("content", PUSH_CONTENT);
pushData.put("background", PUSH_BACKGROUND);
pushData.put("more", PUSH_MORE);
updateInfo = new DesignerUpdateInfo(CURRENT_VERSION, LATEST_VERSION, LAST_IGNORED_VERSION, pushData);
}
@Test
public void testGetters() {
assertEquals(CURRENT_VERSION, updateInfo.getCurrentVersion());
assertEquals(LATEST_VERSION, updateInfo.getLatestVersion());
assertEquals(LAST_IGNORED_VERSION, updateInfo.getLastIgnoredVersion());
assertEquals(PUSH_VERSION, updateInfo.getPushVersion());
assertEquals(PUSH_CONTENT, updateInfo.getPushContent());
assertEquals(PUSH_BACKGROUND, updateInfo.getBackgroundUrl());
assertEquals(PUSH_MORE, updateInfo.getMoreInfoUrl());
}
@Test
public void testHasNewPushVersion() {
// (1)最近被跳过的维护版本号 X0;
// (2)本地版本号 Y;
// (3)最新的推送版本号 X;
// (4)最新的版本号 Z
// 必须满足:Y < X <= Z && X > X0,才返回 true
// 1 true
assertTrue(hasNewVersion("2019.01.03.xx", "2018.05.03.xx", "2019.04.03.yy", "2018.05.03.xx"));
assertTrue(hasNewVersion("2019.01.03.xx", "2018.05.03.xx", "2019.04.03.yy", null));
assertTrue(hasNewVersion("2019.01.03.xx", "2018.05.03.xx", "2019.04.03.yy", StringUtils.EMPTY));
// 2 false
// 2.1 X <= Y && X > X0
assertFalse(hasNewVersion("2019.01.03.xx", "2019.03.03.xx", "2019.04.03.yy", "2018.05.03.xx"));
assertFalse(hasNewVersion("2019.03.03.xx", "2019.03.03.xx", "2019.04.03.yy", "2018.05.03.xx"));
// 2.2 X > Z && X > X0
assertFalse(hasNewVersion("2020.01.03.xx", "2019.03.03.xx", "2019.04.03.yy", "2018.05.03.xx"));
// 2.3 Y < X <= Z && X <= X0
assertFalse(hasNewVersion("2019.01.03.xx", "2018.05.03.xx", "2019.04.03.yy", "2019.02.03.xx"));
assertFalse(hasNewVersion("2019.01.03.xx", "2018.05.03.xx", "2019.04.03.yy", "2019.01.03.xx"));
}
private boolean hasNewVersion(String X, String Y, String Z, String X0) {
JSONObject pushData = JSONObject.create();
pushData.put("version", X);
pushData.put("content", PUSH_CONTENT);
pushData.put("background", PUSH_BACKGROUND);
pushData.put("more", PUSH_MORE);
DesignerUpdateInfo updateInfo = new DesignerUpdateInfo(Y, Z, X0, pushData);
return updateInfo.hasNewPushVersion();
}
@Test
public void testParameterValidation() {
try {
DesignerUpdateInfo updateInfo = new DesignerUpdateInfo(null, null, null, new JSONObject());
Assert.fail("should not reach here!");
} catch (InvalidParameterException e) {
// do nothing
}
}
}

35
designer-base/src/test/resources/com/fr/design/mainframe/template/info/tpl.info

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<TplInfo xmlVersion="20170720" releaseVersion="10.0.0">
<DesignerOpenHistory>
<![CDATA[2019-04-08,2019-04-03,2019-03-29]]></DesignerOpenHistory>
<TemplateInfoList>
<TemplateInfo templateID="16a988ce-8529-42f5-b17c-2ee849355071" day_count="9">
<processMap process="" float_count="0" widget_count="0" cell_count="1" block_count="0" report_type="0"/>
<consumingMap activitykey="2e0ea413-fa9c241e0-9723-4354fce51e81" jar_time="不是安装版本" create_time="2019-03-26 16:13" uuid="476ca2cc-f789-4c5d-8e89-ef146580775c" time_consume="129" version="10.0" username="plough"/>
</TemplateInfo>
<TemplateInfo templateID="23817e4f-64b6-438e-b23b-91c1a4d0b254" day_count="9">
<processMap process="" float_count="0" widget_count="0" cell_count="231" block_count="0" report_type="0"/>
<consumingMap activitykey="2e0ea413-fa9c241e0-9723-4354fce51e81" jar_time="不是安装版本" create_time="2019-03-26 16:52" uuid="476ca2cc-f789-4c5d-8e89-ef146580775c" time_consume="91" version="10.0" username="plough"/>
</TemplateInfo>
<TemplateInfo templateID="31319947-5ce7-4d82-97e4-3cfea825df9c" day_count="9">
<processMap process="" float_count="0" widget_count="0" cell_count="3" block_count="0" report_type="0"/>
<consumingMap activitykey="2e0ea413-fa9c241e0-9723-4354fce51e81" jar_time="不是安装版本" create_time="2019-03-26 16:49" uuid="476ca2cc-f789-4c5d-8e89-ef146580775c" time_consume="160" version="10.0" username="plough"/>
</TemplateInfo>
<TemplateInfo templateID="43bdd914-a3f7-405c-ad96-2feaf28bed74" day_count="8">
<processMap process="" float_count="0" widget_count="0" cell_count="1" block_count="0" report_type="0"/>
<consumingMap activitykey="2e0ea413-fa9c241e0-9723-4354fce51e81" jar_time="2019.01.04.18.06.01.38" create_time="2019-03-27 16:17" uuid="476ca2cc-f789-4c5d-8e89-ef146580775c" time_consume="1426" version="10.0" username="plough"/>
</TemplateInfo>
<TemplateInfo templateID="cd527ae5-06e4-48fb-a73e-e9a06c9e7c58" day_count="12">
<processMap process="" float_count="0" widget_count="0" cell_count="3" block_count="0" report_type="0"/>
<consumingMap activitykey="2e0ea413-fa9c241e0-9723-4354fce51e81" jar_time="2019.01.04.18.06.01.38" create_time="2019-03-15 10:02" uuid="476ca2cc-f789-4c5d-8e89-ef146580775c" time_consume="430" version="10.0" username="plough"/>
</TemplateInfo>
<TemplateInfo templateID="08f9c404-8124-4615-a34c-735dcabee137" day_count="9">
<processMap process="" float_count="0" widget_count="0" cell_count="2" block_count="0" report_type="0"/>
<consumingMap activitykey="2e0ea413-fa9c241e0-9723-4354fce51e81" jar_time="不是安装版本" create_time="2019-03-25 17:22" uuid="476ca2cc-f789-4c5d-8e89-ef146580775c" time_consume="1497" version="10.0" username="plough"/>
</TemplateInfo>
<TemplateInfo templateID="981314a5-9c5b-4831-97d8-15e76735f9e2" day_count="1">
<processMap process="" float_count="0" widget_count="0" cell_count="3" block_count="0" report_type="0"/>
<consumingMap activitykey="2e0ea413-fa9c241e0-9723-4354fce51e81" jar_time="不是安装版本" create_time="2019-04-17 16:24" uuid="476ca2cc-f789-4c5d-8e89-ef146580775c" time_consume="155" version="10.0" username="plough"/>
</TemplateInfo>
</TemplateInfoList>
</TplInfo>

3
designer-chart/src/main/resources/com/fr/aspectj/designerchart/TemplateProcessTracker.aj

@ -4,13 +4,10 @@ package com.fr.aspectj.designerchart;
* Created by plough on 2017/3/3.
*/
import com.fr.chart.chartattr.Chart;
import com.fr.design.mainframe.templateinfo.TemplateInfoCollector;
import org.aspectj.lang.reflect.SourceLocation;
import javax.swing.event.ListSelectionEvent;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.util.Date;
public aspect TemplateProcessTracker {
//声明一个pointcut,匹配你需要的方法

22
designer-form/src/main/java/com/fr/design/designer/creator/XWFitLayout.java

@ -1,15 +1,5 @@
package com.fr.design.designer.creator;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ContainerEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import com.fr.design.designer.beans.LayoutAdapter;
import com.fr.design.designer.beans.adapters.layout.FRFitLayoutAdapter;
import com.fr.design.designer.beans.location.Direction;
@ -24,17 +14,25 @@ import com.fr.design.mainframe.WidgetPropertyPane;
import com.fr.design.utils.gui.LayoutUtils;
import com.fr.form.ui.PaddingMargin;
import com.fr.form.ui.Widget;
import com.fr.form.ui.container.WAbsoluteLayout.BoundsWidget;
import com.fr.form.ui.container.WBodyLayoutType;
import com.fr.form.ui.container.WFitLayout;
import com.fr.form.ui.container.WLayout;
import com.fr.general.FRLogger;
import com.fr.general.FRScreen;
import com.fr.form.ui.container.WAbsoluteLayout.BoundsWidget;
import com.fr.stable.ArrayUtils;
import edu.emory.mathcs.backport.java.util.Arrays;
import javax.swing.JOptionPane;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ContainerEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**

13
designer-form/src/main/java/com/fr/design/designer/properties/mobile/BodyMobilePropertyUI.java

@ -1,8 +1,7 @@
package com.fr.design.designer.properties.mobile;
import com.fr.design.designer.creator.XCreator;
import com.fr.design.designer.creator.XWAbsoluteBodyLayout;
import com.fr.design.designer.creator.XWFitLayout;
import com.fr.design.designer.creator.XLayoutContainer;
import com.fr.design.dialog.BasicPane;
import com.fr.design.fun.impl.AbstractWidgetPropertyUIProvider;
import com.fr.design.gui.itable.AbstractPropertyTable;
@ -16,19 +15,19 @@ public class BodyMobilePropertyUI extends AbstractWidgetPropertyUIProvider {
private XCreator xCreator;
public BodyMobilePropertyUI(XWFitLayout xwFitLayout) {
public BodyMobilePropertyUI(XLayoutContainer xwFitLayout) {
this.xCreator = xwFitLayout;
}
public BodyMobilePropertyUI(XWAbsoluteBodyLayout xwAbsoluteBodyLayout) {
this.xCreator = xwAbsoluteBodyLayout;
}
@Override
public AbstractPropertyTable createWidgetAttrTable() {
return null;
}
public XCreator getxCreator() {
return xCreator;
}
@Override
public BasicPane createWidgetAttrPane() {
return new BodyMobileDefinePane(xCreator);

28
designer-form/src/main/java/com/fr/design/form/util/FormDesignerUtils.java

@ -0,0 +1,28 @@
package com.fr.design.form.util;
import com.fr.design.mainframe.FormDesigner;
import com.fr.form.ui.container.WFitLayout;
public class FormDesignerUtils {
/**
* body布局是否设置了手机重布局
*
* @param designer
* @return
*/
public static boolean isAppRelayout(FormDesigner designer) {
return ((WFitLayout) designer.getRootComponent().toData()).isAppRelayout();
}
/**
* body布局是否设置了绝对布局
*
* @param designer
* @return
*/
public static boolean isBodyAbsolute(FormDesigner designer) {
WFitLayout root = ((WFitLayout) designer.getRootComponent().toData());
return root.getBodyLayoutType() == com.fr.form.ui.container.WBodyLayoutType.ABSOLUTE;
}
}

4
designer-form/src/main/java/com/fr/design/mainframe/JForm.java

@ -37,8 +37,8 @@ import com.fr.design.gui.xpane.FormHyperlinkGroupPaneNoPop;
import com.fr.design.layout.FRGUIPaneFactory;
import com.fr.design.mainframe.form.FormECCompositeProvider;
import com.fr.design.mainframe.form.FormECDesignerProvider;
import com.fr.design.mainframe.templateinfo.JFormProcessInfo;
import com.fr.design.mainframe.templateinfo.TemplateProcessInfo;
import com.fr.design.mainframe.template.info.JFormProcessInfo;
import com.fr.design.mainframe.template.info.TemplateProcessInfo;
import com.fr.design.mainframe.toolbar.ToolBarMenuDock;
import com.fr.design.mainframe.toolbar.ToolBarMenuDockPlus;
import com.fr.design.menu.KeySetUtils;

2
designer-form/src/main/java/com/fr/design/mainframe/templateinfo/JFormProcessInfo.java → designer-form/src/main/java/com/fr/design/mainframe/template/info/JFormProcessInfo.java

@ -1,4 +1,4 @@
package com.fr.design.mainframe.templateinfo;
package com.fr.design.mainframe.template.info;
import com.fr.form.main.Form;
import com.fr.form.ui.container.WLayout;

18
designer-form/src/main/java/com/fr/design/widget/ui/designer/XmlRelationedBasicPane.java

@ -0,0 +1,18 @@
package com.fr.design.widget.ui.designer;
import com.fr.design.dialog.BasicPane;
/**
* 有些控件在不同终端需要对相同的属性分别进行设置,基础设置面板是一样的但是映射到控件上的属性又是不一样的,为了重用面板,这边加上xmltag做区分
*/
public abstract class XmlRelationedBasicPane extends BasicPane{
private String xmlTag;
public XmlRelationedBasicPane(String xmlTag) {
this.xmlTag = xmlTag;
}
public String getXmlTag() {
return xmlTag;
}
}

44
designer-form/src/main/java/com/fr/design/widget/ui/designer/component/PaddingBoundPane.java

@ -21,22 +21,26 @@ import java.awt.Component;
/**
* Created by ibm on 2017/8/3.
*/
public class PaddingBoundPane extends BasicPane{
public class PaddingBoundPane extends BasicPane {
protected UISpinner top;
protected UISpinner bottom;
protected UISpinner left;
protected UISpinner right;
public PaddingBoundPane() {
initBoundPane();
initBoundPane(0, 0, 0, 0);
}
public void initBoundPane() {
public PaddingBoundPane(int top, int bottom, int left, int right) {
initBoundPane(top, bottom, left, right);
}
public void initBoundPane(int t, int b, int l, int r) {
this.setLayout(FRGUIPaneFactory.createBorderLayout());
top = new UISpinner(0, Integer.MAX_VALUE, 1, 0);
bottom = new UISpinner(0, Integer.MAX_VALUE, 1, 0);
left = new UISpinner(0, Integer.MAX_VALUE, 1, 0);
right = new UISpinner(0, Integer.MAX_VALUE, 1, 0);
top = new UISpinner(0, Integer.MAX_VALUE, 1, t);
bottom = new UISpinner(0, Integer.MAX_VALUE, 1, b);
left = new UISpinner(0, Integer.MAX_VALUE, 1, l);
right = new UISpinner(0, Integer.MAX_VALUE, 1, r);
top.setGlobalName(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Layout_Padding_Duplicate"));
bottom.setGlobalName(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Layout_Padding_Duplicate"));
left.setGlobalName(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Layout_Padding_Duplicate"));
@ -45,12 +49,12 @@ public class PaddingBoundPane extends BasicPane{
label.setBorder(BorderFactory.createEmptyBorder(IntervalConstants.INTERVAL_L1, 0, 0, 0));
label.setVerticalAlignment(SwingConstants.TOP);
JPanel panel = TableLayoutHelper.createGapTableLayoutPane(new Component[][]{
new Component[]{label, createRightPane()}}, TableLayoutHelper.FILL_LASTCOLUMN, IntervalConstants.INTERVAL_W2, IntervalConstants.INTERVAL_L1 );
new Component[]{label, createRightPane()}}, TableLayoutHelper.FILL_LASTCOLUMN, IntervalConstants.INTERVAL_W2, IntervalConstants.INTERVAL_L1);
this.add(panel);
}
public JPanel createRightPane(){
public JPanel createRightPane() {
double f = TableLayout.FILL;
double p = TableLayout.PREFERRED;
double[] rowSize = {p, p};
@ -67,7 +71,7 @@ public class PaddingBoundPane extends BasicPane{
JPanel northPanel = TableLayoutHelper.createGapTableLayoutPane(components1, rowSize, columnSize, rowCount, IntervalConstants.INTERVAL_L6, IntervalConstants.INTERVAL_L6);
northPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, IntervalConstants.INTERVAL_L1, 0));
JPanel centerPanel = TableLayoutHelper.createGapTableLayoutPane(components2, rowSize, columnSize, rowCount, IntervalConstants.INTERVAL_L6, IntervalConstants.INTERVAL_L6);
JPanel panel = FRGUIPaneFactory.createBorderLayout_S_Pane();
JPanel panel = FRGUIPaneFactory.createBorderLayout_S_Pane();
panel.setBorder(BorderFactory.createEmptyBorder(IntervalConstants.INTERVAL_L1, 0, IntervalConstants.INTERVAL_L1, 0));
panel.add(northPanel, BorderLayout.NORTH);
panel.add(centerPanel, BorderLayout.CENTER);
@ -75,20 +79,26 @@ public class PaddingBoundPane extends BasicPane{
}
public void update(RichStyleWidgetProvider marginWidget) {
marginWidget.setMargin(new PaddingMargin((int)top.getValue(), (int)left.getValue(), (int)bottom.getValue(), (int)right.getValue() ));
marginWidget.setMargin(updateBean());
}
public PaddingMargin updateBean() {
return new PaddingMargin((int) top.getValue(), (int) left.getValue(), (int) bottom.getValue(), (int) right.getValue());
}
@Override
protected String title4PopupWindow() {
return "PaddingBoundPane";
}
public void populate(RichStyleWidgetProvider marginWidget) {
PaddingMargin paddingMargin = marginWidget.getMargin();
top.setValue(paddingMargin.getTop());
bottom.setValue(paddingMargin.getBottom());
left.setValue(paddingMargin.getLeft());
right.setValue(paddingMargin.getRight());
populateBean(marginWidget.getMargin());
}
public void populateBean(PaddingMargin paddingMargin) {
top.setValueWithoutEvent(paddingMargin.getTop());
bottom.setValueWithoutEvent(paddingMargin.getBottom());
left.setValueWithoutEvent(paddingMargin.getLeft());
right.setValueWithoutEvent(paddingMargin.getRight());
}
}

82
designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/BodyMobileDefinePane.java

@ -1,21 +1,25 @@
package com.fr.design.widget.ui.designer.mobile;
import com.fr.base.iofile.attr.FormBodyPaddingAttrMark;
import com.fr.design.designer.beans.events.DesignerEvent;
import com.fr.design.designer.creator.XCreator;
import com.fr.design.foldablepane.UIExpandablePane;
import com.fr.design.form.util.FormDesignerUtils;
import com.fr.design.gui.frpane.AttributeChangeListener;
import com.fr.design.gui.icheckbox.UICheckBox;
import com.fr.design.layout.FRGUIPaneFactory;
import com.fr.design.mainframe.FormDesigner;
import com.fr.design.mainframe.MobileWidgetListPane;
import com.fr.design.mainframe.WidgetPropertyPane;
import com.fr.design.widget.ui.designer.mobile.component.MobileComponentAdvancePane;
import com.fr.design.widget.ui.designer.mobile.component.MobileComponentLayoutIntervalPane;
import com.fr.form.ui.RichStyleWidgetProvider;
import com.fr.form.ui.container.WFitLayout;
import com.fr.form.ui.container.WSortLayout;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import java.lang.reflect.Method;
/**
* Created by plough on 2018/2/1.
@ -26,22 +30,40 @@ public class BodyMobileDefinePane extends MobileWidgetDefinePane {
private AttributeChangeListener changeListener;
private UICheckBox appRelayoutCheck;
private MobileWidgetListPane mobileWidgetListPane;
private MobileComponentAdvancePane advancePane;
private MobileComponentLayoutIntervalPane intervalPane;
public BodyMobileDefinePane(XCreator xCreator) {
this.bodyCreator = xCreator;
}
public XCreator getBodyCreator() {
return bodyCreator;
}
public void setBodyCreator(XCreator bodyCreator) {
this.bodyCreator = bodyCreator;
}
public FormDesigner getDesigner() {
return designer;
}
public void setDesigner(FormDesigner designer) {
this.designer = designer;
}
@Override
public void initPropertyGroups(Object source) {
this.setLayout(FRGUIPaneFactory.createBorderLayout());
this.designer = WidgetPropertyPane.getInstance().getEditingFormDesigner();
this.add(getMobilePropertyPane(), BorderLayout.NORTH);
this.add(createNorthPane(), BorderLayout.NORTH);
this.add(getMobileWidgetListPane(), BorderLayout.CENTER);
this.repaint();
}
// 手机属性
private UIExpandablePane getMobilePropertyPane() {
public UIExpandablePane getMobilePropertyPane() {
JPanel panel = FRGUIPaneFactory.createBorderLayout_S_Pane();
appRelayoutCheck = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_App_ReLayout"), true);
appRelayoutCheck.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
@ -55,7 +77,7 @@ public class BodyMobileDefinePane extends MobileWidgetDefinePane {
}
// 控件顺序
private UIExpandablePane getMobileWidgetListPane() {
public UIExpandablePane getMobileWidgetListPane() {
mobileWidgetListPane = new MobileWidgetListPane(designer, (WSortLayout) bodyCreator.toData());
mobileWidgetListPane.setBorder(BorderFactory.createEmptyBorder(10, 0, 5, 0));
JPanel panelWrapper = FRGUIPaneFactory.createBorderLayout_S_Pane();
@ -64,6 +86,24 @@ public class BodyMobileDefinePane extends MobileWidgetDefinePane {
return new UIExpandablePane(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Widget_Order"), 280, 20, panelWrapper);
}
private JPanel createNorthPane() {
JPanel holder = FRGUIPaneFactory.createBorderLayout_S_Pane();
holder.add(getMobilePropertyPane(), BorderLayout.NORTH);
advancePane = new MobileComponentAdvancePane(FormBodyPaddingAttrMark.XML_TAG);
intervalPane = new MobileComponentLayoutIntervalPane(FormBodyPaddingAttrMark.XML_TAG);
//高级
holder.add(advancePane, BorderLayout.CENTER);
//布局
holder.add(intervalPane, BorderLayout.SOUTH);
advancePane.setVisible(!shouldHidePadding(designer));
intervalPane.setVisible(!shouldHidePadding(designer));
return holder;
}
private void bindListeners2Widgets() {
reInitAllListeners();
this.changeListener = new AttributeChangeListener() {
@ -81,25 +121,13 @@ public class BodyMobileDefinePane extends MobileWidgetDefinePane {
initListener(this);
}
// body是否开启手机重布局
private boolean isAppRelayout() {
boolean result = false;
try {
Method m = bodyCreator.toData().getClass().getMethod("isAppRelayout");
result = (boolean)m.invoke(bodyCreator.toData());
} catch (Exception e) {
// do nothing
}
return result;
}
private void setAppRelayout(boolean appRelayoutSeleted) {
if (appRelayoutSeleted == isAppRelayout()) {
if (appRelayoutSeleted == FormDesignerUtils.isAppRelayout(designer)) {
return;
}
try {
Method m = bodyCreator.toData().getClass().getMethod("setAppRelayout", boolean.class);
m.invoke(bodyCreator.toData(), appRelayoutSeleted);
((WFitLayout) designer.getRootComponent().toData()).setAppRelayout(appRelayoutSeleted);
} catch (Exception e) {
// do nothing
}
@ -108,17 +136,31 @@ public class BodyMobileDefinePane extends MobileWidgetDefinePane {
@Override
public void populate(FormDesigner designer) {
this.designer = designer;
appRelayoutCheck.setSelected(isAppRelayout());
appRelayoutCheck.setSelected(FormDesignerUtils.isAppRelayout(designer));
// 数据 populate 完成后,再设置监听
this.bindListeners2Widgets();
this.addAttributeChangeListener(changeListener);
advancePane.populate((RichStyleWidgetProvider) getBodyCreator().toData());
intervalPane.populate((RichStyleWidgetProvider) getBodyCreator().toData());
}
@Override
public void update() {
boolean appRelayout = appRelayoutCheck.isSelected();
setAppRelayout(appRelayoutCheck.isSelected());
boolean appPaddingVisible = appRelayout || !FormDesignerUtils.isBodyAbsolute(designer);
advancePane.setVisible(appPaddingVisible);
intervalPane.setVisible(appPaddingVisible);
mobileWidgetListPane.updateToDesigner();
designer.getEditListenerTable().fireCreatorModified(DesignerEvent.CREATOR_EDITED);
if (advancePane.isVisible()) {
advancePane.update((RichStyleWidgetProvider) getBodyCreator().toData());
}
if (intervalPane.isVisible()) {
intervalPane.update((RichStyleWidgetProvider) getBodyCreator().toData());
}
}
}

21
designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/ChartEditorDefinePane.java

@ -9,6 +9,7 @@ import com.fr.design.designer.creator.XWAbsoluteBodyLayout;
import com.fr.design.designer.creator.XWAbsoluteLayout;
import com.fr.design.designer.properties.items.Item;
import com.fr.design.foldablepane.UIExpandablePane;
import com.fr.design.form.util.FormDesignerUtils;
import com.fr.design.gui.frpane.AttributeChangeListener;
import com.fr.design.gui.icheckbox.UICheckBox;
import com.fr.design.gui.icombobox.UIComboBox;
@ -20,11 +21,14 @@ import com.fr.design.mainframe.DesignerContext;
import com.fr.design.mainframe.FormDesigner;
import com.fr.design.mainframe.WidgetPropertyPane;
import com.fr.form.ui.BaseChartEditor;
import com.fr.form.ui.container.WFitLayout;
import javax.swing.*;
import java.awt.*;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
@ -56,7 +60,7 @@ public class ChartEditorDefinePane extends MobileWidgetDefinePane {
JPanel mobileSettingsPane;
if (isInAbsoluteLayout()) {
mobileSettingsPane = getUnavailableTipPane(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Tip_Chart_Adaptivity_Unavailable_In_Absolute_Layout"));
} else if (!isAppRelayout()) {
} else if (!FormDesignerUtils.isAppRelayout(designer)) {
mobileSettingsPane = getUnavailableTipPane(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Tip_Chart_Adaptivity_Unavailable"));
} else {
mobileSettingsPane = getMobileSettingsPane();
@ -76,11 +80,6 @@ public class ChartEditorDefinePane extends MobileWidgetDefinePane {
return false;
}
// body是否开启手机重布局
private boolean isAppRelayout() {
return ((WFitLayout)designer.getRootComponent().toData()).isAppRelayout();
}
private JPanel getUnavailableTipPane(String tipText) {
JPanel panel = new JPanel(new BorderLayout());
UILabel unavailableTipLabel = new UILabel();
@ -158,7 +157,7 @@ public class ChartEditorDefinePane extends MobileWidgetDefinePane {
this.bindListeners2Widgets();
this.addAttributeChangeListener(changeListener);
if (!isAppRelayout() || isInAbsoluteLayout()) {
if (!FormDesignerUtils.isAppRelayout(designer) || isInAbsoluteLayout()) {
return;
}

17
designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/MobileWidgetDefinePane.java

@ -1,5 +1,6 @@
package com.fr.design.widget.ui.designer.mobile;
import com.fr.design.form.util.FormDesignerUtils;
import com.fr.design.gui.frpane.AbstractAttrNoScrollPane;
import com.fr.design.mainframe.FormDesigner;
@ -7,10 +8,10 @@ import javax.swing.*;
/**
* 所有移动端需要拓展的属性面板均继承此类
*
* <p>
* Created by fanglei on 2017/8/8.
*/
public abstract class MobileWidgetDefinePane extends AbstractAttrNoScrollPane{
public abstract class MobileWidgetDefinePane extends AbstractAttrNoScrollPane {
//初始化panel数据再repaint
public abstract void initPropertyGroups(Object source);
@ -28,11 +29,21 @@ public abstract class MobileWidgetDefinePane extends AbstractAttrNoScrollPane{
// 暂不需要此方法
@Override
protected void initContentPane() {}
protected void initContentPane() {
}
// 暂不需要此方法
@Override
protected JPanel createContentPane() {
return new JPanel();
}
/**
* 绝对布局且不勾选手机重布局 的时候不支持边距设置
*
* @return
*/
public boolean shouldHidePadding(FormDesigner designer) {
return !FormDesignerUtils.isAppRelayout(designer) && FormDesignerUtils.isBodyAbsolute(designer);
}
}

41
designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/TabMobileWidgetDefinePane.java

@ -1,5 +1,6 @@
package com.fr.design.widget.ui.designer.mobile;
import com.fr.base.iofile.attr.FormTabPaddingAttrMark;
import com.fr.design.constants.LayoutConstants;
import com.fr.design.designer.IntervalConstants;
import com.fr.design.designer.creator.XCreator;
@ -9,8 +10,11 @@ import com.fr.design.layout.FRGUIPaneFactory;
import com.fr.design.layout.TableLayoutHelper;
import com.fr.design.mainframe.DesignerContext;
import com.fr.design.mainframe.FormDesigner;
import com.fr.design.mainframe.WidgetPropertyPane;
import com.fr.design.mainframe.widget.accessibles.AccessibleTemplateStyleEditor;
import com.fr.design.mainframe.widget.accessibles.MobileTemplateStylePane;
import com.fr.design.widget.ui.designer.mobile.component.MobileComponentAdvancePane;
import com.fr.design.widget.ui.designer.mobile.component.MobileComponentLayoutIntervalPane;
import com.fr.form.ui.container.cardlayout.WCardTagLayout;
import com.fr.general.cardtag.mobile.MobileTemplateStyle;
@ -20,8 +24,11 @@ import java.awt.Component;
public class TabMobileWidgetDefinePane extends MobileWidgetDefinePane {
private XCreator xCreator;
private FormDesigner designer; // 当前设计器
private AccessibleTemplateStyleEditor templateStyleEditor;
private AttributeChangeListener changeListener;
private MobileComponentAdvancePane advancePane;
private MobileComponentLayoutIntervalPane intervalPane;
public TabMobileWidgetDefinePane(XCreator xCreator) {
this.xCreator = xCreator;
@ -36,6 +43,7 @@ public class TabMobileWidgetDefinePane extends MobileWidgetDefinePane {
}
};
}
/**
* 后台初始化所有事件.
*/
@ -46,24 +54,47 @@ public class TabMobileWidgetDefinePane extends MobileWidgetDefinePane {
@Override
public void initPropertyGroups(Object source) {
this.setLayout(FRGUIPaneFactory.createBorderLayout());
this.designer = WidgetPropertyPane.getInstance().getEditingFormDesigner();
UILabel label = new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Tab_Style_Template"));
templateStyleEditor = new AccessibleTemplateStyleEditor(new MobileTemplateStylePane((WCardTagLayout) xCreator.toData()));
JPanel jPanel = TableLayoutHelper.createGapTableLayoutPane(new Component[][]{new Component[]{label, templateStyleEditor}}, TableLayoutHelper.FILL_LASTCOLUMN, IntervalConstants.INTERVAL_L1, LayoutConstants.VGAP_MEDIUM);
this.add(jPanel, BorderLayout.CENTER);
JPanel jPanel = TableLayoutHelper.createGapTableLayoutPane(new Component[][]{new Component[]{label, templateStyleEditor}, {new UILabel()}}, TableLayoutHelper.FILL_LASTCOLUMN, IntervalConstants.INTERVAL_L1, LayoutConstants.VGAP_LARGE);
JPanel holder = FRGUIPaneFactory.createBorderLayout_S_Pane();
holder.add(jPanel, BorderLayout.NORTH);
if (!shouldHidePadding(designer)) {
advancePane = new MobileComponentAdvancePane(FormTabPaddingAttrMark.XML_TAG);
intervalPane = new MobileComponentLayoutIntervalPane(FormTabPaddingAttrMark.XML_TAG);
//高级
holder.add(advancePane, BorderLayout.CENTER);
//布局
holder.add(intervalPane, BorderLayout.SOUTH);
}
this.add(holder, BorderLayout.NORTH);
}
@Override
public void populate(FormDesigner designer) {
templateStyleEditor.setValue(((WCardTagLayout)xCreator.toData()).getMobileTemplateStyle());
templateStyleEditor.setValue(((WCardTagLayout) xCreator.toData()).getMobileTemplateStyle());
// 数据 populate 完成后,再设置监听
this.bindListeners2Widgets();
this.addAttributeChangeListener(changeListener);
if (advancePane != null) {//业务层面可以写成shouldHidePadding但是这样写应该性能差点
advancePane.populate((WCardTagLayout) xCreator.toData());
}
if (intervalPane != null) {
intervalPane.populate((WCardTagLayout) xCreator.toData());
}
}
@Override
public void update() {
((WCardTagLayout)xCreator.toData()).setMobileTemplateStyle((MobileTemplateStyle) templateStyleEditor.getValue());
((WCardTagLayout) xCreator.toData()).setMobileTemplateStyle((MobileTemplateStyle) templateStyleEditor.getValue());
DesignerContext.getDesignerFrame().getSelectedJTemplate().fireTargetModified(); // 触发设计器保存按钮亮起来
if (advancePane != null) {
advancePane.update((WCardTagLayout) xCreator.toData());
}
if (intervalPane != null) {
intervalPane.update((WCardTagLayout) xCreator.toData());
}
}
}

45
designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/component/MobileComponentAdvancePane.java

@ -0,0 +1,45 @@
package com.fr.design.widget.ui.designer.mobile.component;
import com.fr.base.iofile.attr.AttrMarkFactory;
import com.fr.base.iofile.attr.FormBodyPaddingAttrMark;
import com.fr.design.foldablepane.UIExpandablePane;
import com.fr.design.layout.FRGUIPaneFactory;
import com.fr.design.widget.ui.designer.XmlRelationedBasicPane;
import com.fr.design.widget.ui.designer.component.PaddingBoundPane;
import com.fr.form.ui.RichStyleWidgetProvider;
import java.awt.BorderLayout;
/**
* 只有内边距设置的高级设置
*/
public class MobileComponentAdvancePane extends XmlRelationedBasicPane {
private PaddingBoundPane paddingBound;
public MobileComponentAdvancePane(String xmlTag) {
super(xmlTag);
this.setLayout(FRGUIPaneFactory.createBorderLayout());
paddingBound = new PaddingBoundPane(FormBodyPaddingAttrMark.DEFAULT_SIZE, FormBodyPaddingAttrMark.DEFAULT_SIZE, FormBodyPaddingAttrMark.DEFAULT_SIZE, FormBodyPaddingAttrMark.DEFAULT_SIZE);
UIExpandablePane advanceExpandablePane = new UIExpandablePane(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_Advanced"), 280, 20, paddingBound);
this.add(advanceExpandablePane, BorderLayout.NORTH);
}
@Override
protected String title4PopupWindow() {
return "ComponentAdvancePane";
}
public void update(RichStyleWidgetProvider marginWidget) {
FormBodyPaddingAttrMark attrMark = marginWidget.getWidgetAttrMark(getXmlTag());
attrMark = attrMark == null ? (FormBodyPaddingAttrMark) AttrMarkFactory.createAttrMark(getXmlTag()) : attrMark;
attrMark.setPaddingMargin(paddingBound.updateBean());
marginWidget.addWidgetAttrMark(attrMark);
}
public void populate(RichStyleWidgetProvider marginWidget) {
FormBodyPaddingAttrMark attrMark = marginWidget.getWidgetAttrMark(getXmlTag());
if (attrMark != null) {
paddingBound.populateBean(attrMark.getPaddingMargin());
}
}
}

71
designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/component/MobileComponentLayoutIntervalPane.java

@ -0,0 +1,71 @@
package com.fr.design.widget.ui.designer.mobile.component;
import com.fr.base.iofile.attr.AttrMarkFactory;
import com.fr.base.iofile.attr.FormBodyPaddingAttrMark;
import com.fr.design.designer.IntervalConstants;
import com.fr.design.foldablepane.UIExpandablePane;
import com.fr.design.gui.ilable.UILabel;
import com.fr.design.gui.ispinner.UISpinner;
import com.fr.design.layout.FRGUIPaneFactory;
import com.fr.design.layout.TableLayout;
import com.fr.design.layout.TableLayoutHelper;
import com.fr.design.utils.gui.UIComponentUtils;
import com.fr.design.widget.FRWidgetFactory;
import com.fr.design.widget.ui.designer.XmlRelationedBasicPane;
import com.fr.form.ui.RichStyleWidgetProvider;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import java.awt.Component;
/**
* 只有组件间隔的布局设置
*/
public class MobileComponentLayoutIntervalPane extends XmlRelationedBasicPane {
private UISpinner componentIntervel;
public MobileComponentLayoutIntervalPane(String xmlTag) {
super(xmlTag);
this.setLayout(FRGUIPaneFactory.createBorderLayout());
UILabel intervalLabel = FRWidgetFactory.createLineWrapLabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Component_Interval"));
double f = TableLayout.FILL;
double p = TableLayout.PREFERRED;
double[] rowSize = {p, p};
double[] columnSize = {p, f};
int[][] rowCount = {{1, 1}, {1, 1}};
componentIntervel = new UISpinner(0, Integer.MAX_VALUE, 1, FormBodyPaddingAttrMark.DEFAULT_SIZE);
JPanel componentIntervelPane = UIComponentUtils.wrapWithBorderLayoutPane(componentIntervel);
Component[][] components = new Component[][]{
new Component[]{intervalLabel, componentIntervelPane}
};
JPanel centerPane = TableLayoutHelper.createGapTableLayoutPane(components, rowSize, columnSize, rowCount, IntervalConstants.INTERVAL_W1, IntervalConstants.INTERVAL_L1);
centerPane.setBorder(BorderFactory.createEmptyBorder(IntervalConstants.INTERVAL_L1, IntervalConstants.INTERVAL_L5, 0, 0));
JPanel holder = FRGUIPaneFactory.createBorderLayout_S_Pane();
holder.add(centerPane, BorderLayout.NORTH);
UIExpandablePane layoutExpandablePane = new UIExpandablePane(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Layout"), 280, 20, holder);
this.add(layoutExpandablePane, BorderLayout.NORTH);
}
@Override
protected String title4PopupWindow() {
return "ComponentIntervelPane";
}
public void update(RichStyleWidgetProvider marginWidget) {
FormBodyPaddingAttrMark attrMark = marginWidget.getWidgetAttrMark(getXmlTag());
attrMark = attrMark == null ? (FormBodyPaddingAttrMark) AttrMarkFactory.createAttrMark(getXmlTag()) : attrMark;
attrMark.setInterval((int) componentIntervel.getValue());
marginWidget.addWidgetAttrMark(attrMark);
}
public void populate(RichStyleWidgetProvider marginWidget) {
FormBodyPaddingAttrMark attrMark = marginWidget.getWidgetAttrMark(getXmlTag());
if (attrMark != null) {
componentIntervel.setValueWithoutEvent(attrMark.getInterval());
}
}
}

2
designer-form/src/main/resources/com/fr/aspectj/designerform/TemplateProcessTracker.aj

@ -3,12 +3,10 @@ package com.fr.aspectj.designerform;
/**
* Created by plough on 2017/3/3.
*/
import com.fr.design.mainframe.templateinfo.TemplateInfoCollector;
import org.aspectj.lang.reflect.SourceLocation;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.util.Date;
public aspect TemplateProcessTracker {
//声明一个pointcut,匹配你需要的方法

4
designer-realize/src/main/java/com/fr/design/mainframe/JWorkBook.java

@ -41,8 +41,8 @@ import com.fr.design.gui.ibutton.UIButton;
import com.fr.design.gui.icontainer.UIModeControlContainer;
import com.fr.design.gui.imenu.UIMenuItem;
import com.fr.design.mainframe.cell.QuickEditorRegion;
import com.fr.design.mainframe.templateinfo.JWorkBookProcessInfo;
import com.fr.design.mainframe.templateinfo.TemplateProcessInfo;
import com.fr.design.mainframe.template.info.JWorkBookProcessInfo;
import com.fr.design.mainframe.template.info.TemplateProcessInfo;
import com.fr.design.mainframe.toolbar.ToolBarMenuDockPlus;
import com.fr.design.menu.KeySetUtils;
import com.fr.design.menu.MenuDef;

5
designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/AlphaFineDialog.java

@ -33,15 +33,12 @@ import com.fr.design.mainframe.alphafine.search.manager.impl.RecentSearchManager
import com.fr.design.mainframe.alphafine.search.manager.impl.RecommendSearchManager;
import com.fr.design.mainframe.alphafine.search.manager.impl.SegmentationManager;
import com.fr.design.mainframe.alphafine.search.manager.impl.SimilarSearchManager;
import com.fr.design.mainframe.errorinfo.ErrorInfoUploader;
import com.fr.design.mainframe.templateinfo.TemplateInfoCollector;
import com.fr.form.main.Form;
import com.fr.form.main.FormIO;
import com.fr.general.ComparatorUtils;
import com.fr.general.http.HttpClient;
import com.fr.io.TemplateWorkBookIO;
import com.fr.io.exporter.ImageExporter;
import com.fr.json.JSONException;
import com.fr.json.JSONObject;
import com.fr.log.FineLoggerFactory;
import com.fr.main.impl.WorkBook;
@ -958,8 +955,6 @@ public class AlphaFineDialog extends UIDialog {
RecentSearchManager searchManager = RecentSearchManager.getInstance();
searchManager.addModel(storeText, cellModel);
sendDataToServer(storeText, cellModel);
TemplateInfoCollector.getInstance().sendTemplateInfo();
ErrorInfoUploader.getInstance().sendErrorInfo();
}
}
});

10
designer-realize/src/main/java/com/fr/design/mainframe/errorinfo/ErrorInfo.java

@ -25,6 +25,7 @@ public class ErrorInfo {
private String templateid;
private String logid;
private String log;
private String stackTrace;
public ErrorInfo(String username, String uuid, String activekey) {
this.username = username;
@ -89,6 +90,14 @@ public class ErrorInfo {
this.log = log;
}
public String getStackTrace() {
return stackTrace;
}
public void setStackTrace(String stackTrace) {
this.stackTrace = stackTrace;
}
private String dateToString(){
DateFormat df = FRContext.getDefaultValues().getDateTimeFormat();
return df.format(new Date());
@ -107,6 +116,7 @@ public class ErrorInfo {
jo.put("uploadtime", uploadtime);
jo.put("logid", logid);
jo.put("log", log);
jo.put("stacktrace", stackTrace);
saveFileToCache(jo);
}

14
designer-realize/src/main/java/com/fr/design/mainframe/errorinfo/ErrorInfoLogAppender.java

@ -30,6 +30,7 @@ import java.io.InputStream;
public class ErrorInfoLogAppender extends AppenderSkeleton {
private static final int ERROR_LEN = 8;
private static final int ERROR_STACK_TRACE = 15;
// 缓存下不变的, 没必要频繁取.
private String username;
@ -73,10 +74,23 @@ public class ErrorInfoLogAppender extends AppenderSkeleton {
errorInfo.setTemplateid(templateid);
errorInfo.setLog(msg);
errorInfo.setLogid(logid);
errorInfo.setStackTrace(readStackTrace(event));
errorInfo.saveAsJSON();
}
}
private String readStackTrace(LoggingEvent event) {
String[] s = event.getThrowableStrRep();
StringBuilder sb = new StringBuilder();
if (s != null) {
int len = Math.min(s.length, ERROR_STACK_TRACE);
for (int i = 0; i < len; i++) {
sb.append(s[i]).append("\n");
}
}
return sb.toString();
}
private String readLogID(String log) {
String errorCode = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_Engine_ErrorCode_Prefix");
// 报错信息国际化不规范, 有些是中文分号, 有些是英文

152
designer-realize/src/main/java/com/fr/design/mainframe/messagecollect/entity/FileEntityBuilder.java

@ -0,0 +1,152 @@
package com.fr.design.mainframe.messagecollect.entity;
import com.fr.general.CloudCenter;
import com.fr.general.IOUtils;
import com.fr.general.http.HttpToolbox;
import com.fr.json.JSONArray;
import com.fr.json.JSONException;
import com.fr.json.JSONObject;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.CommonUtils;
import com.fr.stable.EncodeConstants;
import com.fr.stable.StableUtils;
import com.fr.stable.StringUtils;
import com.fr.third.jodd.datetime.JDateTime;
import com.fr.third.org.apache.http.HttpEntity;
import com.fr.third.org.apache.http.HttpResponse;
import com.fr.third.org.apache.http.client.HttpClient;
import com.fr.third.org.apache.http.client.methods.HttpPut;
import com.fr.third.org.apache.http.entity.FileEntity;
import com.fr.third.org.apache.http.impl.client.DefaultHttpClient;
import com.fr.third.org.apache.http.util.EntityUtils;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import static com.fr.third.org.apache.http.HttpStatus.SC_OK;
/**
* @author alex sung
* @date 2019/4/8
*/
public class FileEntityBuilder {
private static final String INTELLI_OPERATION_URL = "intelli.operation.url";
private static final String OPERATION_URL = "https://cloud.fanruan.com/config/protect/operation";
private static final String ATTR_SIGNATURE = "signature";
private static final String ATTR_KEY = "key";
private static final String FOCUS_POINT_FILE_ROOT_PATH = "FocusPoint";
/**
* 文件夹路径
*/
private String folderName;
public FileEntityBuilder(String folderName) {
this.folderName = folderName;
}
public String getFolderName() {
return folderName;
}
public void setFolderName(String folderName) {
this.folderName = folderName;
}
public File generateZipFile(String pathName) {
File zipFile = null;
try {
zipFile = new File(pathName + ".zip");
java.util.zip.ZipOutputStream zipOut = new java.util.zip.ZipOutputStream(new FileOutputStream(zipFile));
IOUtils.zip(zipOut, new File(pathName));
zipOut.close();
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
return zipFile;
}
public void generateFile(JSONArray jsonArray, String folderName) {
if (jsonArray.size() == 0) {
return;
}
try {
String content = jsonArray.toString();
String fileName = String.valueOf(UUID.randomUUID());
File file = new File(folderName + File.separator + fileName + ".json");
StableUtils.makesureFileExist(file);
FileOutputStream out = new FileOutputStream(file);
InputStream in = new ByteArrayInputStream(content.getBytes(EncodeConstants.ENCODING_UTF_8));
IOUtils.copyBinaryTo(in, out);
in.close();
out.close();
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
}
public void deleteFileAndZipFile(File zipFile, String pathName) {
File file = new File(pathName);
CommonUtils.deleteFile(file);
CommonUtils.deleteFile(zipFile);
}
/**
* 上传文件到云中心
* @param file 待上传文件
* @param keyFileName 目标文件
* @throws IOException
*/
public static void uploadFile(File file, String keyFileName) throws IOException {
String today = new JDateTime().toString("YYYY-MM-DD");
HttpClient httpclient = new DefaultHttpClient();
try {
String signedUrl = generateSignedUploadUrl(FOCUS_POINT_FILE_ROOT_PATH + File.separator + today + File.separator +keyFileName);
if(StringUtils.isEmpty(signedUrl)){
FineLoggerFactory.getLogger().error("signedUrl is null.");
return;
}
HttpPut httpPost = new HttpPut(signedUrl);
httpPost.addHeader("Content-Type","application/octet-stream");
FileEntity fileEntity = new FileEntity(file);
httpPost.setEntity(fileEntity);
HttpResponse response = httpclient.execute(httpPost);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == SC_OK) {
HttpEntity resEntity = response.getEntity();
EntityUtils.consume(resEntity);
} else {
HttpEntity entity = response.getEntity();
String result = EntityUtils.toString(entity, "utf-8");
FineLoggerFactory.getLogger().info("upload file result:" + result);
}
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
}
private static String generateSignedUploadUrl(String fileKeyName) throws IOException {
String url = CloudCenter.getInstance().acquireUrlByKind(INTELLI_OPERATION_URL, OPERATION_URL);
Map<String, String> parameters = new HashMap<String, String>();
parameters.put(ATTR_KEY, fileKeyName);
parameters.put(ATTR_SIGNATURE, String.valueOf(CommonUtils.signature()));
String responseText = HttpToolbox.get(url, parameters);
try {
JSONObject data = new JSONObject(responseText);
if ("success".equals(data.optString("status"))) {
return data.optString("url");
}
} catch (JSONException e) {
FineLoggerFactory.getLogger().error("Illegal response text."+e, e.getMessage());
}
return null;
}
}

133
designer-realize/src/main/java/com/fr/design/mainframe/messagecollect/impl/AbstractSendDataToCloud.java

@ -0,0 +1,133 @@
package com.fr.design.mainframe.messagecollect.impl;
import com.fr.design.mainframe.messagecollect.entity.FileEntityBuilder;
import com.fr.design.mainframe.messagecollect.utils.MessageCollectUtils;
import com.fr.intelli.record.MetricRegistry;
import com.fr.json.JSONArray;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.ProductConstants;
import com.fr.stable.StableUtils;
import com.fr.stable.query.QueryFactory;
import com.fr.stable.query.condition.QueryCondition;
import com.fr.stable.query.data.DataList;
import com.fr.stable.query.restriction.RestrictionFactory;
import com.fr.stable.xml.XMLTools;
import com.fr.stable.xml.XMLable;
import java.io.File;
import java.io.FileOutputStream;
/**
* @author alex sung
* @date 2019/3/22
*/
public abstract class AbstractSendDataToCloud implements XMLable {
private static final String FILE_NAME = "messagecollect.info";
private static final String COLUMN_TIME = "time";
protected String lastTime;
private static final int PAGE_SIZE = 10000;
private long totalCount = -1;
private FileEntityBuilder fileEntityBuilder;
public FileEntityBuilder getFileEntityBuilder() {
return fileEntityBuilder;
}
public void setFileEntityBuilder(FileEntityBuilder fileEntityBuilder) {
this.fileEntityBuilder = fileEntityBuilder;
}
public String getLastTime() {
return lastTime;
}
public void setLastTime(String lastTime) {
this.lastTime = lastTime;
}
public void saveLastTime() {
setLastTime(MessageCollectUtils.dateToString());
try {
FileOutputStream out = new FileOutputStream(getLastTimeFile());
XMLTools.writeOutputStreamXML(this, out);
} catch (Exception ex) {
FineLoggerFactory.getLogger().error(ex.getMessage());
}
}
public static File getLastTimeFile() {
return new File(StableUtils.pathJoin(ProductConstants.getEnvHome(), FILE_NAME));
}
public <T> void queryData(long currentTime, long lastTime, Class<T> tClass) {
queryAndSendOnePageFunctionContent(currentTime, lastTime, 0, tClass);
long page = (totalCount / PAGE_SIZE) + 1;
for (int i = 1; i < page; i++) {
queryAndSendOnePageFunctionContent(currentTime, lastTime, i, tClass);
}
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
private <T> void queryAndSendOnePageFunctionContent(long current, long last, int page, Class<T> tClass) {
QueryCondition condition = QueryFactory.create()
.skip(page * PAGE_SIZE)
.count(PAGE_SIZE)
.addSort(COLUMN_TIME, true)
.addRestriction(RestrictionFactory.lte(COLUMN_TIME, current))
.addRestriction(RestrictionFactory.gte(COLUMN_TIME, last));
try {
DataList<T> points = MetricRegistry.getMetric().find(tClass, condition);
//第一次查询获取总记录数
if (page == 0) {
totalCount = points.getTotalCount();
}
dealWithData(points);
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
}
public <T> void dealWithData(DataList<T> tDataList) throws Exception {
generateThisPageFile(tDataList);
}
private <T> void generateThisPageFile(DataList<T> points) {
File file = null;
try {
JSONArray jsonArray = dealWithSendFunctionContent(points);
//生成json文件
fileEntityBuilder.generateFile(jsonArray, getFileEntityBuilder().getFolderName());
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
}
public abstract <T> JSONArray dealWithSendFunctionContent(DataList<T> focusPoints);
/**
* 生成zip并发送zip文件
* @param pathName zip文件路径
*/
protected void sendZipFile(String pathName) {
File file = null;
try {
file = fileEntityBuilder.generateZipFile(pathName);
if (file != null) {
fileEntityBuilder.uploadFile(file, file.getName());
}
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
return;
}
fileEntityBuilder.deleteFileAndZipFile(file, pathName);
}
}

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

Loading…
Cancel
Save