diff --git a/designer-base/src/main/java/com/fr/design/DesignerEnvManager.java b/designer-base/src/main/java/com/fr/design/DesignerEnvManager.java index 66a516554..dcc588fa3 100644 --- a/designer-base/src/main/java/com/fr/design/DesignerEnvManager.java +++ b/designer-base/src/main/java/com/fr/design/DesignerEnvManager.java @@ -14,6 +14,8 @@ import com.fr.design.env.DesignerWorkspaceType; import com.fr.design.env.LocalDesignerWorkspaceInfo; import com.fr.design.env.RemoteDesignerWorkspaceInfo; import com.fr.design.file.HistoryTemplateListPane; +import com.fr.design.mainframe.vcs.VcsConfigManager; +import com.fr.design.update.push.DesignerPushUpdateConfigManager; import com.fr.design.style.color.ColorSelectConfigManager; import com.fr.design.utils.DesignUtils; import com.fr.file.FILEFactory; @@ -146,6 +148,9 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter { */ private AlphaFineConfigManager alphaFineConfigManager = new AlphaFineConfigManager(); + private DesignerPushUpdateConfigManager designerPushUpdateConfigManager = DesignerPushUpdateConfigManager.getInstance(); + + private VcsConfigManager vcsConfigManager = VcsConfigManager.getInstance(); public static final String CAS_CERTIFICATE_PATH = "certificatePath"; @@ -693,6 +698,14 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter { this.joinProductImprove = joinProductImprove; } + public boolean isAutoPushUpdateEnabled() { + return designerPushUpdateConfigManager.isAutoPushUpdateEnabled(); + } + + public void setAutoPushUpdateEnabled(boolean autoPushUpdateEnabled) { + designerPushUpdateConfigManager.setAutoPushUpdateEnabled(autoPushUpdateEnabled); + } + /** * 是否磁盘空间参数 * @@ -1333,6 +1346,7 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter { } } + private void readJettyPort(XMLableReader reader) { String tmpVal; if ((tmpVal = reader.getElementValue()) != null) { @@ -1477,14 +1491,18 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter { readUUID(reader); } else if ("status".equals(name)) { readActiveStatus(reader); - } else if (CAS_PARAS.equals(name)) { + } else if (ComparatorUtils.equals(CAS_PARAS, name)) { readHttpsParas(reader); - } else if ("AlphaFineConfigManager".equals(name)) { + } else if (name.equals("AlphaFineConfigManager")) { readAlphaFineAttr(reader); - } else if ("RecentColors".equals(name)) { + } else if (name.equals("RecentColors")) { readRecentColor(reader); } else if ("OpenDebug".equals(name)) { readOpenDebug(reader); + } else if (name.equals(DesignerPushUpdateConfigManager.XML_TAG)) { + readDesignerPushUpdateAttr(reader); + } else if (name.equals(vcsConfigManager.XML_TAG)) { + readVcsAttr(reader); } else { readLayout(reader, name); } @@ -1667,6 +1685,14 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter { checkRecentOpenedFileNum(); } + private void readDesignerPushUpdateAttr(XMLableReader reader) { + reader.readXMLObject(designerPushUpdateConfigManager); + } + + private void readVcsAttr(XMLableReader reader) { + reader.readXMLObject(vcsConfigManager); + } + /** * Write XML.
* The method will be invoked when save data to XML file.
@@ -1690,6 +1716,8 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter { writeAlphaFineAttr(writer); writeRecentColor(writer); writeOpenDebug(writer); + writeDesignerPushUpdateAttr(writer); + writeVcsAttr(writer); writer.end(); } @@ -1713,7 +1741,6 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter { } } - //写入uuid private void writeUUID(XMLPrintWriter writer) { writer.startTAG("uuid"); @@ -1929,4 +1956,20 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter { .end(); } + private void writeDesignerPushUpdateAttr(XMLPrintWriter writer) { + this.designerPushUpdateConfigManager.writeXML(writer); + } + + private void writeVcsAttr(XMLPrintWriter writer) { + this.vcsConfigManager.writeXML(writer); + } + + + public VcsConfigManager getVcsConfigManager() { + return vcsConfigManager; + } + + public void setVcsConfigManager(VcsConfigManager vcsConfigManager) { + this.vcsConfigManager = vcsConfigManager; + } } diff --git a/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java b/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java index ae6ba4c25..9775de2f5 100644 --- a/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java +++ b/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java @@ -24,6 +24,9 @@ import com.fr.design.layout.FRGUIPaneFactory; import com.fr.design.layout.TableLayout; import com.fr.design.layout.TableLayoutHelper; import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.vcs.VcsConfigManager; +import com.fr.design.mainframe.vcs.common.VcsHelper; +import com.fr.design.update.push.DesignerPushUpdateManager; import com.fr.design.utils.gui.GUICoreUtils; import com.fr.design.widget.FRWidgetFactory; import com.fr.general.ComparatorUtils; @@ -41,6 +44,8 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; @@ -118,7 +123,6 @@ public class PreferencePane extends BasicPane { private KeyStroke shortCutKeyStore = null; private UIColorButton gridLineColorTBButton; - private UIColorButton paginationLineColorTBButton; private UICheckBox supportCellEditorDefCheckBox; @@ -134,7 +138,16 @@ public class PreferencePane extends BasicPane { private UISpinner cachingTemplateSpinner; private UICheckBox openDebugComboBox; private UICheckBox useOptimizedUPMCheckbox; - private UICheckBox joinProductImprove; + private UICheckBox joinProductImproveCheckBox; + private UICheckBox autoPushUpdateCheckBox; + + private UICheckBox vcsEnableCheckBox; + private UICheckBox saveCommitCheckBox; + private UICheckBox useIntervalCheckBox; + private IntegerEditor saveIntervalEditor; + private UILabel remindVcsLabel; + + public PreferencePane() { this.initComponents(); @@ -152,11 +165,11 @@ public class PreferencePane extends BasicPane { jtabPane.addTab(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Advanced"), advancePane); contentPane.add(jtabPane, BorderLayout.NORTH); - createFunctionPane(generalPane); createEditPane(generalPane); createGuiOfGridPane(generalPane); createColorSettingPane(generalPane); + createVcsSettingPane(generalPane); // ConfPane JPanel confLocationPane = FRGUIPaneFactory.createX_AXISBoxInnerContainer_S_Pane(); @@ -184,10 +197,14 @@ public class PreferencePane extends BasicPane { upmSelectorPane.add(useOptimizedUPMCheckbox); advancePane.add(upmSelectorPane); + JPanel improvePane = FRGUIPaneFactory.createVerticalTitledBorderPane(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Product_Improve")); + joinProductImproveCheckBox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Join_Product_Improve")); + improvePane.add(joinProductImproveCheckBox); - JPanel improvePane = FRGUIPaneFactory.createTitledBorderPane(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Product_Improve")); - joinProductImprove = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Join_Product_Improve")); - improvePane.add(joinProductImprove); + if (DesignerPushUpdateManager.getInstance().isAutoPushUpdateSupported()) { + autoPushUpdateCheckBox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Automatic_Push_Update")); + improvePane.add(autoPushUpdateCheckBox); + } JPanel spaceUpPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); spaceUpPane.add(oraclePane, BorderLayout.NORTH); @@ -196,6 +213,45 @@ public class PreferencePane extends BasicPane { advancePane.add(spaceUpPane); } + private void createVcsSettingPane(JPanel generalPane) { + JPanel vcsPane = FRGUIPaneFactory.createVerticalTitledBorderPane(Toolkit.i18nText("Fine-Design_Vcs_Title")); + generalPane.add(vcsPane); + remindVcsLabel = new UILabel(Toolkit.i18nText("Fine-Design_Vcs_Remind")); + remindVcsLabel.setVisible(!VcsHelper.needInit()); + vcsEnableCheckBox = new UICheckBox(Toolkit.i18nText("Fine-Design_Vcs_SaveAuto")); + saveCommitCheckBox = new UICheckBox(Toolkit.i18nText("Fine-Design_Vcs_No_Delete")); + saveIntervalEditor = new IntegerEditor(60); + useIntervalCheckBox = new UICheckBox(); + JPanel enableVcsPanel = new JPanel(FRGUIPaneFactory.createLeftZeroLayout()); + enableVcsPanel.add(vcsEnableCheckBox); + enableVcsPanel.add(remindVcsLabel); + JPanel intervalPanel = new JPanel(FRGUIPaneFactory.createLeftZeroLayout()); + UILabel everyLabel = new UILabel(Toolkit.i18nText("Fine-Design_Vcs_Every")); + UILabel delayLabel = new UILabel(Toolkit.i18nText("Fine-Design_Vcs_Delay")); + intervalPanel.add(useIntervalCheckBox); + intervalPanel.add(everyLabel); + intervalPanel.add(saveIntervalEditor); + intervalPanel.add(delayLabel); + vcsEnableCheckBox.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + boolean selected = vcsEnableCheckBox.isSelected(); + if (selected) { + saveCommitCheckBox.setEnabled(true); + saveIntervalEditor.setEnabled(true); + useIntervalCheckBox.setEnabled(true); + } else { + saveCommitCheckBox.setEnabled(false); + saveIntervalEditor.setEnabled(false); + useIntervalCheckBox.setEnabled(false); + } + } + }); + vcsPane.add(enableVcsPanel); + vcsPane.add(intervalPanel); + vcsPane.add(saveCommitCheckBox); + } + private void createFunctionPane(JPanel generalPane) { JPanel functionPane = FRGUIPaneFactory.createTitledBorderPane(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Preference_Function")); generalPane.add(functionPane); @@ -545,6 +601,22 @@ public class PreferencePane extends BasicPane { defaultStringToFormulaBox.setEnabled(false); defaultStringToFormulaBox.setSelected(false); } + VcsConfigManager vcsConfigManager = designerEnvManager.getVcsConfigManager(); + if (VcsHelper.needInit()) { + vcsEnableCheckBox.setSelected(vcsConfigManager.isVcsEnable()); + } else { + vcsEnableCheckBox.setEnabled(false); + vcsEnableCheckBox.setSelected(false); + } + if (!vcsEnableCheckBox.isSelected()) { + saveCommitCheckBox.setEnabled(false); + saveIntervalEditor.setEnabled(false); + useIntervalCheckBox.setEnabled(false); + } + + saveIntervalEditor.setValue(vcsConfigManager.getSaveInterval()); + saveCommitCheckBox.setSelected(vcsConfigManager.isSaveCommit()); + useIntervalCheckBox.setSelected(vcsConfigManager.isUseInterval()); supportCellEditorDefCheckBox.setSelected(designerEnvManager.isSupportCellEditorDef()); @@ -565,12 +637,15 @@ public class PreferencePane extends BasicPane { this.portEditor.setValue(new Integer(designerEnvManager.getEmbedServerPort())); openDebugComboBox.setSelected(designerEnvManager.isOpenDebug()); - useOptimizedUPMCheckbox.setSelected(ServerPreferenceConfig.getInstance().isUseOptimizedUPM()); this.oracleSpace.setSelected(designerEnvManager.isOracleSystemSpace()); this.cachingTemplateSpinner.setValue(designerEnvManager.getCachingTemplateLimit()); - this.joinProductImprove.setSelected(designerEnvManager.isJoinProductImprove()); + this.joinProductImproveCheckBox.setSelected(designerEnvManager.isJoinProductImprove()); + + if (this.autoPushUpdateCheckBox != null) { + this.autoPushUpdateCheckBox.setSelected(designerEnvManager.isAutoPushUpdateEnabled()); + } } private int chooseCase(int sign) { @@ -631,7 +706,15 @@ public class PreferencePane extends BasicPane { designerEnvManager.setOracleSystemSpace(this.oracleSpace.isSelected()); designerEnvManager.setCachingTemplateLimit((int) this.cachingTemplateSpinner.getValue()); - designerEnvManager.setJoinProductImprove(this.joinProductImprove.isSelected()); + designerEnvManager.setJoinProductImprove(this.joinProductImproveCheckBox.isSelected()); + VcsConfigManager vcsConfigManager = designerEnvManager.getVcsConfigManager(); + vcsConfigManager.setSaveInterval(this.saveIntervalEditor.getValue()); + vcsConfigManager.setVcsEnable(this.vcsEnableCheckBox.isSelected()); + vcsConfigManager.setSaveCommit(this.saveCommitCheckBox.isSelected()); + vcsConfigManager.setUseInterval(this.useIntervalCheckBox.isSelected()); + if (this.autoPushUpdateCheckBox != null) { + designerEnvManager.setAutoPushUpdateEnabled(this.autoPushUpdateCheckBox.isSelected()); + } designerEnvManager.setUndoLimit(maxUndoLimit.getSelectedIndex() * SELECTED_INDEX_5); if (maxUndoLimit.getSelectedIndex() == SELECTED_INDEX_5) { @@ -649,6 +732,7 @@ public class PreferencePane extends BasicPane { return new Class[]{Log4jConfig.class}; } }); + Configurations.update(new Worker() { @Override public void run() { diff --git a/designer-base/src/main/java/com/fr/design/gui/ispinner/UISpinner.java b/designer-base/src/main/java/com/fr/design/gui/ispinner/UISpinner.java index c9c398829..ba6913a38 100644 --- a/designer-base/src/main/java/com/fr/design/gui/ispinner/UISpinner.java +++ b/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){ diff --git a/designer-base/src/main/java/com/fr/design/layout/FRGUIPaneFactory.java b/designer-base/src/main/java/com/fr/design/layout/FRGUIPaneFactory.java index db48cf995..d906cc39e 100644 --- a/designer-base/src/main/java/com/fr/design/layout/FRGUIPaneFactory.java +++ b/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; + } + /** * 创建一个带标题边框面板并且居中显示 * diff --git a/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrameFileDealerPane.java b/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrameFileDealerPane.java index 7b9db81ed..df9f3bab3 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrameFileDealerPane.java +++ b/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; } diff --git a/designer-base/src/main/java/com/fr/design/mainframe/DesktopCardPane.java b/designer-base/src/main/java/com/fr/design/mainframe/DesktopCardPane.java index a5f4a72ac..58cd014e0 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/DesktopCardPane.java +++ b/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() { diff --git a/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java b/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java index 35f7d3391..ffa550962 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java +++ b/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> private static short currentIndex = 0;// 此变量用于多次新建模板时,让名字不重复 private DesignModelAdapter 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> 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> 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> // 为收集模版信息作准备 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 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> return false; } collectInfo(); + if (DesignerEnvManager.getEnvManager().getVcsConfigManager().isVcsEnable()) { + VcsHelper.dealWithVcs(this); + } return this.saveFile(); } @@ -621,15 +628,20 @@ public abstract class JTemplate> } } + // 保存新模板时会进入此方法(新建模板直接保存,或者另存为) 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()); diff --git a/designer-base/src/main/java/com/fr/design/mainframe/JVirtualTemplate.java b/designer-base/src/main/java/com/fr/design/mainframe/JVirtualTemplate.java index cea108892..2c1818614 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/JVirtualTemplate.java +++ b/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; diff --git a/designer-base/src/main/java/com/fr/design/mainframe/template/info/DesignerOpenHistory.java b/designer-base/src/main/java/com/fr/design/mainframe/template/info/DesignerOpenHistory.java new file mode 100644 index 000000000..096b6d16a --- /dev/null +++ b/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(); + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/template/info/SendHelper.java b/designer-base/src/main/java/com/fr/design/mainframe/template/info/SendHelper.java new file mode 100644 index 000000000..db489fb3c --- /dev/null +++ b/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 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; + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/template/info/TemplateInfo.java b/designer-base/src/main/java/com/fr/design/mainframe/template/info/TemplateInfo.java new file mode 100644 index 000000000..ffd9aaf3b --- /dev/null +++ b/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 processMap = new HashMap<>(); + private Map 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 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 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; + } +} \ No newline at end of file diff --git a/designer-base/src/main/java/com/fr/design/mainframe/template/info/TemplateInfoCollector.java b/designer-base/src/main/java/com/fr/design/mainframe/template/info/TemplateInfoCollector.java new file mode 100644 index 000000000..e7fc95594 --- /dev/null +++ b/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 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 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(); + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/templateinfo/TemplateProcessInfo.java b/designer-base/src/main/java/com/fr/design/mainframe/template/info/TemplateProcessInfo.java similarity index 74% rename from designer-base/src/main/java/com/fr/design/mainframe/templateinfo/TemplateProcessInfo.java rename to designer-base/src/main/java/com/fr/design/mainframe/template/info/TemplateProcessInfo.java index 0307e4fd6..427d48c73 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/templateinfo/TemplateProcessInfo.java +++ b/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 { protected T template; diff --git a/designer-base/src/main/java/com/fr/design/mainframe/template/info/TimeConsumeTimer.java b/designer-base/src/main/java/com/fr/design/mainframe/template/info/TimeConsumeTimer.java new file mode 100644 index 000000000..527341411 --- /dev/null +++ b/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; + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/templateinfo/TemplateInfoCollector.java b/designer-base/src/main/java/com/fr/design/mainframe/templateinfo/TemplateInfoCollector.java deleted file mode 100644 index 57ff2ce8a..000000000 --- a/designer-base/src/main/java/com/fr/design/mainframe/templateinfo/TemplateInfoCollector.java +++ /dev/null @@ -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 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> 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 processMap = (HashMap) 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 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 templateInfo; - - long timeConsume = ((saveTime - openTime) / ONE_THOUSAND); // 制作模板耗时(单位:s) - String templateID = t.getTemplateID(); - - if (inList(t)) { // 已有记录 - templateInfo = templateInfoList.get(templateID); - // 更新 conusmingMap - HashMap consumingMap = (HashMap) 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 getNewConsumingMap(String templateID, long openTime, long timeConsume) { - HashMap 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 getProcessMap(String templateID, JTemplate jt) { - HashMap 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> completeTemplatesInfo = getCompleteTemplatesInfo(); - for (HashMap 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 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> getCompleteTemplatesInfo() { - ArrayList> completeTemplatesInfo = new ArrayList<>(); - ArrayList testTemplateKeys = new ArrayList<>(); // 保存测试模板的key - for (String key : templateInfoList.keySet()) { - HashMap templateInfo = templateInfoList.get(key); - if ((int) templateInfo.get(ATTR_DAY_COUNT) <= COMPLETE_DAY_COUNT) { // 未完成模板 - continue; - } - if (isTestTemplate(templateInfo)) { - testTemplateKeys.add(key); - continue; - } - HashMap consumingMap = (HashMap) templateInfo.get(XML_CONSUMING_MAP); - HashMap processMap = (HashMap) templateInfo.get(XML_PROCESS_MAP); - String jsonConsumingMap = new JSONObject(consumingMap).toString(); - String jsonProcessMap = new JSONObject(processMap).toString(); - HashMap 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 templateInfo) { - HashMap processMap = (HashMap) 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 processMap = new HashMap<>(); - private HashMap consumingMap = new HashMap<>(); - - @SuppressWarnings("unchecked") - public TemplateInfo(HashMap templateInfo) { - this.dayCount = (int) templateInfo.get(ATTR_DAY_COUNT); - this.processMap = (HashMap) templateInfo.get(XML_PROCESS_MAP); - this.consumingMap = (HashMap) templateInfo.get(XML_CONSUMING_MAP); - this.templateID = (String) processMap.get(ATTR_TEMPLATE_ID); - } - - public TemplateInfo() { - } - - public String getTemplateID() { - return templateID; - } - - public HashMap getTemplateInfo() { - HashMap 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) { - // 什么也不做,使用默认值 - } - } - } - - } -} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/toolbar/ToolBarMenuDock.java b/designer-base/src/main/java/com/fr/design/mainframe/toolbar/ToolBarMenuDock.java index 2577b6bd0..c26dce9a0 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/toolbar/ToolBarMenuDock.java +++ b/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; diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/VcsConfigManager.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/VcsConfigManager.java new file mode 100644 index 000000000..bbb6e9d9b --- /dev/null +++ b/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(); + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/common/VcsCacheFileNodeFile.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/common/VcsCacheFileNodeFile.java new file mode 100644 index 000000000..9933fc0e7 --- /dev/null +++ b/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))); + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/common/VcsHelper.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/common/VcsHelper.java new file mode 100644 index 000000000..4fc99731a --- /dev/null +++ b/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(); + + } + + +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/EditFileVersionDialog.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/EditFileVersionDialog.java new file mode 100644 index 000000000..f6533de4d --- /dev/null +++ b/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 { + + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/FileVersionCellEditor.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/FileVersionCellEditor.java new file mode 100644 index 000000000..bdb4bc1ee --- /dev/null +++ b/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(); + } +} \ No newline at end of file diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/FileVersionCellRender.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/FileVersionCellRender.java new file mode 100644 index 000000000..c1cc23fc4 --- /dev/null +++ b/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; + } + + +} \ No newline at end of file diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/FileVersionDialog.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/FileVersionDialog.java new file mode 100644 index 000000000..cb7234640 --- /dev/null +++ b/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 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 { + + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/FileVersionFirstRowPanel.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/FileVersionFirstRowPanel.java new file mode 100644 index 000000000..9d892ba53 --- /dev/null +++ b/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); + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/FileVersionRowPanel.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/FileVersionRowPanel.java new file mode 100644 index 000000000..657e990cc --- /dev/null +++ b/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; + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/FileVersionTable.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/FileVersionTable.java new file mode 100644 index 000000000..aa9c87ca0 --- /dev/null +++ b/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())); + + 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 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 vcsEntities; + + + CellModel(List 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; + } + + + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/FileVersionsPanel.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/FileVersionsPanel.java new file mode 100644 index 000000000..2e68e0e33 --- /dev/null +++ b/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); + } +} diff --git a/designer-base/src/main/java/com/fr/design/onlineupdate/actions/FileDownloader.java b/designer-base/src/main/java/com/fr/design/update/actions/FileDownloader.java similarity index 95% rename from designer-base/src/main/java/com/fr/design/onlineupdate/actions/FileDownloader.java rename to designer-base/src/main/java/com/fr/design/update/actions/FileDownloader.java index d076cc01e..556fa5113 100644 --- a/designer-base/src/main/java/com/fr/design/onlineupdate/actions/FileDownloader.java +++ b/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; diff --git a/designer-base/src/main/java/com/fr/design/onlineupdate/actions/SoftwareUpdateAction.java b/designer-base/src/main/java/com/fr/design/update/actions/SoftwareUpdateAction.java similarity index 83% rename from designer-base/src/main/java/com/fr/design/onlineupdate/actions/SoftwareUpdateAction.java rename to designer-base/src/main/java/com/fr/design/update/actions/SoftwareUpdateAction.java index 1b14f5f0b..cb412dcb1 100644 --- a/designer-base/src/main/java/com/fr/design/onlineupdate/actions/SoftwareUpdateAction.java +++ b/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; diff --git a/designer-base/src/main/java/com/fr/design/onlineupdate/domain/DownloadItem.java b/designer-base/src/main/java/com/fr/design/update/domain/DownloadItem.java similarity index 98% rename from designer-base/src/main/java/com/fr/design/onlineupdate/domain/DownloadItem.java rename to designer-base/src/main/java/com/fr/design/update/domain/DownloadItem.java index 6871190aa..220f2602d 100644 --- a/designer-base/src/main/java/com/fr/design/onlineupdate/domain/DownloadItem.java +++ b/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; diff --git a/designer-base/src/main/java/com/fr/design/onlineupdate/domain/UpdateConstants.java b/designer-base/src/main/java/com/fr/design/update/domain/UpdateConstants.java similarity index 97% rename from designer-base/src/main/java/com/fr/design/onlineupdate/domain/UpdateConstants.java rename to designer-base/src/main/java/com/fr/design/update/domain/UpdateConstants.java index 293c5d1f0..737deafe6 100644 --- a/designer-base/src/main/java/com/fr/design/onlineupdate/domain/UpdateConstants.java +++ b/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. diff --git a/designer-base/src/main/java/com/fr/design/onlineupdate/domain/UpdateInfoCachePropertyManager.java b/designer-base/src/main/java/com/fr/design/update/domain/UpdateInfoCachePropertyManager.java similarity index 95% rename from designer-base/src/main/java/com/fr/design/onlineupdate/domain/UpdateInfoCachePropertyManager.java rename to designer-base/src/main/java/com/fr/design/update/domain/UpdateInfoCachePropertyManager.java index 7895c6913..32537fcf4 100644 --- a/designer-base/src/main/java/com/fr/design/onlineupdate/domain/UpdateInfoCachePropertyManager.java +++ b/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; diff --git a/designer-base/src/main/java/com/fr/design/onlineupdate/factory/DirectoryOperationFactory.java b/designer-base/src/main/java/com/fr/design/update/factory/DirectoryOperationFactory.java similarity index 98% rename from designer-base/src/main/java/com/fr/design/onlineupdate/factory/DirectoryOperationFactory.java rename to designer-base/src/main/java/com/fr/design/update/factory/DirectoryOperationFactory.java index 9d929c095..906b71412 100644 --- a/designer-base/src/main/java/com/fr/design/onlineupdate/factory/DirectoryOperationFactory.java +++ b/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; diff --git a/designer-base/src/main/java/com/fr/design/update/push/DesignerPushUpdateConfigManager.java b/designer-base/src/main/java/com/fr/design/update/push/DesignerPushUpdateConfigManager.java new file mode 100644 index 000000000..9ea734e73 --- /dev/null +++ b/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; + } +} diff --git a/designer-base/src/main/java/com/fr/design/update/push/DesignerPushUpdateDialog.java b/designer-base/src/main/java/com/fr/design/update/push/DesignerPushUpdateDialog.java new file mode 100644 index 000000000..3327dd9ed --- /dev/null +++ b/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 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() + .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); + } + } +} diff --git a/designer-base/src/main/java/com/fr/design/update/push/DesignerPushUpdateManager.java b/designer-base/src/main/java/com/fr/design/update/push/DesignerPushUpdateManager.java new file mode 100644 index 000000000..dcb5d45fd --- /dev/null +++ b/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()); + } +} diff --git a/designer-base/src/main/java/com/fr/design/update/push/DesignerUpdateInfo.java b/designer-base/src/main/java/com/fr/design/update/push/DesignerUpdateInfo.java new file mode 100644 index 000000000..5d5e02a47 --- /dev/null +++ b/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; + } + +} diff --git a/designer-base/src/main/java/com/fr/design/onlineupdate/ui/dialog/EncodingDetect.java b/designer-base/src/main/java/com/fr/design/update/ui/dialog/EncodingDetect.java similarity index 99% rename from designer-base/src/main/java/com/fr/design/onlineupdate/ui/dialog/EncodingDetect.java rename to designer-base/src/main/java/com/fr/design/update/ui/dialog/EncodingDetect.java index ecd63fa16..41bba0f4f 100644 --- a/designer-base/src/main/java/com/fr/design/onlineupdate/ui/dialog/EncodingDetect.java +++ b/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; diff --git a/designer-base/src/main/java/com/fr/design/onlineupdate/ui/dialog/RestoreDialog.java b/designer-base/src/main/java/com/fr/design/update/ui/dialog/RestoreDialog.java similarity index 93% rename from designer-base/src/main/java/com/fr/design/onlineupdate/ui/dialog/RestoreDialog.java rename to designer-base/src/main/java/com/fr/design/update/ui/dialog/RestoreDialog.java index c316311ab..402df06b8 100644 --- a/designer-base/src/main/java/com/fr/design/onlineupdate/ui/dialog/RestoreDialog.java +++ b/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; diff --git a/designer-base/src/main/java/com/fr/design/onlineupdate/ui/dialog/UpdateMainDialog.java b/designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java similarity index 96% rename from designer-base/src/main/java/com/fr/design/onlineupdate/ui/dialog/UpdateMainDialog.java rename to designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java index 9438df33b..ad6c4b33f 100644 --- a/designer-base/src/main/java/com/fr/design/onlineupdate/ui/dialog/UpdateMainDialog.java +++ b/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(); diff --git a/designer-base/src/main/java/com/fr/design/onlineupdate/ui/widget/ColorfulCellRender.java b/designer-base/src/main/java/com/fr/design/update/ui/widget/ColorfulCellRender.java similarity index 97% rename from designer-base/src/main/java/com/fr/design/onlineupdate/ui/widget/ColorfulCellRender.java rename to designer-base/src/main/java/com/fr/design/update/ui/widget/ColorfulCellRender.java index 461489839..e5d644b20 100644 --- a/designer-base/src/main/java/com/fr/design/onlineupdate/ui/widget/ColorfulCellRender.java +++ b/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; diff --git a/designer-base/src/main/java/com/fr/design/onlineupdate/ui/widget/LoadingLabel.java b/designer-base/src/main/java/com/fr/design/update/ui/widget/LoadingLabel.java similarity index 96% rename from designer-base/src/main/java/com/fr/design/onlineupdate/ui/widget/LoadingLabel.java rename to designer-base/src/main/java/com/fr/design/update/ui/widget/LoadingLabel.java index a21d67e46..f45f6d67f 100644 --- a/designer-base/src/main/java/com/fr/design/onlineupdate/ui/widget/LoadingLabel.java +++ b/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; diff --git a/designer-base/src/main/java/com/fr/design/onlineupdate/ui/widget/UpdateActionLabel.java b/designer-base/src/main/java/com/fr/design/update/ui/widget/UpdateActionLabel.java similarity index 98% rename from designer-base/src/main/java/com/fr/design/onlineupdate/ui/widget/UpdateActionLabel.java rename to designer-base/src/main/java/com/fr/design/update/ui/widget/UpdateActionLabel.java index 74c2b6ae5..efd10fb8b 100644 --- a/designer-base/src/main/java/com/fr/design/onlineupdate/ui/widget/UpdateActionLabel.java +++ b/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; diff --git a/designer-base/src/main/java/com/fr/design/onlineupdate/ui/widget/UpdateInfoTable.java b/designer-base/src/main/java/com/fr/design/update/ui/widget/UpdateInfoTable.java similarity index 96% rename from designer-base/src/main/java/com/fr/design/onlineupdate/ui/widget/UpdateInfoTable.java rename to designer-base/src/main/java/com/fr/design/update/ui/widget/UpdateInfoTable.java index f94a514ad..c7690f3d5 100644 --- a/designer-base/src/main/java/com/fr/design/onlineupdate/ui/widget/UpdateInfoTable.java +++ b/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; diff --git a/designer-base/src/main/java/com/fr/design/onlineupdate/ui/widget/UpdateInfoTableCellRender.java b/designer-base/src/main/java/com/fr/design/update/ui/widget/UpdateInfoTableCellRender.java similarity index 96% rename from designer-base/src/main/java/com/fr/design/onlineupdate/ui/widget/UpdateInfoTableCellRender.java rename to designer-base/src/main/java/com/fr/design/update/ui/widget/UpdateInfoTableCellRender.java index 22c603c86..9a853762e 100644 --- a/designer-base/src/main/java/com/fr/design/onlineupdate/ui/widget/UpdateInfoTableCellRender.java +++ b/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; diff --git a/designer-base/src/main/java/com/fr/design/onlineupdate/ui/widget/UpdateInfoTableModel.java b/designer-base/src/main/java/com/fr/design/update/ui/widget/UpdateInfoTableModel.java similarity index 96% rename from designer-base/src/main/java/com/fr/design/onlineupdate/ui/widget/UpdateInfoTableModel.java rename to designer-base/src/main/java/com/fr/design/update/ui/widget/UpdateInfoTableModel.java index 147f88a29..0fa128e36 100644 --- a/designer-base/src/main/java/com/fr/design/onlineupdate/ui/widget/UpdateInfoTableModel.java +++ b/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; diff --git a/designer-base/src/main/java/com/fr/design/onlineupdate/ui/widget/UpdateInfoTextAreaCellRender.java b/designer-base/src/main/java/com/fr/design/update/ui/widget/UpdateInfoTextAreaCellRender.java similarity index 96% rename from designer-base/src/main/java/com/fr/design/onlineupdate/ui/widget/UpdateInfoTextAreaCellRender.java rename to designer-base/src/main/java/com/fr/design/update/ui/widget/UpdateInfoTextAreaCellRender.java index 1e3cebdf8..4f9836d4c 100644 --- a/designer-base/src/main/java/com/fr/design/onlineupdate/ui/widget/UpdateInfoTextAreaCellRender.java +++ b/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; diff --git a/designer-base/src/main/resources/com/fr/aspectj/designerbase/TemplateProcessTracker.aj b/designer-base/src/main/resources/com/fr/aspectj/designerbase/TemplateProcessTracker.aj deleted file mode 100644 index 1ab23100b..000000000 --- a/designer-base/src/main/resources/com/fr/aspectj/designerbase/TemplateProcessTracker.aj +++ /dev/null @@ -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); - } - - -} diff --git a/designer-base/src/main/resources/com/fr/design/images/vcs/icon_back_normal@2x.png b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_back_normal@2x.png new file mode 100644 index 000000000..bb3fd50cd Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_back_normal@2x.png differ diff --git a/designer-base/src/main/resources/com/fr/design/images/vcs/icon_delete.png b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_delete.png new file mode 100755 index 000000000..6fb2baac5 Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_delete.png differ diff --git a/designer-base/src/main/resources/com/fr/design/images/vcs/icon_delete@2x.png b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_delete@2x.png new file mode 100755 index 000000000..b98c9f4cd Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_delete@2x.png differ diff --git a/designer-base/src/main/resources/com/fr/design/images/vcs/icon_edit.png b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_edit.png new file mode 100755 index 000000000..7206039bf Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_edit.png differ diff --git a/designer-base/src/main/resources/com/fr/design/images/vcs/icon_edit@2x.png b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_edit@2x.png new file mode 100755 index 000000000..5993e4389 Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_edit@2x.png differ diff --git a/designer-base/src/main/resources/com/fr/design/images/vcs/icon_filter@1x.png b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_filter@1x.png new file mode 100755 index 000000000..7a8ec6287 Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_filter@1x.png differ diff --git a/designer-base/src/main/resources/com/fr/design/images/vcs/icon_filter@2x.png b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_filter@2x.png new file mode 100755 index 000000000..9b3a132a7 Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_filter@2x.png differ diff --git a/designer-base/src/main/resources/com/fr/design/images/vcs/icon_list_disabled.png b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_list_disabled.png new file mode 100644 index 000000000..5325682ff Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_list_disabled.png differ diff --git a/designer-base/src/main/resources/com/fr/design/images/vcs/icon_list_disabled@2x.png b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_list_disabled@2x.png new file mode 100644 index 000000000..619551cc8 Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_list_disabled@2x.png differ diff --git a/designer-base/src/main/resources/com/fr/design/images/vcs/icon_list_normal@2x.png b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_list_normal@2x.png new file mode 100644 index 000000000..a400bffec Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_list_normal@2x.png differ diff --git a/designer-base/src/main/resources/com/fr/design/images/vcs/icon_revert.png b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_revert.png new file mode 100755 index 000000000..993ba3c21 Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_revert.png differ diff --git a/designer-base/src/main/resources/com/fr/design/images/vcs/icon_revert@2x.png b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_revert@2x.png new file mode 100755 index 000000000..5f26df54c Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_revert@2x.png differ diff --git a/designer-base/src/main/resources/com/fr/design/images/vcs/icon_save_disabled.png b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_save_disabled.png new file mode 100644 index 000000000..3e164a0eb Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_save_disabled.png differ diff --git a/designer-base/src/main/resources/com/fr/design/images/vcs/icon_save_disabled@2x.png b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_save_disabled@2x.png new file mode 100644 index 000000000..70db7bcc4 Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_save_disabled@2x.png differ diff --git a/designer-base/src/main/resources/com/fr/design/images/vcs/icon_save_normal@2x.png b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_save_normal@2x.png new file mode 100644 index 000000000..34e94cb27 Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_save_normal@2x.png differ diff --git a/designer-base/src/main/resources/com/fr/design/images/vcs/icon_user@1x.png b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_user@1x.png new file mode 100755 index 000000000..7af8db572 Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_user@1x.png differ diff --git a/designer-base/src/main/resources/com/fr/design/images/vcs/icon_user@2x.png b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_user@2x.png new file mode 100755 index 000000000..872351544 Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/vcs/icon_user@2x.png differ diff --git a/designer-base/src/main/resources/com/fr/design/images/vcs/vcs_back.png b/designer-base/src/main/resources/com/fr/design/images/vcs/vcs_back.png new file mode 100644 index 000000000..6b3a61b89 Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/vcs/vcs_back.png differ diff --git a/designer-base/src/main/resources/com/fr/design/images/vcs/vcs_list.png b/designer-base/src/main/resources/com/fr/design/images/vcs/vcs_list.png new file mode 100644 index 000000000..df6cd02de Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/vcs/vcs_list.png differ diff --git a/designer-base/src/main/resources/com/fr/design/images/vcs/vcs_save.png b/designer-base/src/main/resources/com/fr/design/images/vcs/vcs_save.png new file mode 100644 index 000000000..6783204cf Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/vcs/vcs_save.png differ diff --git a/designer-base/src/main/resources/com/fr/design/ui/update/push/pushUpdate.css b/designer-base/src/main/resources/com/fr/design/ui/update/push/pushUpdate.css new file mode 100644 index 000000000..add1d3a3a --- /dev/null +++ b/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; +} + diff --git a/designer-base/src/main/resources/com/fr/design/ui/update/push/pushUpdate.js b/designer-base/src/main/resources/com/fr/design/ui/update/push/pushUpdate.js new file mode 100644 index 000000000..1342cfb55 --- /dev/null +++ b/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() + ")"); +}); \ No newline at end of file diff --git a/designer-base/src/test/java/com/fr/design/mainframe/template/info/DesignerOpenHistoryTest.java b/designer-base/src/test/java/com/fr/design/mainframe/template/info/DesignerOpenHistoryTest.java new file mode 100644 index 000000000..e3f63ddc3 --- /dev/null +++ b/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()); + } +} diff --git a/designer-base/src/test/java/com/fr/design/mainframe/template/info/SendHelperTest.java b/designer-base/src/test/java/com/fr/design/mainframe/template/info/SendHelperTest.java new file mode 100644 index 000000000..fb89143d8 --- /dev/null +++ b/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 = "\n" + + "\n" + + "\n" + + ""; + + // 只在调试的时候运行,不需要每次都自动运行 + 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); + } +} diff --git a/designer-base/src/test/java/com/fr/design/mainframe/template/info/TemplateInfoCollectorTest.java b/designer-base/src/test/java/com/fr/design/mainframe/template/info/TemplateInfoCollectorTest.java new file mode 100644 index 000000000..e83a523fc --- /dev/null +++ b/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 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 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 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()); + } +} diff --git a/designer-base/src/test/java/com/fr/design/mainframe/template/info/TemplateInfoTest.java b/designer-base/src/test/java/com/fr/design/mainframe/template/info/TemplateInfoTest.java new file mode 100644 index 000000000..19f6066ff --- /dev/null +++ b/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 = "\n" + + "\n" + + "\n" + + ""; + + private static final String SAVE_AS_INFO = "\n" + + "\n" + + "\n" + + ""; + + 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 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 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); + } +} diff --git a/designer-base/src/test/java/com/fr/design/mainframe/template/info/TemplateInfoTestHelper.java b/designer-base/src/test/java/com/fr/design/mainframe/template/info/TemplateInfoTestHelper.java new file mode 100644 index 000000000..be2b58989 --- /dev/null +++ b/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(); + } +} diff --git a/designer-base/src/test/java/com/fr/design/mainframe/template/info/TimeConsumeTimerTest.java b/designer-base/src/test/java/com/fr/design/mainframe/template/info/TimeConsumeTimerTest.java new file mode 100644 index 000000000..5dcf60e7f --- /dev/null +++ b/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()); + } +} diff --git a/designer-base/src/test/java/com/fr/design/update/push/DesignerPushUpdateConfigManagerTest.java b/designer-base/src/test/java/com/fr/design/update/push/DesignerPushUpdateConfigManagerTest.java new file mode 100644 index 000000000..17831b0b7 --- /dev/null +++ b/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("")); + 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()); + } +} diff --git a/designer-base/src/test/java/com/fr/design/update/push/DesignerPushUpdateDialogTest.java b/designer-base/src/test/java/com/fr/design/update/push/DesignerPushUpdateDialogTest.java new file mode 100644 index 000000000..bc194fe06 --- /dev/null +++ b/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); + } +} diff --git a/designer-base/src/test/java/com/fr/design/update/push/DesignerPushUpdateManagerTest.java b/designer-base/src/test/java/com/fr/design/update/push/DesignerPushUpdateManagerTest.java new file mode 100644 index 000000000..333580f1a --- /dev/null +++ b/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()); + } + +} diff --git a/designer-base/src/test/java/com/fr/design/update/push/DesignerUpdateInfoTest.java b/designer-base/src/test/java/com/fr/design/update/push/DesignerUpdateInfoTest.java new file mode 100644 index 000000000..14f986e8e --- /dev/null +++ b/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 + } + } +} diff --git a/designer-base/src/test/resources/com/fr/design/mainframe/template/info/tpl.info b/designer-base/src/test/resources/com/fr/design/mainframe/template/info/tpl.info new file mode 100644 index 000000000..eae77630f --- /dev/null +++ b/designer-base/src/test/resources/com/fr/design/mainframe/template/info/tpl.info @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/designer-chart/src/main/resources/com/fr/aspectj/designerchart/TemplateProcessTracker.aj b/designer-chart/src/main/resources/com/fr/aspectj/designerchart/TemplateProcessTracker.aj index 89ed91033..a2d2fb7b4 100644 --- a/designer-chart/src/main/resources/com/fr/aspectj/designerchart/TemplateProcessTracker.aj +++ b/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,匹配你需要的方法 diff --git a/designer-form/src/main/java/com/fr/design/designer/creator/XWFitLayout.java b/designer-form/src/main/java/com/fr/design/designer/creator/XWFitLayout.java index c9b55b298..8d3d75a35 100644 --- a/designer-form/src/main/java/com/fr/design/designer/creator/XWFitLayout.java +++ b/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; /** diff --git a/designer-form/src/main/java/com/fr/design/designer/properties/mobile/BodyMobilePropertyUI.java b/designer-form/src/main/java/com/fr/design/designer/properties/mobile/BodyMobilePropertyUI.java index 5175b30dc..401f7d2f2 100644 --- a/designer-form/src/main/java/com/fr/design/designer/properties/mobile/BodyMobilePropertyUI.java +++ b/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); diff --git a/designer-form/src/main/java/com/fr/design/form/util/FormDesignerUtils.java b/designer-form/src/main/java/com/fr/design/form/util/FormDesignerUtils.java new file mode 100644 index 000000000..d976816f5 --- /dev/null +++ b/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; + } + +} \ No newline at end of file diff --git a/designer-form/src/main/java/com/fr/design/mainframe/JForm.java b/designer-form/src/main/java/com/fr/design/mainframe/JForm.java index d25ba6fee..bcf761fac 100644 --- a/designer-form/src/main/java/com/fr/design/mainframe/JForm.java +++ b/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; diff --git a/designer-form/src/main/java/com/fr/design/mainframe/templateinfo/JFormProcessInfo.java b/designer-form/src/main/java/com/fr/design/mainframe/template/info/JFormProcessInfo.java similarity index 95% rename from designer-form/src/main/java/com/fr/design/mainframe/templateinfo/JFormProcessInfo.java rename to designer-form/src/main/java/com/fr/design/mainframe/template/info/JFormProcessInfo.java index 15f501b13..fce5ab809 100644 --- a/designer-form/src/main/java/com/fr/design/mainframe/templateinfo/JFormProcessInfo.java +++ b/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; diff --git a/designer-form/src/main/java/com/fr/design/widget/ui/designer/XmlRelationedBasicPane.java b/designer-form/src/main/java/com/fr/design/widget/ui/designer/XmlRelationedBasicPane.java new file mode 100644 index 000000000..1f9b13693 --- /dev/null +++ b/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; + } +} \ No newline at end of file diff --git a/designer-form/src/main/java/com/fr/design/widget/ui/designer/component/PaddingBoundPane.java b/designer-form/src/main/java/com/fr/design/widget/ui/designer/component/PaddingBoundPane.java index 9b4a432a2..856ff6a0c 100644 --- a/designer-form/src/main/java/com/fr/design/widget/ui/designer/component/PaddingBoundPane.java +++ b/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()); + } } diff --git a/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/BodyMobileDefinePane.java b/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/BodyMobileDefinePane.java index 11c5023ef..b91e1d7b6 100644 --- a/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/BodyMobileDefinePane.java +++ b/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()); + } } } diff --git a/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/ChartEditorDefinePane.java b/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/ChartEditorDefinePane.java index 86df38abc..b9ae12e20 100644 --- a/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/ChartEditorDefinePane.java +++ b/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; } diff --git a/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/MobileWidgetDefinePane.java b/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/MobileWidgetDefinePane.java index ac4de096a..d44707181 100644 --- a/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/MobileWidgetDefinePane.java +++ b/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.*; /** * 所有移动端需要拓展的属性面板均继承此类 - * + *

* 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); + } } diff --git a/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/TabMobileWidgetDefinePane.java b/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/TabMobileWidgetDefinePane.java index f6d94da06..8a795b887 100644 --- a/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/TabMobileWidgetDefinePane.java +++ b/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()); + } } } diff --git a/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/component/MobileComponentAdvancePane.java b/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/component/MobileComponentAdvancePane.java new file mode 100644 index 000000000..f94d57907 --- /dev/null +++ b/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()); + } + } +} \ No newline at end of file diff --git a/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/component/MobileComponentLayoutIntervalPane.java b/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/component/MobileComponentLayoutIntervalPane.java new file mode 100644 index 000000000..5a565373d --- /dev/null +++ b/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()); + } + } +} \ No newline at end of file diff --git a/designer-form/src/main/resources/com/fr/aspectj/designerform/TemplateProcessTracker.aj b/designer-form/src/main/resources/com/fr/aspectj/designerform/TemplateProcessTracker.aj index 3be1a77fa..9daec5009 100644 --- a/designer-form/src/main/resources/com/fr/aspectj/designerform/TemplateProcessTracker.aj +++ b/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,匹配你需要的方法 diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/InformationCollector.java b/designer-realize/src/main/java/com/fr/design/mainframe/InformationCollector.java index 8a3f4bb25..775f402d8 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/InformationCollector.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/InformationCollector.java @@ -7,7 +7,8 @@ import com.fr.base.FRContext; import com.fr.config.MarketConfig; import com.fr.design.DesignerEnvManager; import com.fr.design.mainframe.errorinfo.ErrorInfoUploader; -import com.fr.design.mainframe.templateinfo.TemplateInfoCollector; +import com.fr.design.mainframe.messagecollect.impl.FocusPointMessageUploader; +import com.fr.design.mainframe.template.info.TemplateInfoCollector; import com.fr.general.CloudCenter; import com.fr.general.ComparatorUtils; import com.fr.general.DateUtils; @@ -15,20 +16,13 @@ import com.fr.general.DesUtils; import com.fr.general.GeneralUtils; import com.fr.general.IOUtils; import com.fr.general.http.HttpToolbox; -import com.fr.intelli.record.FocusPoint; -import com.fr.intelli.record.MetricRegistry; import com.fr.json.JSONArray; -import com.fr.json.JSONException; 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.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.XMLPrintWriter; import com.fr.stable.xml.XMLReadable; import com.fr.stable.xml.XMLTools; @@ -53,7 +47,6 @@ import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -81,24 +74,6 @@ public class InformationCollector implements XMLReadable, XMLWriter { private static final String XML_KEY = "ActiveKey"; private static final String XML_OS = "OS"; - public static final String TABLE_NAME = "fr_functionrecord"; - public static final String FUNC_COLUMNNAME = "func"; - public static final String COLUMN_TIME = "time"; - public static final String TABLE_FUNCTION_RECORD = "function.record"; - private static final String ATTR_ID = "id"; - private static final String ATTR_TEXT = "text"; - private static final String ATTR_SOURCE = "source"; - private static final String ATTR_TIME = "time"; - private static final String ATTR_TIMES = "times"; - private static final String ATTR_TITLE = "title"; - private static final String ATTR_USER_NAME = "username"; - private static final String ATTR_UUID = "uuid"; - private static final String ATTR_ITEMS = "items"; - private static final String ATTR_FUNCTION_ARRAY = "functionArray"; - private static final int MAX_EACH_REQUEST_RECORD_COUNT = 200; - private static final int PAGE_SIZE = 200; - private long totalCount = -1; - private static InformationCollector collector; //启动时间与关闭时间列表 @@ -184,6 +159,12 @@ public class InformationCollector implements XMLReadable, XMLWriter { } private void sendUserInfo(){ + long currentTime = new Date().getTime(); + long lastTime = getLastTimeMillis(); + + if (currentTime - lastTime <= DELTA) { + return; + } JSONObject content = getJSONContentAsByte(); String url = CloudCenter.getInstance().acquireUrlByKind("user.info.v10"); boolean success = false; @@ -201,135 +182,9 @@ public class InformationCollector implements XMLReadable, XMLWriter { } } - private void sendFunctionsInfo(long currentTime, long lastTime){ - FineLoggerFactory.getLogger().info("Start sent function records to the cloud center..."); - queryAndSendOnePageFunctionContent(currentTime, lastTime, 0); - long page = (totalCount/PAGE_SIZE) + 1; - for(int i=1; i focusPoints = MetricRegistry.getMetric().find(FocusPoint.class,condition); - //第一次查询获取总记录数 - if(page == 0){ - totalCount = focusPoints.getTotalCount(); - } - sendThisPageFunctionContent(focusPoints); - } catch (Exception e) { - FineLoggerFactory.getLogger().error(e.getMessage(), e); - } - } - - private void sendThisPageFunctionContent(DataList focusPoints) { - String url = CloudCenter.getInstance().acquireUrlByKind(TABLE_FUNCTION_RECORD); - try { - JSONObject jsonObject = dealWithSendFunctionContent(focusPoints); - sendFunctionRecord(url, jsonObject); - } catch (JSONException e) { - FineLoggerFactory.getLogger().error(e.getMessage(), e); - } - } - - private JSONObject dealWithSendFunctionContent(DataList focusPoints) throws JSONException { - JSONObject jsonObject = new JSONObject(); - Map map = new HashMap<>(); - if(!focusPoints.isEmpty()){ - DesignerEnvManager envManager = DesignerEnvManager.getEnvManager(); - String bbsUserName = MarketConfig.getInstance().getBbsUsername(); - String uuid = envManager.getUUID(); - jsonObject.put(ATTR_UUID, uuid); - jsonObject.put(ATTR_USER_NAME, bbsUserName); - for(FocusPoint focusPoint : focusPoints.getList()) { - FunctionRecord functionRecord = getOneRecord(focusPoint); - if (map.containsKey(focusPoint.getId())) { - int times = ((FunctionRecord)map.get(focusPoint.getId())).getTimes() + 1; - functionRecord.setTimes(times); - map.put(focusPoint.getId(), functionRecord); - } else { - map.put(focusPoint.getId(), functionRecord); - } - } - jsonObject.put(ATTR_ITEMS, mapToJSONArray(map)); - } - return jsonObject; - } - - private JSONArray mapToJSONArray(Map map) throws JSONException { - JSONArray jsonArray = new JSONArray(); - for(String keys : map.keySet()){ - FunctionRecord functionRecord = (FunctionRecord)map.get(keys); - JSONObject jo = new JSONObject(); - jo.put(ATTR_ID, functionRecord.getId()); - jo.put(ATTR_TEXT, functionRecord.getText()); - jo.put(ATTR_SOURCE, functionRecord.getSource()); - jo.put(ATTR_TIME, functionRecord.getTime()); - jo.put(ATTR_TITLE, functionRecord.getTitle()); - jo.put(ATTR_TIMES, functionRecord.getTimes()); - jsonArray.put(jo); - } - return jsonArray; - } - - private void sendFunctionRecord(String url, JSONObject record) { - boolean success = false; - try { - HashMap para = new HashMap<>(); - para.put("token", SiteCenterToken.generateToken()); - para.put("content", record); - String res = HttpToolbox.post(url, para); - success = ComparatorUtils.equals(new JSONObject(res).get("status"), "success"); - if (success) { - this.lastTime = dateToString(); - } else { - FineLoggerFactory.getLogger().error("Error occured when sent function records to the cloud center."); - } - } catch (Exception e) { - FineLoggerFactory.getLogger().error(e.getMessage(), e); - } - } - - private FunctionRecord getOneRecord(FocusPoint focusPoint) { - FunctionRecord functionRecord = new FunctionRecord(); - functionRecord.setId(focusPoint.getId() == null?StringUtils.EMPTY : focusPoint.getId()); - functionRecord.setText(focusPoint.getText() == null?StringUtils.EMPTY : focusPoint.getText()); - functionRecord.setSource(focusPoint.getSource()); - functionRecord.setTime(focusPoint.getTime().getTime()); - functionRecord.setTitle(focusPoint.getTitle() == null?StringUtils.EMPTY : focusPoint.getTitle()); - functionRecord.setUsername(MarketConfig.getInstance().getBbsUsername() == null?StringUtils.EMPTY : MarketConfig.getInstance().getBbsUsername()); - functionRecord.setUuid(DesignerEnvManager.getEnvManager().getUUID() == null?StringUtils.EMPTY : DesignerEnvManager.getEnvManager().getUUID()); - return functionRecord; - } - - /** - * 收集开始使用时间,发送信息 - */ + /** + * 收集开始使用时间,发送信息 + */ public void collectStartTime(){ this.current.setStartDate(dateToString()); @@ -345,18 +200,12 @@ public class InformationCollector implements XMLReadable, XMLWriter { service.schedule(new Runnable() { @Override public void run() { - long currentTime = new Date().getTime(); - long lastTime = getLastTimeMillis(); - if (currentTime - lastTime > DELTA) { - sendUserInfo(); - sendFunctionsInfo(currentTime, lastTime); - } - + sendUserInfo(); + FocusPointMessageUploader.getInstance().sendToCloudCenter(); TemplateInfoCollector.getInstance().sendTemplateInfo(); ErrorInfoUploader.getInstance().sendErrorInfo(); } }, SEND_DELAY, TimeUnit.SECONDS); - } /** @@ -524,94 +373,4 @@ public class InformationCollector implements XMLReadable, XMLWriter { } } - - private class FunctionRecord implements Comparable{ - private String id; - private String text; - private int source; - private long time; - private int times = 1; - private String title; - private String username; - private String uuid; - - public FunctionRecord(){ - - } - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public int getTimes() { - return times; - } - - public void setTimes(int times) { - this.times = times; - } - - public String getText() { - return text; - } - - public void setText(String text) { - this.text = text; - } - - public int getSource() { - return source; - } - - public void setSource(int source) { - this.source = source; - } - - public long getTime() { - return time; - } - - public void setTime(long time) { - this.time = time; - } - - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getUuid() { - return uuid; - } - - public void setUuid(String uuid) { - this.uuid = uuid; - } - - @Override - public int compareTo(Object o) { - FunctionRecord functionRecord = (FunctionRecord) o; - if(this.getId().equals((functionRecord.getId())) && this.getText().equals(functionRecord.getText()) - && this.getSource() == functionRecord.getSource() && this.getTime() == functionRecord.getTime() - && this.getTitle().equals(functionRecord.getTitle()) && this.getUsername().equals(functionRecord.getUsername()) - && this.getUuid().equals(functionRecord.getUuid())){ - return 0; - } - return 1; - } - } } \ No newline at end of file diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/JWorkBook.java b/designer-realize/src/main/java/com/fr/design/mainframe/JWorkBook.java index 44becbd9c..07136bc1e 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/JWorkBook.java +++ b/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; diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/AlphaFineDialog.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/AlphaFineDialog.java index c8ecd9303..044a484b1 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/AlphaFineDialog.java +++ b/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(); } } }); diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/errorinfo/ErrorInfo.java b/designer-realize/src/main/java/com/fr/design/mainframe/errorinfo/ErrorInfo.java index 052aaef9f..d22bcf75b 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/errorinfo/ErrorInfo.java +++ b/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); } diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/errorinfo/ErrorInfoLogAppender.java b/designer-realize/src/main/java/com/fr/design/mainframe/errorinfo/ErrorInfoLogAppender.java index 8d4970a86..bcdea3e5a 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/errorinfo/ErrorInfoLogAppender.java +++ b/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"); // 报错信息国际化不规范, 有些是中文分号, 有些是英文 diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/messagecollect/entity/FileEntityBuilder.java b/designer-realize/src/main/java/com/fr/design/mainframe/messagecollect/entity/FileEntityBuilder.java new file mode 100644 index 000000000..2555a2632 --- /dev/null +++ b/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 parameters = new HashMap(); + 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; + } +} diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/messagecollect/impl/AbstractSendDataToCloud.java b/designer-realize/src/main/java/com/fr/design/mainframe/messagecollect/impl/AbstractSendDataToCloud.java new file mode 100644 index 000000000..da98e4778 --- /dev/null +++ b/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 void queryData(long currentTime, long lastTime, Class 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 void queryAndSendOnePageFunctionContent(long current, long last, int page, Class 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 points = MetricRegistry.getMetric().find(tClass, condition); + //第一次查询获取总记录数 + if (page == 0) { + totalCount = points.getTotalCount(); + } + dealWithData(points); + + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + } + + public void dealWithData(DataList tDataList) throws Exception { + generateThisPageFile(tDataList); + } + + private void generateThisPageFile(DataList 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 JSONArray dealWithSendFunctionContent(DataList 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); + } + + +} diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/messagecollect/impl/FocusPointMessageUploader.java b/designer-realize/src/main/java/com/fr/design/mainframe/messagecollect/impl/FocusPointMessageUploader.java new file mode 100644 index 000000000..33adf8e2d --- /dev/null +++ b/designer-realize/src/main/java/com/fr/design/mainframe/messagecollect/impl/FocusPointMessageUploader.java @@ -0,0 +1,107 @@ +package com.fr.design.mainframe.messagecollect.impl; + +import com.fr.config.MarketConfig; +import com.fr.design.DesignerEnvManager; +import com.fr.design.mainframe.messagecollect.entity.FileEntityBuilder; +import com.fr.design.mainframe.messagecollect.utils.MessageCollectUtils; +import com.fr.intelli.record.FocusPoint; +import com.fr.json.JSONArray; +import com.fr.json.JSONObject; +import com.fr.log.FineLoggerFactory; +import com.fr.stable.ProductConstants; +import com.fr.stable.StableUtils; +import com.fr.stable.query.data.DataList; +import com.fr.stable.xml.XMLPrintWriter; +import com.fr.stable.xml.XMLableReader; + +import java.util.Date; +import java.util.UUID; + +/** + * @author alex sung + * @date 2019/3/22 + */ +public class FocusPointMessageUploader extends AbstractSendDataToCloud { + + private static final String TAG = "FocusPointMessageTag"; + private static final String SEPARATOR = "_"; + private static final String FOCUS_POINT = "FocusPoint"; + private static final long DELTA = 24 * 3600 * 1000L; + private static volatile FocusPointMessageUploader instance; + + public static FocusPointMessageUploader getInstance() { + if (instance == null) { + synchronized (FocusPointMessageUploader.class) { + if (instance == null) { + instance = new FocusPointMessageUploader(); + } + } + } + return instance; + } + + @Override + public JSONArray dealWithSendFunctionContent(DataList focusPoints) { + JSONArray ja = new JSONArray(); + for(T t:focusPoints.getList()){ + FocusPoint focusPoint = (FocusPoint)t; + JSONObject jo = new JSONObject(); + jo.put("id",focusPoint.getId()); + jo.put("text",focusPoint.getText()); + jo.put("source",focusPoint.getSource()); + jo.put("time",focusPoint.getTime()); + jo.put("username",focusPoint.getUsername()); + jo.put("ip",focusPoint.getIp()); + jo.put("title",focusPoint.getTitle()); + jo.put("body",focusPoint.getBody()); + ja.put(jo); + } + return ja; + } + + public void sendToCloudCenter() { + MessageCollectUtils.readXMLFile(instance, getLastTimeFile()); + long currentTime = new Date().getTime(); + long lastTime = MessageCollectUtils.getLastTimeMillis(this.lastTime); + if (currentTime - lastTime <= DELTA) { + return; + } + try { + generatePath(); + queryData(currentTime, lastTime, FocusPoint.class); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage()); + } + sendZipFile(getFileEntityBuilder().getFolderName()); + saveLastTime(); + } + + @Override + public void readXML(XMLableReader reader) { + if (reader.isAttr()) { + this.setLastTime(reader.getAttrAsString("focusPointLastTime", null)); + } + } + + @Override + public void writeXML(XMLPrintWriter writer) { + writer.startTAG(TAG); + writer.attr("focusPointLastTime", lastTime); + writer.end(); + } + + private void generatePath() { + DesignerEnvManager envManager = DesignerEnvManager.getEnvManager(); + String bbsUserName = MarketConfig.getInstance().getBbsUsername(); + String uuid = envManager.getUUID(); + //文件夹名称的格式是: "FocusPoint" + 大版本号 + 小版本号 + uuid + bbsUserName + randomUuid,均以下划线分隔 + StringBuilder sb = new StringBuilder(); + sb.append(FOCUS_POINT).append(SEPARATOR). + append(ProductConstants.MAIN_VERSION).append(SEPARATOR). + append(ProductConstants.MINOR_VERSION).append(SEPARATOR). + append(uuid).append(SEPARATOR).append(bbsUserName).append(SEPARATOR). + append(UUID.randomUUID()); + String folderName = StableUtils.pathJoin(ProductConstants.getEnvHome(), sb.toString()); + setFileEntityBuilder(new FileEntityBuilder(folderName)); + } +} diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/messagecollect/utils/MessageCollectUtils.java b/designer-realize/src/main/java/com/fr/design/mainframe/messagecollect/utils/MessageCollectUtils.java new file mode 100644 index 000000000..738942208 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/design/mainframe/messagecollect/utils/MessageCollectUtils.java @@ -0,0 +1,63 @@ +package com.fr.design.mainframe.messagecollect.utils; + +import com.fr.base.FRContext; +import com.fr.general.DateUtils; +import com.fr.general.IOUtils; +import com.fr.log.FineLoggerFactory; +import com.fr.stable.EncodeConstants; +import com.fr.stable.StringUtils; +import com.fr.stable.xml.XMLReadable; +import com.fr.stable.xml.XMLableReader; +import com.fr.third.javax.xml.stream.XMLStreamException; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.text.DateFormat; +import java.util.Date; + +/** + * @author alex sung + * @date 2019/3/26 + */ +public class MessageCollectUtils { + + public static void readXMLFile(XMLReadable xmlReadable, File xmlFile) { + if (xmlFile == null || !xmlFile.exists()) { + return; + } + String charset = EncodeConstants.ENCODING_UTF_8; + try { + InputStream is = new FileInputStream(xmlFile); + String fileContent = IOUtils.inputStream2String(is); + 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 (IOException | XMLStreamException e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + } + + public static long getLastTimeMillis(String lastTime) { + if (StringUtils.isEmpty(lastTime)) { + return 0; + } + try { + return DateUtils.string2Date(lastTime, true).getTime(); + } catch (Exception e) { + return -1; + } + } + + public static String dateToString(){ + DateFormat df = FRContext.getDefaultValues().getDateTimeFormat(); + return df.format(new Date()); + } +} diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/templateinfo/JWorkBookProcessInfo.java b/designer-realize/src/main/java/com/fr/design/mainframe/template/info/JWorkBookProcessInfo.java similarity index 98% rename from designer-realize/src/main/java/com/fr/design/mainframe/templateinfo/JWorkBookProcessInfo.java rename to designer-realize/src/main/java/com/fr/design/mainframe/template/info/JWorkBookProcessInfo.java index 306c6b0d1..eec777328 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/templateinfo/JWorkBookProcessInfo.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/template/info/JWorkBookProcessInfo.java @@ -1,4 +1,4 @@ -package com.fr.design.mainframe.templateinfo; +package com.fr.design.mainframe.template.info; import com.fr.base.parameter.ParameterUI; import com.fr.main.impl.WorkBook; diff --git a/designer-realize/src/main/java/com/fr/start/Designer.java b/designer-realize/src/main/java/com/fr/start/Designer.java index bd872e41a..1d207d330 100644 --- a/designer-realize/src/main/java/com/fr/start/Designer.java +++ b/designer-realize/src/main/java/com/fr/start/Designer.java @@ -11,6 +11,7 @@ import com.fr.design.actions.server.ServerConfigManagerAction; import com.fr.design.actions.server.StyleListAction; import com.fr.design.actions.server.WidgetManagerAction; import com.fr.design.constants.UIConstants; +import com.fr.design.file.HistoryTemplateListCache; import com.fr.design.file.HistoryTemplateListPane; import com.fr.design.file.MutilTempalteTabPane; import com.fr.design.fun.MenuHandler; @@ -80,6 +81,10 @@ public class Designer extends BaseDesigner { private UIButton redo; private UIPreviewButton run; + public Designer(String[] args) { + super(args); + } + /** * 设计器启动的Main方法 * @@ -108,11 +113,6 @@ public class Designer extends BaseDesigner { } - public Designer(String[] args) { - super(args); - } - - /** * 创建新建文件的快捷方式数组。 * @@ -215,7 +215,7 @@ public class Designer extends BaseDesigner { saveButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - JTemplate jt = HistoryTemplateListPane.getInstance().getCurrentEditingTemplate(); + JTemplate jt = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); jt.stopEditing(); jt.saveTemplate(); jt.requestFocus(); @@ -224,6 +224,7 @@ public class Designer extends BaseDesigner { return saveButton; } + private UIButton createUndoButton() { undo = new UIButton(BaseUtils.readIcon("/com/fr/design/images/buttonicon/undo.png")); undo.setToolTipText(KeySetUtils.UNDO.getMenuKeySetName()); diff --git a/designer-realize/src/main/resources/com/fr/aspectj/designer/TemplateProcessTracker.aj b/designer-realize/src/main/resources/com/fr/aspectj/designer/TemplateProcessTracker.aj index 8e4eeb716..2c5433818 100644 --- a/designer-realize/src/main/resources/com/fr/aspectj/designer/TemplateProcessTracker.aj +++ b/designer-realize/src/main/resources/com/fr/aspectj/designer/TemplateProcessTracker.aj @@ -5,13 +5,11 @@ package com.fr.aspectj.designer; * Created by plough on 2017/3/3. */ -import com.fr.design.mainframe.templateinfo.TemplateInfoCollector; import com.fr.grid.Grid; import org.aspectj.lang.reflect.SourceLocation; import java.awt.event.ActionEvent; import java.awt.event.MouseEvent; -import java.util.Date; public aspect TemplateProcessTracker { //声明一个pointcut,匹配你需要的方法