diff --git a/designer-base/src/main/java/com/fr/base/function/ThrowableRunnable.java b/designer-base/src/main/java/com/fr/base/function/ThrowableRunnable.java new file mode 100644 index 000000000..c89ad2115 --- /dev/null +++ b/designer-base/src/main/java/com/fr/base/function/ThrowableRunnable.java @@ -0,0 +1,24 @@ +package com.fr.base.function; + +import com.fr.log.FineLoggerFactory; + +/** + * 可抛出异常的 Runnable + * + * created by Harrison on 2022/05/24 + **/ +public interface ThrowableRunnable { + + void run() throws T; + + static Runnable toRunnable(ThrowableRunnable runnable) { + + return () -> { + try { + runnable.run(); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + }; + } +} 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 7761038b7..8301a0ca3 100644 --- a/designer-base/src/main/java/com/fr/design/DesignerEnvManager.java +++ b/designer-base/src/main/java/com/fr/design/DesignerEnvManager.java @@ -29,6 +29,7 @@ import com.fr.design.style.color.ColorSelectConfigManager; import com.fr.design.update.push.DesignerPushUpdateConfigManager; import com.fr.design.utils.DesignUtils; import com.fr.design.utils.DesignerPort; +import com.fr.env.detect.base.EnvDetectorConfig; import com.fr.exit.DesignerExiter; import com.fr.file.FILEFactory; import com.fr.general.ComparatorUtils; @@ -168,6 +169,12 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter { private boolean embedServerLazyStartup = false; //最近使用的颜色 private ColorSelectConfigManager configManager = new ColorSelectConfigManager(); + + /** + * 环境检测配置 + */ + private EnvDetectorConfig envDetectorConfig = EnvDetectorConfig.getInstance(); + /** * alphafine */ @@ -1820,6 +1827,8 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter { readActiveStatus(reader); } else if (ComparatorUtils.equals(CAS_PARAS, name)) { readHttpsParas(reader); + } else if (name.equals(EnvDetectorConfig.XML_TAG)) { + readEnvDetectorConfig(reader); } else if (name.equals("AlphaFineConfigManager")) { readAlphaFineAttr(reader); } else if (name.equals("RecentColors")) { @@ -1856,6 +1865,10 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter { private void readAlphaFineAttr(XMLableReader reader) { reader.readXMLObject(this.alphaFineConfigManager = AlphaFineConfigManager.getInstance()); } + + private void readEnvDetectorConfig(XMLableReader reader) { + reader.readXMLObject(this.envDetectorConfig); + } private void readHttpsParas(XMLableReader reader) { String tempVal; @@ -2070,6 +2083,7 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter { writeActiveStatus(writer); writeHttpsParas(writer); writeAlphaFineAttr(writer); + writeEnvDetectorConfig(writer); writeRecentColor(writer); writeOpenDebug(writer); writeDesignerPushUpdateAttr(writer); @@ -2113,6 +2127,12 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter { writer.end(); } } + + private void writeEnvDetectorConfig(XMLPrintWriter writer) { + if (this.envDetectorConfig != null) { + this.envDetectorConfig.writeXML(writer); + } + } //写入uuid private void writeUUID(XMLPrintWriter writer) { diff --git a/designer-base/src/main/java/com/fr/design/EnvChangeEntrance.java b/designer-base/src/main/java/com/fr/design/EnvChangeEntrance.java index cd9c835f3..7d2fc6106 100644 --- a/designer-base/src/main/java/com/fr/design/EnvChangeEntrance.java +++ b/designer-base/src/main/java/com/fr/design/EnvChangeEntrance.java @@ -13,6 +13,7 @@ import com.fr.design.env.DesignerWorkspaceType; import com.fr.design.env.RemoteDesignerWorkspaceInfo; import com.fr.design.env.RemoteWorkspace; import com.fr.design.file.HistoryTemplateListCache; +import com.fr.design.file.SaveSomeTemplatePane; import com.fr.design.file.TemplateTreePane; import com.fr.design.i18n.Toolkit; import com.fr.design.mainframe.DesignerContext; @@ -388,6 +389,11 @@ public class EnvChangeEntrance { @Override public void doOk() { + SaveSomeTemplatePane saveSomeTemplatePane = new SaveSomeTemplatePane(true, SwingUtilities.getWindowAncestor(envListPane)); + if (!saveSomeTemplatePane.showSavePane()) { + // 用户取消保存时,取消切换目录操作 + return; + } boolean changeResult = envListOkAction(envListPane, PopTipStrategy.LATER); // 切换完成后清理密码 updateNotRememberPwdEnv(); diff --git a/designer-base/src/main/java/com/fr/design/actions/file/ExitDesignerAction.java b/designer-base/src/main/java/com/fr/design/actions/file/ExitDesignerAction.java index 1ca29a042..5925e250f 100644 --- a/designer-base/src/main/java/com/fr/design/actions/file/ExitDesignerAction.java +++ b/designer-base/src/main/java/com/fr/design/actions/file/ExitDesignerAction.java @@ -3,6 +3,7 @@ */ package com.fr.design.actions.file; +import com.fr.design.file.SaveSomeTemplatePane; import com.fr.design.mainframe.TemplateSavingChecker; import java.awt.event.ActionEvent; @@ -28,9 +29,14 @@ public class ExitDesignerAction extends UpdateAction { * @param e 事件 */ public void actionPerformed(ActionEvent e) { + // 检查是否有正在保存的模板 if (!TemplateSavingChecker.check()) { return; } - DesignerContext.getDesignerFrame().exit(); + // 提示用户保存模板 + SaveSomeTemplatePane saveSomeTemplatePane = new SaveSomeTemplatePane(true); + if (saveSomeTemplatePane.showSavePane()) { + DesignerContext.getDesignerFrame().exit(); + } } -} \ No newline at end of file +} 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 9c12c491c..9f15abee7 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 @@ -9,6 +9,7 @@ import com.fr.design.dialog.BasicPane; import com.fr.design.dialog.DialogActionAdapter; import com.fr.design.dialog.DialogActionListener; import com.fr.design.editor.editor.IntegerEditor; +import com.fr.design.file.SaveSomeTemplatePane; import com.fr.design.gui.frpane.UITabbedPane; import com.fr.design.gui.ibutton.UIButton; import com.fr.design.gui.ibutton.UIColorButton; @@ -889,6 +890,12 @@ public class PreferencePane extends BasicPane { if (!languageChanged) { return; } + // 重启弹窗出现之前提示用户保存模板 + SaveSomeTemplatePane saveSomeTempaltePane = new SaveSomeTemplatePane(true, SwingUtilities.getWindowAncestor(this)); + if (!saveSomeTempaltePane.showSavePane()) { + // 保存失败时,直接返回 + return; + } int rv = JOptionPane.showOptionDialog( null, i18nText("Fine-Design_Basic_Language_Change_Successful"), diff --git a/designer-base/src/main/java/com/fr/design/actions/file/SwitchExistEnv.java b/designer-base/src/main/java/com/fr/design/actions/file/SwitchExistEnv.java index c59a92b97..33761a92e 100644 --- a/designer-base/src/main/java/com/fr/design/actions/file/SwitchExistEnv.java +++ b/designer-base/src/main/java/com/fr/design/actions/file/SwitchExistEnv.java @@ -5,6 +5,7 @@ import com.fr.design.EnvChangeEntrance; import com.fr.design.actions.UpdateAction; import com.fr.design.env.DesignerWorkspaceInfo; import com.fr.design.file.HistoryTemplateListCache; +import com.fr.design.file.SaveSomeTemplatePane; import com.fr.design.mainframe.JTemplate; import com.fr.design.menu.KeySetUtils; import com.fr.design.menu.MenuDef; @@ -66,7 +67,11 @@ public class SwitchExistEnv extends MenuDef { // 打开配置目录面板 EnvChangeEntrance.getInstance().chooseEnv(envName); } else { - EnvChangeEntrance.getInstance().switch2Env(envName); + SaveSomeTemplatePane saveSomeTemplatePane = new SaveSomeTemplatePane(true); + if (saveSomeTemplatePane.showSavePane()) { + // 用户模板保存后,才进行切换目录操作 + EnvChangeEntrance.getInstance().switch2Env(envName); + } } } diff --git a/designer-base/src/main/java/com/fr/design/components/notification/NotificationAction.java b/designer-base/src/main/java/com/fr/design/components/notification/NotificationAction.java new file mode 100644 index 000000000..d2f56976e --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/components/notification/NotificationAction.java @@ -0,0 +1,21 @@ +package com.fr.design.components.notification; + +/** + * created by Harrison on 2022/05/24 + **/ +public interface NotificationAction { + + /** + * 行为名 + * + * @return 名称 + */ + String name(); + + /** + * 行为动作 + * + * @param args 参数 + */ + void run(Object... args); +} diff --git a/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java new file mode 100644 index 000000000..d9a1cf0c2 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java @@ -0,0 +1,391 @@ +package com.fr.design.components.notification; + +import com.fr.base.function.ThrowableRunnable; +import com.fr.base.svg.IconUtils; +import com.fr.design.components.page.PageControlModel; +import com.fr.design.components.page.PageControlPanel; +import com.fr.design.dialog.link.MessageWithLink; +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.i18n.Toolkit; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.layout.VerticalFlowLayout; +import com.fr.design.utils.LinkStrUtils; +import com.fr.env.detect.base.EnvDetectorConfig; + +import javax.swing.BorderFactory; +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.ScrollPaneConstants; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Container; +import java.awt.Desktop; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.net.URI; +import java.util.Arrays; +import java.util.List; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +/** + * 右下角的提醒 异常提醒 + * 相关使用方式见 提醒组件 + * + * created by Harrison on 2022/05/24 + **/ +public class NotificationDialog extends JDialog { + + /** + * 通知框的内部高度 + */ + private static final int CONTENT_INNER_HEIGHT = 60; + /** + * 通知框如果出现滚动条后的内部宽度 + */ + private static final int CONTENT_SCROLL_WIDTH = 280; + + private static final int CONTENT_WIDTH = 300; + private static final int CONTENT_HEIGHT = 100; + /** + * 通知框的外部宽高 + */ + private static final Dimension CONTENT_SIZE = new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT); + private static final Dimension BUTTON_DIMENSION = new Dimension(68, 20); + + /** + * 标记 LABEL, 没有作用 + */ + private static final UILabel SIGN_LABEL = new UILabel("#"); + /** + * 确认一个 LABEL 的宽高 + */ + private static final Dimension SIGN_LABEL_DIMENSION = SIGN_LABEL.getPreferredSize(); + + private NotificationDialogProperties properties; + + /* 数据 model */ + + private List notificationModels; + private PageControlModel pageControlModel; + + private JPanel body; + private JPanel headerPanel; + private JPanel contentPanel; + private JPanel tailPanel; + + public NotificationDialog(NotificationDialogProperties properties, List notificationModels) { + + super(properties.getOwner()); + setTitle(properties.getTitle()); + this.properties = properties; + + this.notificationModels = notificationModels; + this.pageControlModel = new PageControlModel(0, this.notificationModels.size()); + + initComponents(); + } + + public void initComponents() { + + //UI 配置 + configProperties(); + + this.body = FRGUIPaneFactory.createBorderLayout_L_Pane(); + body.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + + //首行 + layoutHeaderPanel(); + + //消息内容 + layoutContentPanel(); + + //查看详情 + layoutTailPanel(); + + add(body); + + Dimension dimension = body.getPreferredSize(); + setSize(dimension.width, dimension.height); + + Container parent = getParent(); + setLocation((parent.getWidth() - dimension.width - 30 + parent.getX()), + parent.getY() + parent.getHeight() - dimension.height - 30); + + } + + public void open() { + + setVisible(true); + } + + private void configProperties() { + + setModal(properties.isModal()); + setFocusable(false); + setAutoRequestFocus(false); + setResizable(false); + } + + protected JPanel createHeaderPanel() { + + return null; + } + + /** + * 内容 + * + * @return 内容面板 + */ + protected JPanel createContentPanel() { + + JPanel contentPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + contentPanel.setBorder(BorderFactory.createEmptyBorder(8, 10, 8, 10)); + contentPanel.setName("contentPanel"); + + NotificationModel model = getCurrentModel(); + + UILabel icon = new UILabel(getIconForType(model.getType())); + icon.setPreferredSize(new Dimension(16, 16)); + JPanel iconPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); + iconPanel.setBorder(BorderFactory.createEmptyBorder(0, 5, 10, 8)); + iconPanel.add(icon, BorderLayout.NORTH); + + contentPanel.add(iconPanel, BorderLayout.WEST); + + JPanel centerPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + centerPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 5)); + + NotificationMessage[] messages = model.getMessages(); + List messageComponents = Arrays.stream(messages) + .map((messageModel) -> { + if (messageModel.getType() == NotificationMessage.Type.LINK) { + NotificationMessage.LinkMessage linkMessage = (NotificationMessage.LinkMessage) messageModel; + return new MessageWithLink(linkMessage.format(), ThrowableRunnable.toRunnable(() -> { + Desktop.getDesktop().browse(URI.create(linkMessage.getLink())); + })); + } + return new UILabel(LinkStrUtils.generateHtmlTag(messageModel.format())); + }) + .collect(Collectors.toList()); + + // 当高度 大于 60 时,就会出现滚动条。 + // 当出现滚动条时,需要将内部的宽度限制为 280, 否则会展示不出来 + Function calStandardWidth = height -> height > CONTENT_INNER_HEIGHT ? CONTENT_SCROLL_WIDTH : CONTENT_WIDTH; + + int widthUnit = messageComponents.stream() + .map((component) -> { + Dimension preferredSize = component.getPreferredSize(); + double width = preferredSize.getWidth(); + double widthFactor = Math.ceil(width / CONTENT_WIDTH); + // 这里的高度是没有限制宽度的,如果限制宽度,高度会变更,所以这里需要加上宽度的影响 + return preferredSize.getHeight() + widthFactor * SIGN_LABEL_DIMENSION.getHeight(); + }) + .reduce(Double::sum) + .map(calStandardWidth) + .orElse(CONTENT_WIDTH); + + messageComponents = messageComponents.stream() + .peek((component) -> { + Dimension preferredSize = component.getPreferredSize(); + double componentWidth = preferredSize.getWidth(); + double componentHeight = preferredSize.getHeight(); + double heightFactor = Math.ceil(componentHeight / SIGN_LABEL_DIMENSION.getHeight()); + double widthFactor = Math.ceil(componentWidth / widthUnit); + int realHeight = (int)Math.ceil(heightFactor + widthFactor - 1) * (int)(Math.ceil(SIGN_LABEL_DIMENSION.getHeight())); + component.setPreferredSize(new Dimension(widthUnit, realHeight)); + }) + .collect(Collectors.toList()); + + // 竖向排列 + JPanel messageSummaryPanel = FRGUIPaneFactory.createVerticalFlowLayout_Pane(true, VerticalFlowLayout.TOP, 0, 0); + messageComponents.forEach(messageSummaryPanel::add); + + JScrollPane jScrollPane = new JScrollPane(messageSummaryPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + jScrollPane.setBorder(BorderFactory.createEmptyBorder()); + + centerPanel.add(jScrollPane, BorderLayout.CENTER); + centerPanel.setPreferredSize(CONTENT_SIZE); + + contentPanel.add(centerPanel, BorderLayout.CENTER); + + return contentPanel; + } + + /** + * 行动 + * + * UI布局 + * /翻页/不再提醒/提醒行为/我知道了 + * + * @return 行动面板 + */ + protected JPanel createTailPanel() { + + JPanel tailPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); + tailPanel.setName("tailPanel"); + + // 翻页按钮效果 + PageControlPanel pageControlPanel = new PageControlPanel(pageControlModel); + + pageControlPanel.actions(new Runnable() { + @Override + public void run() { + pageControlModel = pageControlPanel.performPrevious(); + refresh(); + } + }, new Runnable() { + @Override + public void run() { + pageControlModel = pageControlPanel.performNext(); + refresh(); + } + }); + + tailPanel.add(pageControlPanel, BorderLayout.WEST); + + // 行为效果 + JPanel actionsPanel = FRGUIPaneFactory.createBorderLayout_M_Pane(); + + { + actionsPanel.setBorder(BorderFactory.createEmptyBorder()); + actionsPanel.setName("actionsPanel"); + + UILabel notReminder = new UILabel(); + notReminder.setText(Toolkit.i18nText("Fine-Design_Basic_Not_Reminder")); + notReminder.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + // 配置处理 + EnvDetectorConfig.getInstance().setEnabled(false); + // 点击事件 + destroy(); + } + }); + Color color = new Color(65, 155, 249); + notReminder.setForeground(color); + actionsPanel.add(notReminder, BorderLayout.WEST); + + JPanel buttonPanel = FRGUIPaneFactory.createBorderLayout_M_Pane(); + buttonPanel.setBorder(BorderFactory.createEmptyBorder()); + + // real-action + NotificationModel currentModel = getCurrentModel(); + NotificationAction action = currentModel.getAction(); + if (action != null) { + UIButton actionButton = new UIButton(action.name()); + actionButton.setPreferredSize(BUTTON_DIMENSION); + actionButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + action.run(); + } + }); + buttonPanel.add(actionButton, BorderLayout.WEST); + } + + UIButton knowButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Know")); + knowButton.setPreferredSize(BUTTON_DIMENSION); + knowButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (pageControlModel.isLast()) { + destroy(); + return; + } + pageControlModel = pageControlPanel.performNext(); + refresh(); + } + }); + buttonPanel.add(knowButton, BorderLayout.EAST); + + actionsPanel.add(buttonPanel, BorderLayout.EAST); + } + + tailPanel.add(actionsPanel, BorderLayout.EAST); + + return tailPanel; + } + + private void refresh() { + + layoutContentPanel(); + + layoutTailPanel(); + + this.repaint(); + } + + private void layoutHeaderPanel() { + + this.headerPanel = layoutPanel(this.headerPanel, this::createHeaderPanel, BorderLayout.NORTH); + } + + private void layoutTailPanel() { + + this.tailPanel = layoutPanel(this.tailPanel, this::createTailPanel, BorderLayout.SOUTH); + } + + private void layoutContentPanel() { + + this.contentPanel = layoutPanel(this.contentPanel, this::createContentPanel, BorderLayout.CENTER); + } + + private JPanel layoutPanel(JPanel oldPanel, Supplier supplier, Object constraints){ + + if (oldPanel != null) { + this.body.remove(oldPanel); + } + JPanel newPanel = supplier.get(); + if (newPanel != null) { + this.body.add(newPanel, constraints); + } + return newPanel; + } + + private NotificationModel getCurrentModel() { + + int index = pageControlModel.getIndex(); + return notificationModels.get(index); + } + + protected Icon getIconForType(NotificationType type) { + + String iconPath; + switch (type) { + case ERROR: + iconPath = "/com/fr/design/standard/reminder/reminder_error.svg"; + break; + case INFO: + iconPath = "/com/fr/design/standard/reminder/reminder_success.svg"; + break; + case WARNING: + iconPath = "/com/fr/design/standard/reminder/reminder_warning.svg"; + break; + default: + return null; + } + return IconUtils.readIcon(iconPath); + } + + private void destroy() { + + setVisible(false); + dispose(); + } + + @Override + public void dispose() { + + super.dispose(); + // todo + } +} diff --git a/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialogProperties.java b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialogProperties.java new file mode 100644 index 000000000..5bcff6715 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialogProperties.java @@ -0,0 +1,39 @@ +package com.fr.design.components.notification; + +import java.awt.Frame; + +/** + * 通知会话的属性 + * + * created by Harrison on 2022/05/24 + **/ +public class NotificationDialogProperties { + + private Frame owner; + + private String title; + + private boolean modal; + + public NotificationDialogProperties(Frame owner, String title) { + this.owner = owner; + this.title = title; + this.modal = false; + } + + public void setModal(boolean modal) { + this.modal = modal; + } + + public Frame getOwner() { + return owner; + } + + public String getTitle() { + return title; + } + + public boolean isModal() { + return modal; + } +} diff --git a/designer-base/src/main/java/com/fr/design/components/notification/NotificationMessage.java b/designer-base/src/main/java/com/fr/design/components/notification/NotificationMessage.java new file mode 100644 index 000000000..c107a0fb8 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/components/notification/NotificationMessage.java @@ -0,0 +1,88 @@ +package com.fr.design.components.notification; + +import com.fr.design.utils.LinkStrUtils; + +/** + * created by Harrison on 2022/05/24 + **/ +public interface NotificationMessage { + + /** + * 格式化 + * + * @return 通知信息 + */ + String format(); + + /** + * 类型 + * + * @return 类型 + */ + Type getType(); + + enum Type { + + /** + * 简单型 + */ + SIMPLE, + + /** + * 链接 + */ + LINK + } + + class SimpleMessage implements NotificationMessage { + + private String text; + + public SimpleMessage(String text) { + this.text = text; + } + + @Override + public String format() { + return text; + } + + @Override + public Type getType() { + return Type.SIMPLE; + } + } + + class LinkMessage implements NotificationMessage { + + private String text; + + private String link; + + public LinkMessage(String text, String link) { + + this.text = text; + this.link = link; + } + + public String getText() { + return text; + } + + public String getLink() { + return link; + } + + @Override + public String format() { + + return LinkStrUtils.generateHtmlTag(text); + } + + @Override + public Type getType() { + + return Type.LINK; + } + } +} diff --git a/designer-base/src/main/java/com/fr/design/components/notification/NotificationModel.java b/designer-base/src/main/java/com/fr/design/components/notification/NotificationModel.java new file mode 100644 index 000000000..86d5a9edf --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/components/notification/NotificationModel.java @@ -0,0 +1,39 @@ +package com.fr.design.components.notification; + +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class NotificationModel { + + private final NotificationType type; + private final NotificationMessage[] messages; + + @Nullable + private final NotificationAction action; + + public NotificationModel(NotificationType type, @Nullable NotificationAction action, List messages) { + this(type, action, messages.toArray(new NotificationMessage[0])); + } + + public NotificationModel(NotificationType type, @Nullable NotificationAction action, NotificationMessage... messages) { + + this.type = type; + this.messages = messages; + this.action = action; + } + + public NotificationType getType() { + return type; + } + + public NotificationMessage[] getMessages() { + return messages == null ? new NotificationMessage[0] : messages; + } + + public @Nullable NotificationAction getAction() { + + return action; + } + +} diff --git a/designer-base/src/main/java/com/fr/design/components/notification/NotificationType.java b/designer-base/src/main/java/com/fr/design/components/notification/NotificationType.java new file mode 100644 index 000000000..0f5ab7b34 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/components/notification/NotificationType.java @@ -0,0 +1,16 @@ +package com.fr.design.components.notification; + +/** + * 提醒类型 + * 决定图标种类 + * + * created by Harrison on 2022/05/27 + **/ +public enum NotificationType { + + INFO, + + ERROR, + + WARNING +} diff --git a/designer-base/src/main/java/com/fr/design/components/page/PageControlModel.java b/designer-base/src/main/java/com/fr/design/components/page/PageControlModel.java new file mode 100644 index 000000000..3d024fb05 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/components/page/PageControlModel.java @@ -0,0 +1,78 @@ +package com.fr.design.components.page; + +/** + * created by Harrison on 2022/05/26 + **/ +public class PageControlModel { + + /** + * 当前索引 + * + * = (页数-1) + */ + private int index; + + /** + * 总页数 + */ + private int summary; + + public PageControlModel(int index, int summary) { + this.index = index; + this.summary = summary; + } + + public PageControlModel() { + } + + public PageControlModel previous() { + + this.index--; + return this; + } + + public PageControlModel next() { + + this.index++; + return this; + } + + /** + * 页数 + * index+1 + * + * @return 页数 + */ + public int getNumber() { + return index + 1; + } + + public boolean isFirst() { + return getNumber() == 1; + } + + public boolean isLast() { + return getNumber() == getSummary(); + } + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } + + public int getSummary() { + return summary; + } + + public void setSummary(int summary) { + this.summary = summary; + } + + public String toContent() { + + return getNumber() + "/" + this.summary; + } +} diff --git a/designer-base/src/main/java/com/fr/design/components/page/PageControlPanel.java b/designer-base/src/main/java/com/fr/design/components/page/PageControlPanel.java new file mode 100644 index 000000000..cc8502889 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/components/page/PageControlPanel.java @@ -0,0 +1,151 @@ +package com.fr.design.components.page; + +import com.fr.base.svg.IconUtils; +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.gui.ilable.UILabel; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.function.Function; + +/** + * 翻页组件 + * 相关使用方式见 翻页组件 + * + * created by Harrison on 2022/05/26 + **/ +public class PageControlPanel extends JPanel { + + private static final long serialVersionUID = 8140501834691131305L; + + private static final Dimension PAGE_CONTROL_BUTTON_DIMENSION = new Dimension(20, 20); + + private UIButton previous; + private Runnable previousAction; + + private UILabel content; + private PageControlModel model; + + private UIButton next; + private Runnable nextAction; + + public PageControlPanel(PageControlModel model) { + + this.model = model; + setBorder(BorderFactory.createEmptyBorder()); + setLayout(new BorderLayout(6, 0)); + + this.previous = new UIButton(); + previous.setPreferredSize(PAGE_CONTROL_BUTTON_DIMENSION); + previous.setIcon(IconUtils.readIcon("/com/fr/design/standard/arrow/arrow_enable_left.svg")); + previous.setDisabledIcon(IconUtils.readIcon("/com/fr/design/standard/arrow/arrow_disable_left.svg")); + previous.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + performAction(previousAction); + } + }); + + this.add(previous, BorderLayout.WEST); + + this.content = new UILabel(model.toContent()); + this.add(content, BorderLayout.CENTER); + + next = new UIButton(); + next.setPreferredSize(PAGE_CONTROL_BUTTON_DIMENSION); + next.setIcon(IconUtils.readIcon("/com/fr/design/standard/arrow/arrow_enable_right.svg")); + next.setDisabledIcon(IconUtils.readIcon("/com/fr/design/standard/arrow/arrow_disable_right.svg")); + next.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + performAction(nextAction); + } + }); + this.add(next, BorderLayout.EAST); + + refresh(); + } + + public PageControlModel performPrevious() { + + update(PageControlModel::previous); + return this.model; + } + + public PageControlModel performNext() { + + update(PageControlModel::next); + return this.model; + } + + public PageControlModel getModel() { + + return this.model; + } + + public void update(PageControlModel model) { + + this.model.setIndex(model.getIndex()); + this.model.setSummary(model.getSummary()); + refresh(); + } + + public void update(Function updateAction) { + + PageControlModel newModel = updateAction.apply(this.model); + update(newModel); + refresh(); + } + + public void refresh() { + + this.content.setText(this.model.toContent()); + this.content.repaint(); + + this.previous.setEnabled(true); + this.next.setEnabled(true); + if (model.getNumber() == 1) { + // 禁用上一个 + disableButton(this.previous); + // 禁用next + if (model.getNumber() == model.getSummary()) { + disableButton(this.next); + } + return; + } + + // 禁用next + if (model.getNumber() == model.getSummary()) { + disableButton(this.next); + } + } + + private void enable(UIButton button) { + + button.setEnabled(true); + } + + private void disableButton(UIButton button) { + + button.setEnabled(false); + } + + private void performAction(Runnable action) { + + if (action != null) { + action.run(); + refresh(); + } + } + + public void actions(Runnable previousAction, Runnable nextAction) { + + this.previousAction = previousAction; + this.nextAction = nextAction; + } + +} diff --git a/designer-base/src/main/java/com/fr/design/components/table/TablePanel.java b/designer-base/src/main/java/com/fr/design/components/table/TablePanel.java new file mode 100644 index 000000000..317a17195 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/components/table/TablePanel.java @@ -0,0 +1,184 @@ +package com.fr.design.components.table; + +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.utils.ColorUtils; +import com.fr.third.org.apache.commons.lang3.ArrayUtils; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import javax.swing.JSeparator; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.GridLayout; + +/** + * 表头 + * 内容 + * + * 适用于需要一个表格的 Panel + * + * created by Harrison on 2022/05/26 + **/ +public class TablePanel extends JPanel { + + private static final Color DEFAULT_HEADER_COLOR = new Color(232, 232, 233); + + private static final Color DEFAULT_ODD_ROW_COLOR = new Color(245, 245, 247); + + private static final Color DEFAULT_EVEN_ROW_COLOR = Color.WHITE; + + private JPanel headerPanel; + + private JPanel[] headerItemPanels; + + private JPanel contentPanel; + + private JPanel[][] cellPanels; + + public TablePanel(int row, int column) { + + setLayout(FRGUIPaneFactory.createBorderLayout()); + + /* header 部分 */ + + this.headerPanel = new JPanel(); + headerPanel.setLayout(FRGUIPaneFactory.createNColumnGridLayout(column)); + headerPanel.setName("header-panel"); + headerPanel.setPreferredSize(new Dimension(640, 24)); + + // border + headerPanel.setBorder(BorderFactory.createLineBorder(new Color(218, 218, 221))); + syncHeaderColor(headerPanel); + + headerItemPanels = new JPanel[column]; + for (int i = 0; i < column; i++) { + JPanel headerItemWrapper = FRGUIPaneFactory.createBorderLayout_S_Pane(); + syncHeaderColor(headerItemWrapper); + headerItemWrapper.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10)); + + JPanel headerItemPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + headerItemPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0)); + headerItemPanels[i] = headerItemPanel; + + UILabel label = new UILabel(); + syncHeaderColor(label); + + headerItemPanel.add(new UILabel(), BorderLayout.CENTER); + + headerItemWrapper.add(headerItemPanel, BorderLayout.WEST); + if (i != column - 1) { + JSeparator separator = new JSeparator(JSeparator.VERTICAL); + separator.setBackground(new Color(218, 218, 221)); + headerItemWrapper.add(separator, BorderLayout.EAST); + } + headerPanel.add(headerItemWrapper); + } + + /* content 部分 */ + + contentPanel = new JPanel(); + + contentPanel.setLayout(new GridLayout(row, 1)); + contentPanel.setBorder(BorderFactory.createLineBorder(new Color(218, 218, 221))); + + cellPanels = new JPanel[row][column]; + for (int i = 0; i < row; i++) { + + JPanel rowPanel = new JPanel(); + // 获取行号 + Color rowColor = getRowColorByRowNumber(i + 1); + rowPanel.setBackground(rowColor); + rowPanel.setLayout(FRGUIPaneFactory.createNColumnGridLayout(column)); + rowPanel.setName("row-" + i); + rowPanel.setBorder(BorderFactory.createEmptyBorder()); + rowPanel.setPreferredSize(new Dimension(640, 24)); + + for (int j = 0; j < column; j++) { + + JPanel rowItemPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + rowItemPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0)); + rowItemPanel.setName("rowItemPanel-"+ i + "-" + j); + final UILabel empty = new UILabel(); + empty.setPreferredSize(new Dimension(210, 24)); + rowItemPanel.setBackground(rowPanel.getBackground()); + rowItemPanel.add(empty, BorderLayout.CENTER); + + rowPanel.add(rowItemPanel); + cellPanels[i][j] = rowItemPanel; + } + + contentPanel.add(rowPanel); + } + + add(headerPanel, BorderLayout.NORTH); + add(contentPanel, BorderLayout.SOUTH); + } + + /** + * 获取行的颜色 + * + * @param row 行号 + * @return 颜色 + */ + private Color getRowColorByRowNumber(int row) { + + Color rowColor; + if (row % 2 != 0) { + rowColor = DEFAULT_EVEN_ROW_COLOR; + } else { + rowColor = DEFAULT_ODD_ROW_COLOR; + } + return rowColor; + } + + public void updateHeaders(String[] headers) { + + for (int i = 0; i < headers.length; i++) { + String header = headers[i]; + UILabel headerContent = new UILabel(header); + JPanel headerItemPanel = headerItemPanels[i]; + if (ArrayUtils.getLength(headerItemPanel.getComponents()) == 1) { + headerItemPanel.remove(0); + } + headerItemPanel.add(headerContent); + syncHeaderColor(headerItemPanel); + } + } + + public void updateCell(int row, int column, Component component) { + + int x = row - 1; + int y = column - 1; + + syncCellColor(row, component); + + JPanel cellPanel = this.cellPanels[x][y]; + if (ArrayUtils.getLength(cellPanel.getComponents()) == 1) { + cellPanel.remove(0); + } + cellPanel.add(component); + } + + public void updateCell(int row, int column, String value) { + + UILabel cellContent = new UILabel(value); + syncCellColor(row, cellContent); + this.updateCell(row, column, cellContent); + } + + private void syncHeaderColor(Component component) { + + ColorUtils.syncBackground(component, DEFAULT_HEADER_COLOR); + } + + private void syncCellColor(int row, Component component) { + + Color rowColor = getRowColorByRowNumber(row); + ColorUtils.syncBackground(component, rowColor); + component.setBackground(rowColor); + } + +} diff --git a/designer-base/src/main/java/com/fr/design/constants/DesignerColor.java b/designer-base/src/main/java/com/fr/design/constants/DesignerColor.java new file mode 100644 index 000000000..0ece84cca --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/constants/DesignerColor.java @@ -0,0 +1,24 @@ +package com.fr.design.constants; + +import java.awt.Color; + +/** + * 见 设计器规范 + * 将相关的逻辑抽象过来 + * 如果后面更改的话, 可以统一修改 + * 如果换版本,可以换成 v2 这种类推 + * + * created by Harrison on 2022/05/26 + **/ +public interface DesignerColor { + + interface Button { + + interface Primary { + + Color PRESSED = new Color(29, 122, 220); + Color HOVER = new Color(84, 165, 249); + Color NORMAL = new Color(65, 155, 249); + } + } +} diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/SummaryMethodComboBox.java b/designer-base/src/main/java/com/fr/design/data/datapane/SummaryMethodComboBox.java index 7aee00e24..5e4cc2cd4 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/SummaryMethodComboBox.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/SummaryMethodComboBox.java @@ -40,10 +40,13 @@ public class SummaryMethodComboBox extends UIComboBox { * 更新公式选择. */ public void populateBean(AbstractDataFunction function) { - for (int i = 0; i < CLASS_ARRAY.length; i++) { - if (function != null && ComparatorUtils.equals(function.getClass(), CLASS_ARRAY[i])) { - setSelectedIndex(i); - break; + if (function != null) { + for (int i = 0; i < CLASS_ARRAY.length; i++) { + if (this.getModel() != null && this.getModel().getSize() > i + && ComparatorUtils.equals(function.getClass(), CLASS_ARRAY[i])) { + setSelectedIndex(i); + break; + } } } } diff --git a/designer-base/src/main/java/com/fr/design/dialog/FineJOptionPane.java b/designer-base/src/main/java/com/fr/design/dialog/FineJOptionPane.java index c64455079..4e0b4e520 100644 --- a/designer-base/src/main/java/com/fr/design/dialog/FineJOptionPane.java +++ b/designer-base/src/main/java/com/fr/design/dialog/FineJOptionPane.java @@ -8,6 +8,7 @@ import javax.swing.JDialog; import javax.swing.JOptionPane; import java.awt.Component; import java.awt.HeadlessException; +import java.awt.event.ActionListener; import java.util.HashMap; import java.util.Map; @@ -184,6 +185,29 @@ public class FineJOptionPane extends JOptionPane { messageType, icon, options, initialValue); } + /** + * 带确认+取消按钮的弹出框,并绑定确认事件 + * + * @param parentComponent 父容器 + * @param message 具体的提示消息 + * @param title 标题 + * @param icon 图标 + * @param initialValue 初始选项 + * @throws HeadlessException + */ + public static int showConfirmDialogWithOkListener(Component parentComponent, Object message, + String title, Icon icon, Object initialValue, ActionListener listener) + throws HeadlessException { + int val = showOptionDialog(parentComponent, message, title, JOptionPane.DEFAULT_OPTION, + FineJOptionPane.WARNING_MESSAGE, icon, OPTION_OK_CANCEL, initialValue); + if (val == JOptionPane.OK_OPTION && listener!=null) { + listener.actionPerformed(null); + } + return val; + } + + + /** * 指定消息内容的输入弹出框 * @param message 消息内容 diff --git a/designer-base/src/main/java/com/fr/design/dialog/link/MessageWithLink.java b/designer-base/src/main/java/com/fr/design/dialog/link/MessageWithLink.java index 2e839d27c..d412fb682 100644 --- a/designer-base/src/main/java/com/fr/design/dialog/link/MessageWithLink.java +++ b/designer-base/src/main/java/com/fr/design/dialog/link/MessageWithLink.java @@ -1,16 +1,18 @@ package com.fr.design.dialog.link; -import com.fr.design.gui.ilable.UILabel; +import com.fr.design.utils.LinkStrUtils; import com.fr.log.FineLoggerFactory; import com.fr.stable.StringUtils; import javax.swing.JEditorPane; import javax.swing.event.HyperlinkEvent; -import javax.swing.event.HyperlinkListener; import java.awt.Color; import java.awt.Desktop; import java.awt.Font; import java.net.URI; +import java.net.URL; + +import static com.fr.design.utils.LinkStrUtils.LABEL; /** * 用来构建JOptionPane带超链的消息提示 @@ -20,9 +22,28 @@ import java.net.URI; * Created by hades on 2020/10/23 */ public class MessageWithLink extends JEditorPane { + + /** + * 直接放入 html 内容 + * 如果有超链接标签, 如 的话,将会自动点击匹配 url + * + * @param htmlText html内容 + */ + public MessageWithLink(String htmlText) { + + super("text/html", htmlText); + initListener(); + setEditable(false); + setBorder(null); + } - private static final UILabel LABEL = new UILabel(); - + public MessageWithLink(String htmlText, Runnable action) { + + super("text/html", htmlText); + initListener(action); + setEditable(false); + setBorder(null); + } public MessageWithLink(String message, String linkName, String link) { this(message, linkName, link, LABEL.getBackground(), LABEL.getFont()); @@ -49,36 +70,45 @@ public class MessageWithLink extends JEditorPane { } public MessageWithLink(String frontMessage, String linkName, String link, String backMessage, Color backgroundColor, Font font, Color fontColor) { - super("text/html", "" + frontMessage + "" + linkName + "" + backMessage + ""); + + super("text/html", "" + frontMessage + "" + linkName + "" + backMessage + ""); initListener(link); setEditable(false); setBorder(null); } - - protected void initListener(String link) { - addHyperlinkListener(new HyperlinkListener() { - @Override - public void hyperlinkUpdate(HyperlinkEvent e) { - if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) { - try { - Desktop.getDesktop().browse(URI.create(link)); - } catch (Exception exception) { - FineLoggerFactory.getLogger().error(exception.getMessage(), exception); - } + + protected void initListener() { + + addHyperlinkListener(hyperlinkEvent -> { + try { + if (hyperlinkEvent.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) { + URL url = hyperlinkEvent.getURL(); + Desktop.getDesktop().browse(url.toURI()); } + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + }); + } + + protected void initListener(Runnable runnable) { + + addHyperlinkListener(e -> { + if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) { + runnable.run(); } }); } - private static StringBuilder generateStyle(Color backgroundColor, Font font, Color fontColor) { - // 构建相同风格样式 - StringBuilder style = new StringBuilder("font-family:" + font.getFamily() + ";"); - style.append("font-weight:").append(font.isBold() ? "bold" : "normal").append(";"); - style.append("font-size:").append(font.getSize()).append("pt;"); - style.append("color:rgb(").append(fontColor.getRed()).append(",").append(fontColor.getGreen()).append(",").append(fontColor.getBlue()).append(");"); - style.append("background-color: rgb(").append(backgroundColor.getRed()).append(",").append(backgroundColor.getGreen()).append(",").append(backgroundColor.getBlue()).append(");"); - - return style; + protected void initListener(String link) { + + initListener(() -> { + try { + Desktop.getDesktop().browse(URI.create(link)); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + }); } } diff --git a/designer-base/src/main/java/com/fr/design/extra/exe/ReadUpdateOnlineExecutor.java b/designer-base/src/main/java/com/fr/design/extra/exe/ReadUpdateOnlineExecutor.java index 739b84666..4656d8d39 100644 --- a/designer-base/src/main/java/com/fr/design/extra/exe/ReadUpdateOnlineExecutor.java +++ b/designer-base/src/main/java/com/fr/design/extra/exe/ReadUpdateOnlineExecutor.java @@ -1,15 +1,24 @@ package com.fr.design.extra.exe; +import com.fr.design.dialog.FineJOptionPane; import com.fr.design.extra.PluginsReaderFromStore; import com.fr.design.extra.Process; +import com.fr.design.i18n.Toolkit; +import com.fr.design.utils.DesignUtils; +import com.fr.general.SiteBlockedException; import com.fr.json.JSONArray; import com.fr.json.JSONObject; import com.fr.log.FineLoggerFactory; import com.fr.plugin.view.PluginView; import com.fr.stable.StringUtils; +import javax.swing.JOptionPane; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.util.List; +import static com.fr.design.dialog.FineJOptionPane.OPTION_OK_CANCEL; + /** * Created by vito on 16/4/19. */ @@ -41,6 +50,19 @@ public class ReadUpdateOnlineExecutor implements Executor { jsonArray.put(jsonObject); } result = jsonArray.toString(); + } catch (SiteBlockedException e1) { + FineJOptionPane.showConfirmDialogWithOkListener(null, + e1.getMessage(), + Toolkit.i18nText("Fine-Design_Basic_Alert"), null, null, + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + DesignUtils.visitEnvServerByParameters( + "#management/system/normal", + null, + null); + } + }); } catch (Exception e) { FineLoggerFactory.getLogger().error(e.getMessage(), e); } diff --git a/designer-base/src/main/java/com/fr/design/file/HistoryTemplateListCache.java b/designer-base/src/main/java/com/fr/design/file/HistoryTemplateListCache.java index d6b12efa6..cb38ea8bd 100644 --- a/designer-base/src/main/java/com/fr/design/file/HistoryTemplateListCache.java +++ b/designer-base/src/main/java/com/fr/design/file/HistoryTemplateListCache.java @@ -353,7 +353,7 @@ public class HistoryTemplateListCache implements CallbackEvent { int size = historyList.size(); for (int i = 0; i < size; i++) { JTemplate template = historyList.get(i); - FILE file = template.templateToStashFile(); + FILE file = template.templateToStashFile4Envchange(); if (file != null) { stashFILEMap.put(i, file); } @@ -382,7 +382,8 @@ public class HistoryTemplateListCache implements CallbackEvent { } FineLoggerFactory.getLogger().info("{} is being reloaded", stashedFile.getName()); JTemplate template = historyList.get(i); - template.refreshResource(stashedFile); + // 切换环境后,刷新资源并且将暂存的FILE替换到template中 + template.refreshResourceAndEditingFILE(stashedFile); } catch (Exception e) { FineLoggerFactory.getLogger().error(e.getMessage(), e); } diff --git a/designer-base/src/main/java/com/fr/design/file/SaveSomeTemplatePane.java b/designer-base/src/main/java/com/fr/design/file/SaveSomeTemplatePane.java index 7fef2e379..72945b31c 100644 --- a/designer-base/src/main/java/com/fr/design/file/SaveSomeTemplatePane.java +++ b/designer-base/src/main/java/com/fr/design/file/SaveSomeTemplatePane.java @@ -17,7 +17,6 @@ import com.fr.design.mainframe.JTemplate; import com.fr.general.ComparatorUtils; import com.fr.log.FineLoggerFactory; -import com.fr.stable.ProductConstants; import javax.swing.*; import javax.swing.border.EmptyBorder; @@ -43,17 +42,27 @@ public class SaveSomeTemplatePane extends BasicPane { private boolean isJudgeCurrentEditingTemplate = true; public SaveSomeTemplatePane(boolean isNeedTojudgeCurrent) { + this(isNeedTojudgeCurrent, DesignerContext.getDesignerFrame()); + } + + /** + * 支持自定义设置 dialog的父窗口 + * @param isNeedTojudgeCurrent + * @param parent + */ + public SaveSomeTemplatePane(boolean isNeedTojudgeCurrent, Window parent) { this.setLayout(FRGUIPaneFactory.createBorderLayout()); if (this.dialog == null) { - this.dialog = this.showSmallWindow(DesignerContext.getDesignerFrame(), new DialogActionAdapter() { + this.dialog = this.showSmallWindow(parent, new DialogActionAdapter() { @Override public void doOk() { + isAllSaved = true; for (int i = 0; i < templateCheckBoxes.length; i++) { if (templateCheckBoxes[i].isSelected()) { - saveSelectedTemplate(unSavedTemplate.get(i)); + // 当存在模板保存失败时,视为整体的isAllSaved失败 + isAllSaved = saveSelectedTemplate(unSavedTemplate.get(i)) && isAllSaved; } } - isAllSaved = true; } public void doCancel() { @@ -180,31 +189,15 @@ public class SaveSomeTemplatePane extends BasicPane { } - private void saveSelectedTemplate(JTemplate specifiedTemplate) { + private boolean saveSelectedTemplate(JTemplate specifiedTemplate) { if (!specifiedTemplate.isSaved()) { specifiedTemplate.stopEditing(); - specifiedTemplate.saveTemplate(); + return specifiedTemplate.saveTemplate(); } FineLoggerFactory.getLogger().info( com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Template_Already_Saved", specifiedTemplate.getEditingFILE().getName())); + return true; } - - public int saveLastOneTemplate() { - JTemplate specifiedTemplate = HistoryTemplateListPane.getInstance().getCurrentEditingTemplate(); - if (!specifiedTemplate.isALLSaved()) { - specifiedTemplate.stopEditing(); - int returnVal = FineJOptionPane.showConfirmDialog(DesignerContext.getDesignerFrame(), com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Utils_Would_You_Like_To_Save") + " \"" + specifiedTemplate.getEditingFILE() + "\" ?", - com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Confirm"), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); - if (returnVal == JOptionPane.YES_OPTION) { - specifiedTemplate.saveTemplate(); - FineLoggerFactory.getLogger().info(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Template_Already_Saved", specifiedTemplate.getEditingFILE().getName())); - } - return returnVal; - } - return JOptionPane.YES_OPTION; - } - - protected String title4PopupWindow() { return com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Save"); } diff --git a/designer-base/src/main/java/com/fr/design/gui/frpane/TreeSettingPane.java b/designer-base/src/main/java/com/fr/design/gui/frpane/TreeSettingPane.java index 77d3aa583..b187a8bfa 100644 --- a/designer-base/src/main/java/com/fr/design/gui/frpane/TreeSettingPane.java +++ b/designer-base/src/main/java/com/fr/design/gui/frpane/TreeSettingPane.java @@ -62,12 +62,8 @@ public class TreeSettingPane extends BasicPane implements DataCreatorUI { UILabel buildWayLabel = new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Build_Way") + " :"); buildWayPanel.add(buildWayLabel); buildBox = new UIComboBox(buildWay); - buildBox.addItemListener(new ItemListener() { - - @Override - public void itemStateChanged(ItemEvent e) { - cardChanged(buildBox.getSelectedIndex()); - } + buildBox.addItemListener(e -> { + cardChanged(buildBox.getSelectedIndex()); }); buildWayPanel.add(buildBox); @@ -79,7 +75,15 @@ public class TreeSettingPane extends BasicPane implements DataCreatorUI { cardChanged(0); } - private void cardChanged(int index) { + @Override + public void checkValid() throws Exception { + doBuildBoxSelect(buildBox.getSelectedIndex()); + } + + private void doBuildBoxSelect(Integer selectedIndex) { + } + + private void cardChanged(int index) { this.remove(controlPane); this.remove(autoBuildPane); diff --git a/designer-base/src/main/java/com/fr/design/gui/ilable/ActionLabel.java b/designer-base/src/main/java/com/fr/design/gui/ilable/ActionLabel.java index ebccd24f4..85d73515e 100644 --- a/designer-base/src/main/java/com/fr/design/gui/ilable/ActionLabel.java +++ b/designer-base/src/main/java/com/fr/design/gui/ilable/ActionLabel.java @@ -13,11 +13,16 @@ import java.awt.event.MouseEvent; */ public class ActionLabel extends UILabel { private ActionListener actionListener; + private Color color; public ActionLabel(String text) { - super(text); + this(text, Color.blue); + } - this.setForeground(Color.blue); + public ActionLabel(String text, Color color) { + super(text); + this.color = color; + this.setForeground(color); this.addMouseListener(mouseInputAdapter); this.addMouseMotionListener(mouseInputAdapter); } @@ -33,7 +38,7 @@ public class ActionLabel extends UILabel { public void paintComponent(Graphics _gfx) { super.paintComponent(_gfx); - _gfx.setColor(Color.blue); + _gfx.setColor(this.color); _gfx.drawLine(0, this.getHeight() - 1, this.getWidth(), this.getHeight() - 1); } diff --git a/designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/TreeRootPane.java b/designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/TreeRootPane.java index bb754716f..a848b06d6 100644 --- a/designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/TreeRootPane.java +++ b/designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/TreeRootPane.java @@ -8,75 +8,89 @@ import com.fr.design.layout.FRGUIPaneFactory; import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.JPanel; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; public class TreeRootPane extends BasicPane { - - // 是否支持多选(checkBoxTree) - //private JCheckBox multipleSelection; - private UICheckBox checkTypeCheckBox; - - // richer:加载的方式,支持异步加载和完全加载 - private UICheckBox loadTypeCheckBox; - - private UICheckBox layerTypeCheckBox; - - private UICheckBox returnFullPathCheckBox ; - - public TreeRootPane() { - this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); - - JPanel checkTypePane = FRGUIPaneFactory.createBoxFlowInnerContainer_S_Pane_First0(); - checkTypePane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - checkTypeCheckBox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Tree_Mutiple_Selection_Or_Not")); - checkTypeCheckBox.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - - checkTypePane.add(checkTypeCheckBox); - this.add(checkTypePane); - - JPanel loadTypePane = FRGUIPaneFactory.createBoxFlowInnerContainer_S_Pane_First0(); - checkTypePane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - loadTypeCheckBox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Widget_Load_By_Async")); - loadTypeCheckBox.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - - loadTypePane.add(loadTypeCheckBox); - this.add(loadTypePane); - - JPanel leafSelectPane = FRGUIPaneFactory.createBoxFlowInnerContainer_S_Pane_First0(); - checkTypePane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - leafSelectPane.add(layerTypeCheckBox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Tree_Select_Leaf_Only"))); - layerTypeCheckBox.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - - this.add(leafSelectPane); + + // 是否支持多选(checkBoxTree) + //private JCheckBox multipleSelection; + private UICheckBox checkTypeCheckBox; + + // richer:加载的方式,支持异步加载和完全加载 + private UICheckBox loadTypeCheckBox; + + private UICheckBox layerTypeCheckBox; + + private UICheckBox returnFullPathCheckBox; + + public TreeRootPane() { + this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + + JPanel checkTypePane = FRGUIPaneFactory.createBoxFlowInnerContainer_S_Pane_First0(); + checkTypePane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + checkTypeCheckBox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Tree_Mutiple_Selection_Or_Not")); + checkTypeCheckBox.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + + checkTypePane.add(checkTypeCheckBox); + this.add(checkTypePane); + + JPanel loadTypePane = FRGUIPaneFactory.createBoxFlowInnerContainer_S_Pane_First0(); + checkTypePane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + loadTypeCheckBox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Widget_Load_By_Async")); + loadTypeCheckBox.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + loadTypeCheckBox.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + UICheckBox checkBox = (UICheckBox) e.getSource(); + doLoadTypeChange(checkBox.isSelected()); + } + }); + + loadTypePane.add(loadTypeCheckBox); + this.add(loadTypePane); + + JPanel leafSelectPane = FRGUIPaneFactory.createBoxFlowInnerContainer_S_Pane_First0(); + checkTypePane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + leafSelectPane.add(layerTypeCheckBox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Tree_Select_Leaf_Only"))); + layerTypeCheckBox.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + + this.add(leafSelectPane); JPanel returnFullPathPane = FRGUIPaneFactory.createBoxFlowInnerContainer_S_Pane_First0(); - checkTypePane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - returnFullPathPane.add(returnFullPathCheckBox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Tree_Return_Full_Path"))); - returnFullPathCheckBox.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - - this.add(returnFullPathPane); - - } - - @Override - protected String title4PopupWindow() { - return "tree"; - } - - public void populate(TreeAttr treeAttr) { - checkTypeCheckBox.setSelected(treeAttr.isMultipleSelection()); - loadTypeCheckBox.setSelected(treeAttr.isAjax()); - layerTypeCheckBox.setSelected(treeAttr.isSelectLeafOnly()); + checkTypePane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + returnFullPathPane.add(returnFullPathCheckBox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Tree_Return_Full_Path"))); + returnFullPathCheckBox.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + + this.add(returnFullPathPane); + + } + + private void doLoadTypeChange(Boolean selected) { + //给埋点插件提供一个方法,埋埋点用 + } + + @Override + protected String title4PopupWindow() { + return "tree"; + } + + public void populate(TreeAttr treeAttr) { + checkTypeCheckBox.setSelected(treeAttr.isMultipleSelection()); + loadTypeCheckBox.setSelected(treeAttr.isAjax()); + layerTypeCheckBox.setSelected(treeAttr.isSelectLeafOnly()); returnFullPathCheckBox.setSelected(treeAttr.isReturnFullPath()); - } + } - public TreeAttr update() { - TreeAttr treeAttr = new TreeAttr(); - treeAttr.setMultipleSelection(checkTypeCheckBox.isSelected()); - treeAttr.setAjax(loadTypeCheckBox.isSelected()); - treeAttr.setSelectLeafOnly(layerTypeCheckBox.isSelected()); + public TreeAttr update() { + TreeAttr treeAttr = new TreeAttr(); + treeAttr.setMultipleSelection(checkTypeCheckBox.isSelected()); + treeAttr.setAjax(loadTypeCheckBox.isSelected()); + treeAttr.setSelectLeafOnly(layerTypeCheckBox.isSelected()); treeAttr.setReturnFullPath(returnFullPathCheckBox.isSelected()); - return treeAttr; - } + return treeAttr; + } } diff --git a/designer-base/src/main/java/com/fr/design/gui/style/BackgroundPane.java b/designer-base/src/main/java/com/fr/design/gui/style/BackgroundPane.java index bdf96f32e..3e0f6c213 100644 --- a/designer-base/src/main/java/com/fr/design/gui/style/BackgroundPane.java +++ b/designer-base/src/main/java/com/fr/design/gui/style/BackgroundPane.java @@ -34,6 +34,11 @@ public class BackgroundPane extends AbstractBasicStylePane { //获取当前面板 protected BackgroundQuickPane currentPane = null; + /** + * 记录上次选中的面板 + */ + protected BackgroundQuickPane lastSelectedPane = null; + public BackgroundPane() { this.initComponents(); @@ -62,6 +67,9 @@ public class BackgroundPane extends AbstractBasicStylePane { @Override public void itemStateChanged(ItemEvent e) { cardlayout.show(centerPane, (String) typeComboBox.getSelectedItem()); + if (e.getStateChange() == ItemEvent.DESELECTED) { + lastSelectedPane = currentPane; + } currentPane = paneList[typeComboBox.getSelectedIndex()]; fireStateChanged(); } @@ -144,6 +152,8 @@ public class BackgroundPane extends AbstractBasicStylePane { pane.populateBean(background); typeComboBox.setSelectedIndex(i); currentPane = paneList[i]; + // 填充 初始化 + lastSelectedPane = currentPane; return; } } diff --git a/designer-base/src/main/java/com/fr/design/gui/style/BorderPane.java b/designer-base/src/main/java/com/fr/design/gui/style/BorderPane.java index 3c917ea45..6d6259393 100644 --- a/designer-base/src/main/java/com/fr/design/gui/style/BorderPane.java +++ b/designer-base/src/main/java/com/fr/design/gui/style/BorderPane.java @@ -15,6 +15,7 @@ import com.fr.design.gui.icombobox.LineComboBox; import com.fr.design.gui.ilable.UILabel; import com.fr.design.layout.TableLayout; import com.fr.design.layout.TableLayoutHelper; +import com.fr.design.mainframe.backgroundpane.NullBackgroundQuickPane; import com.fr.design.style.color.NewColorSelectBox; import com.fr.design.utils.gui.AdjustWorkBookDefaultStyleUtils; import com.fr.general.IOUtils; @@ -246,7 +247,9 @@ public class BorderPane extends AbstractBasicStylePane implements GlobalNameObse style = AdjustWorkBookDefaultStyleUtils.adjustCellElement(Style.DEFAULT_STYLE); } - if (backgroundPane.currentPane.isBackgroundChange()) { + boolean isSetNullBackground = backgroundPane.currentPane instanceof NullBackgroundQuickPane + && !(backgroundPane.lastSelectedPane instanceof NullBackgroundQuickPane); + if (backgroundPane.currentPane.isBackgroundChange() || isSetNullBackground) { style = style.deriveBackground(backgroundPane.update()); } if (BORDER_SET.contains(globalNameListener.getGlobalName())) { diff --git a/designer-base/src/main/java/com/fr/design/gui/style/FormatPane.java b/designer-base/src/main/java/com/fr/design/gui/style/FormatPane.java index ba68ab36f..2485b4de4 100644 --- a/designer-base/src/main/java/com/fr/design/gui/style/FormatPane.java +++ b/designer-base/src/main/java/com/fr/design/gui/style/FormatPane.java @@ -24,12 +24,22 @@ import com.fr.general.ComparatorUtils; import com.fr.stable.StringUtils; -import java.math.BigDecimal; +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FontMetrics; +import java.awt.Graphics; import java.math.RoundingMode; -import javax.swing.*; +import javax.swing.BorderFactory; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.SwingConstants; +import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.border.TitledBorder; -import java.awt.*; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.text.Format; @@ -54,6 +64,7 @@ public class FormatPane extends AbstractBasicStylePane implements GlobalNameObse private static final Integer[] TYPES = new Integer[]{ FormatContents.NULL, FormatContents.NUMBER, FormatContents.CURRENCY, FormatContents.PERCENT, + FormatContents.THOUSANDTHS, FormatContents.SCIENTIFIC, FormatContents.DATE, FormatContents.TIME, FormatContents.TEXT}; @@ -244,6 +255,9 @@ public class FormatPane extends AbstractBasicStylePane implements GlobalNameObse } else if (pattern.indexOf("%") > 0) { setPatternComboBoxAndList(FormatContents.PERCENT, pattern); this.roundingBox.setSelected(((CoreDecimalFormat) format).getRoundingMode().equals(RoundingMode.HALF_UP)); + } else if (pattern.indexOf("‰") > 0) { + setPatternComboBoxAndList(FormatContents.THOUSANDTHS, pattern); + this.roundingBox.setSelected(((CoreDecimalFormat) format).getRoundingMode().equals(RoundingMode.HALF_UP)); } else if (pattern.indexOf("E") > 0) { setPatternComboBoxAndList(FormatContents.SCIENTIFIC, pattern); } else { diff --git a/designer-base/src/main/java/com/fr/design/gui/style/TextFormatPane.java b/designer-base/src/main/java/com/fr/design/gui/style/TextFormatPane.java index a477cb16c..0b8e8287a 100644 --- a/designer-base/src/main/java/com/fr/design/gui/style/TextFormatPane.java +++ b/designer-base/src/main/java/com/fr/design/gui/style/TextFormatPane.java @@ -58,7 +58,7 @@ public class TextFormatPane extends AbstractBasicStylePane implements GlobalName private static final Integer[] TYPES = new Integer[]{ FormatContents.NULL, FormatContents.NUMBER, FormatContents.CURRENCY, FormatContents.PERCENT, - FormatContents.SCIENTIFIC, + FormatContents.THOUSANDTHS, FormatContents.SCIENTIFIC, FormatContents.DATE, FormatContents.TIME, FormatContents.TEXT}; @@ -228,6 +228,9 @@ public class TextFormatPane extends AbstractBasicStylePane implements GlobalName this.roundingBox.setSelected(((CoreDecimalFormat) format).getRoundingMode().equals(RoundingMode.HALF_UP)); } else if (pattern.indexOf("E") > 0) { setPatternComboBoxAndList(FormatContents.SCIENTIFIC, pattern); + } else if (pattern.indexOf("‰") > 0) { + setPatternComboBoxAndList(FormatContents.THOUSANDTHS, pattern); + this.roundingBox.setSelected(((CoreDecimalFormat) format).getRoundingMode().equals(RoundingMode.HALF_UP)); } else { setPatternComboBoxAndList(FormatContents.NUMBER, pattern); } @@ -345,7 +348,7 @@ public class TextFormatPane extends AbstractBasicStylePane implements GlobalName } setTextFieldVisible(!isTextOrNull()); setPreviewLabelVisible(!isTextOrNull()); - setRoundingBoxVisible(getFormatContents() == FormatContents.PERCENT); + setRoundingBoxVisible(getFormatContents() == FormatContents.PERCENT || getFormatContents() == FormatContents.THOUSANDTHS); } } diff --git a/designer-base/src/main/java/com/fr/design/lock/LockInfoDialog.java b/designer-base/src/main/java/com/fr/design/lock/LockInfoDialog.java index a0fd544d5..003215ca6 100644 --- a/designer-base/src/main/java/com/fr/design/lock/LockInfoDialog.java +++ b/designer-base/src/main/java/com/fr/design/lock/LockInfoDialog.java @@ -99,7 +99,11 @@ public class LockInfoDialog extends JDialog { return; } final String selectedFilePath = StableUtils.pathJoin(ProjectConstants.REPORTLETS_NAME, TemplateTreePane.getInstance().getFilePath()); - TemplateUtils.createAndOpenTemplate(Toolkit.i18nText("Fine_Design_Template_Lock_Copy"), new FileNodeFILE(new FileNode(selectedFilePath, false)), true); + TemplateUtils.createAndOpenTemplate( + Toolkit.i18nText("Fine_Design_Template_Lock_Copy"), + new FileNodeFILE(new FileNode(selectedFilePath, false)), + false, + true); } }); cancelButton.addActionListener(new ActionListener() { diff --git a/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrame.java b/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrame.java index 812cb86b1..4751649b8 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrame.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrame.java @@ -172,17 +172,10 @@ public class DesignerFrame extends JFrame implements JTemplateActionListener, Ta } //关闭前当前模板 停止编辑 HistoryTemplateListCache.getInstance().getCurrentEditingTemplate().stopEditing(); - SaveSomeTemplatePane saveSomeTempaltePane = new SaveSomeTemplatePane(true); - // 只有一个文件未保存时 - if (HistoryTemplateListCache.getInstance().getHistoryCount() == 1) { - int choose = saveSomeTempaltePane.saveLastOneTemplate(); - if (choose != JOptionPane.CANCEL_OPTION) { - DesignerFrame.this.exit(); - } - } else { - if (saveSomeTempaltePane.showSavePane()) { - DesignerFrame.this.exit(); - } + SaveSomeTemplatePane saveSomeTemplatePane = new SaveSomeTemplatePane(true); + // 全部保存成功才退出 + if (saveSomeTemplatePane.showSavePane()) { + DesignerFrame.this.exit(); } } @@ -1225,4 +1218,4 @@ public class DesignerFrame extends JFrame implements JTemplateActionListener, Ta public void setServerConfig(boolean serverConfig) { this.serverConfig = serverConfig; } -} \ No newline at end of file +} 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 d6ceb3417..c856f4fd3 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 @@ -442,20 +442,40 @@ public abstract class JTemplate> stopListenThemeConfig(); } + /** + * 用于 切换工作目录 时的模板资源暂存 + * @return + */ + public FILE templateToStashFile4Envchange() { + FILE file = this.getEditingFILE(); + if (file.isEnvFile()) { + // 切换工作目录时,模板锁信息被清除,因此这里需要将环境相关模板文件转化为与环境无关的内存模板文件,再创建暂存文件 + return new StashedFILE(new MemFILE(file.getName()), exportBaseBook2ByteArray(), template.suffix()); + } else { + // 其它情况下,直接创建暂存文件 + return templateToStashFile(); + } + } + public FILE templateToStashFile() { FILE file = this.getEditingFILE(); + return new StashedFILE(file, exportBaseBook2ByteArray(), template.suffix()); + } + + private byte[] exportBaseBook2ByteArray() { try { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); BaseBook target = this.getTarget(); if (target != null) { target.export(outputStream); - return new StashedFILE(file, outputStream.toByteArray(), template.suffix()); + return outputStream.toByteArray(); } // 如果 target == null 那么这个模板是被模板内存优化功能处理过的,不用处理 } catch (Exception e) { + FineLoggerFactory.getLogger().error("Export BaseBook to Byte Array Failed"); FineLoggerFactory.getLogger().error(e.getMessage(), e); } - return null; + return new byte[0]; } @@ -492,6 +512,17 @@ public abstract class JTemplate> } + /** + * 刷新 模板资源 和 EditingFILE + * 仅在切换工作目录,reload模板时使用 + * @param file + */ + public void refreshResourceAndEditingFILE(FILE file) { + // 这里替换EditingFILE是为了在切换工作目录后,将模板文件对象设置成环境无关文件对象 + this.editingFILE = file instanceof StashedFILE ? ((StashedFILE) file).getInsideFILE() : file; + refreshResource(file); + } + private void setTargetByFile(FILE file) { T newTemplate = JTemplateFactory.asIOFile(file, this.suffix()); if (newTemplate != null) { @@ -1753,6 +1784,7 @@ public abstract class JTemplate> refreshToolArea(); } DesignerFrameFileDealerPane.getInstance().refresh(); + DesignerFrameFileDealerPane.getInstance().stateChange(); } }); return worker; @@ -1936,4 +1968,4 @@ public abstract class JTemplate> DesignerUIModeConfig.getInstance().setAbsoluteMeasureUIMode(); } -} \ No newline at end of file +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/backgroundpane/NullBackgroundQuickPane.java b/designer-base/src/main/java/com/fr/design/mainframe/backgroundpane/NullBackgroundQuickPane.java index 9a86a38c1..44a46d60a 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/backgroundpane/NullBackgroundQuickPane.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/backgroundpane/NullBackgroundQuickPane.java @@ -35,7 +35,7 @@ public class NullBackgroundQuickPane extends BackgroundQuickPane { @Override public boolean isBackgroundChange() { - return true; + return false; } /** 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 ba9aadcc5..229933736 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 @@ -64,6 +64,7 @@ import com.fr.design.os.impl.SupportOSImpl; import com.fr.design.remote.action.RemoteDesignAuthManagerAction; import com.fr.design.update.actions.SoftwareUpdateAction; import com.fr.design.utils.ThemeUtils; +import com.fr.env.detect.ui.EnvDetectorAction; import com.fr.general.ComparatorUtils; import com.fr.general.GeneralContext; import com.fr.general.locale.LocaleAction; @@ -572,7 +573,9 @@ public abstract class ToolBarMenuDock { if (AlphaFineConfigManager.isALPHALicAvailable()) { shortCuts.add(new AlphaFineAction()); } - + + shortCuts.add(new EnvDetectorAction()); + shortCuts.add(SeparatorDef.DEFAULT); if (DesignerEnvManager.getEnvManager().isOpenDebug()) { OSSupportCenter.buildAction(objects -> shortCuts.add(new FineUIAction()), SupportOSImpl.FINEUI); diff --git a/designer-base/src/main/java/com/fr/design/style/FormatPane.java b/designer-base/src/main/java/com/fr/design/style/FormatPane.java index d1679561d..f1d7a086b 100644 --- a/designer-base/src/main/java/com/fr/design/style/FormatPane.java +++ b/designer-base/src/main/java/com/fr/design/style/FormatPane.java @@ -52,6 +52,7 @@ public class FormatPane extends BasicPane { private UIRadioButton numberRadioButton; private UIRadioButton currencyRadioButton; private UIRadioButton percentRadioButton; + private UIRadioButton thousandthsRadioButton; private UIRadioButton scientificRadioButton; private UIRadioButton dateRadioButton; private UIRadioButton timeRadioButton; @@ -99,6 +100,7 @@ public class FormatPane extends BasicPane { categoryButtonGroup.add(numberRadioButton); categoryButtonGroup.add(currencyRadioButton); categoryButtonGroup.add(percentRadioButton); + categoryButtonGroup.add(thousandthsRadioButton); categoryButtonGroup.add(scientificRadioButton); categoryButtonGroup.add(dateRadioButton); categoryButtonGroup.add(timeRadioButton); @@ -108,6 +110,7 @@ public class FormatPane extends BasicPane { leftControlPane.add(this.createRadioCenterPane(numberRadioButton)); leftControlPane.add(this.createRadioCenterPane(currencyRadioButton)); leftControlPane.add(this.createRadioCenterPane(percentRadioButton)); + leftControlPane.add(this.createRadioCenterPane(thousandthsRadioButton)); leftControlPane.add(this.createRadioCenterPane(scientificRadioButton)); leftControlPane.add(this.createRadioCenterPane(dateRadioButton)); leftControlPane.add(this.createRadioCenterPane(timeRadioButton)); @@ -136,6 +139,8 @@ public class FormatPane extends BasicPane { currencyRadioButton.setMnemonic('C'); percentRadioButton = new UIRadioButton(FormatField.getInstance().getName(FormatContents.PERCENT)); percentRadioButton.setMnemonic('P'); + thousandthsRadioButton = new UIRadioButton(FormatField.getInstance().getName(FormatContents.THOUSANDTHS)); + thousandthsRadioButton.setMnemonic('Q'); scientificRadioButton = new UIRadioButton(FormatField.getInstance().getName(FormatContents.SCIENTIFIC)); scientificRadioButton.setMnemonic('S'); dateRadioButton = new UIRadioButton(FormatField.getInstance().getName(FormatContents.DATE)); @@ -149,6 +154,7 @@ public class FormatPane extends BasicPane { numberRadioButton.addActionListener(radioActionListener); currencyRadioButton.addActionListener(radioActionListener); percentRadioButton.addActionListener(radioActionListener); + thousandthsRadioButton.addActionListener(radioActionListener); scientificRadioButton.addActionListener(radioActionListener); dateRadioButton.addActionListener(radioActionListener); timeRadioButton.addActionListener(radioActionListener); @@ -234,6 +240,9 @@ public class FormatPane extends BasicPane { } else if (pattern.endsWith("%")) { this.percentRadioButton.setSelected(true); this.applyRadioActionListener(this.percentRadioButton); + } else if (pattern.endsWith("‰")){ + this.thousandthsRadioButton.setSelected(true); + this.applyRadioActionListener(this.thousandthsRadioButton); } else if (pattern.indexOf("E") > 0) { this.scientificRadioButton.setSelected(true); this.applyRadioActionListener(this.scientificRadioButton); @@ -259,6 +268,7 @@ public class FormatPane extends BasicPane { scientificRadioButton.setEnabled(false); textRadioButton.setEnabled(false); percentRadioButton.setEnabled(false); + thousandthsRadioButton.setEnabled(false); nullRadioButton.setEnabled(false); dateRadioButton.setEnabled(false); timeRadioButton.setEnabled(false); @@ -285,6 +295,7 @@ public class FormatPane extends BasicPane { numberRadioButton.setEnabled(false); currencyRadioButton.setEnabled(false); percentRadioButton.addActionListener(radioActionListener); + thousandthsRadioButton.setEnabled(false); scientificRadioButton.setEnabled(false); dateRadioButton.setEnabled(false); timeRadioButton.setEnabled(false); @@ -333,6 +344,8 @@ public class FormatPane extends BasicPane { return FormatContents.CURRENCY; else if (percentRadioButton.isSelected()) return FormatContents.PERCENT; + else if (thousandthsRadioButton.isSelected()) + return FormatContents.THOUSANDTHS; else if (scientificRadioButton.isSelected()) return FormatContents.SCIENTIFIC; else if (dateRadioButton.isSelected()) @@ -453,6 +466,8 @@ public class FormatPane extends BasicPane { contents = FormatContents.CURRENCY; } else if (ComparatorUtils.equals(source,percentRadioButton)) { contents = FormatContents.PERCENT; + }else if (ComparatorUtils.equals(source,thousandthsRadioButton)){ + contents = FormatContents.THOUSANDTHS; } else if (ComparatorUtils.equals(source,scientificRadioButton)) { contents = FormatContents.SCIENTIFIC; } else if (ComparatorUtils.equals(source,dateRadioButton)) { diff --git a/designer-base/src/main/java/com/fr/design/ui/util/UIUtil.java b/designer-base/src/main/java/com/fr/design/ui/util/UIUtil.java index b7583d0dd..86093a6d8 100644 --- a/designer-base/src/main/java/com/fr/design/ui/util/UIUtil.java +++ b/designer-base/src/main/java/com/fr/design/ui/util/UIUtil.java @@ -1,9 +1,12 @@ package com.fr.design.ui.util; import com.fr.log.FineLoggerFactory; +import com.fr.third.guava.base.Stopwatch; +import com.fr.third.guava.base.Supplier; import org.jetbrains.annotations.NotNull; import javax.swing.SwingUtilities; +import java.util.concurrent.TimeUnit; /** * 一些常用的 GUI 工具。 @@ -52,4 +55,30 @@ public class UIUtil { } } } + + /** + * 有些时候,交互上需要有一些等待的效果, + * 如果没有等待到, 会让交互失去作用。 + * + * @param supplier 结果 + * @param timeout 超时 + * @param timeUnit 单位 + * @return 结果 + */ + public static T waitUntil(Supplier supplier, long timeout, TimeUnit timeUnit) { + + Stopwatch st = Stopwatch.createStarted(); + T result = supplier.get(); + long elapsed = st.elapsed(timeUnit); + long minus = timeout - elapsed; + if (minus > 0) { + long value = timeUnit.toMillis(minus); + try { + Thread.sleep(value); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + } + return result; + } } diff --git a/designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java b/designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java index 9a849ad4e..deda005c3 100644 --- a/designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java +++ b/designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java @@ -24,6 +24,7 @@ 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.DesignUtils; import com.fr.design.utils.gui.GUICoreUtils; import com.fr.general.*; import com.fr.general.http.HttpToolbox; @@ -56,6 +57,7 @@ import java.util.*; import java.util.List; import java.util.concurrent.ExecutionException; +import static com.fr.design.dialog.FineJOptionPane.OPTION_OK_CANCEL; import static java.nio.charset.StandardCharsets.*; import static javax.swing.JOptionPane.QUESTION_MESSAGE; @@ -350,7 +352,25 @@ public class UpdateMainDialog extends UIDialog { new SwingWorker() { @Override protected JSONObject doInBackground() throws Exception { - return new JSONObject(HttpToolbox.get(CloudCenter.getInstance().acquireUrlByKind("jar11.update"))); + try { + String url = CloudCenter.getInstance().acquireUrlByKind("jar11.update"); + return new JSONObject(HttpToolbox.get(url)); + } catch (SiteBlockedException e) { + stopLoading(); + FineJOptionPane.showConfirmDialogWithOkListener(null, + e.getMessage(), + Toolkit.i18nText("Fine-Design_Basic_Alert"), null, null, + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + DesignUtils.visitEnvServerByParameters( + "#management/system/normal", + null, + null); + } + }); + } + return null; } @Override diff --git a/designer-base/src/main/java/com/fr/design/utils/ColorUtils.java b/designer-base/src/main/java/com/fr/design/utils/ColorUtils.java new file mode 100644 index 000000000..739d39472 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/utils/ColorUtils.java @@ -0,0 +1,30 @@ +package com.fr.design.utils; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.util.Arrays; + +/** + * created by Harrison on 2022/05/29 + **/ +public class ColorUtils { + + /** + * 递归的同步颜色 + * + * @param color 颜色 + */ + public static void syncBackground(Component component, Color color) { + + component.setBackground(color); + + if (component instanceof Container) { + Container container = (Container) component; + Component[] components = container.getComponents(); + if (components != null) { + Arrays.stream(components).forEach((e) -> syncBackground(e, color)); + } + } + } +} diff --git a/designer-base/src/main/java/com/fr/design/utils/LinkStrUtils.java b/designer-base/src/main/java/com/fr/design/utils/LinkStrUtils.java new file mode 100644 index 000000000..b7f8217b4 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/utils/LinkStrUtils.java @@ -0,0 +1,63 @@ +package com.fr.design.utils; + +import com.fr.design.gui.ilable.UILabel; +import com.fr.stable.StringUtils; + +import javax.swing.JComponent; +import java.awt.Color; +import java.awt.Font; + +/** + * created by Harrison on 2022/05/24 + **/ +public class LinkStrUtils { + + public static final UILabel LABEL = new UILabel(); + + public static UILabel generateLabel(String html, JComponent templateLabel) { + + String style = generateStyle(templateLabel.getBackground(), templateLabel.getFont(), templateLabel.getForeground()); + String fullHtml = generateHtmlTag(style, html); + return new UILabel(fullHtml); + } + + public static String generateHtmlTag(String html) { + + String defaultStyle = generateDefaultStyle(); + return generateHtmlTag(defaultStyle, html); + } + + public static String generateHtmlTag(String style, String html) { + + if (StringUtils.isEmpty(style)) { + throw new NullPointerException("style"); + } + if (StringUtils.isEmpty(html)) { + throw new NullPointerException("html"); + } + return "" + html + ""; + } + + public static String generateLinkTag(String link, String text) { + + return "" + text + ""; + } + + public static String generateStyle(Color backgroundColor, Font font, Color fontColor) { + + // 构建相同风格样式 + StringBuilder style = new StringBuilder("font-family:" + font.getFamily() + ";"); + + style.append("font-weight:").append(font.isBold() ? "bold" : "normal").append(";"); + style.append("font-size:").append(font.getSize()).append("pt;"); + style.append("color:rgb(").append(fontColor.getRed()).append(",").append(fontColor.getGreen()).append(",").append(fontColor.getBlue()).append(");"); + style.append("background-color: rgb(").append(backgroundColor.getRed()).append(",").append(backgroundColor.getGreen()).append(",").append(backgroundColor.getBlue()).append(");"); + + return style.toString(); + } + + public static String generateDefaultStyle() { + + return generateStyle(LABEL.getBackground(), LABEL.getFont(), LABEL.getForeground()); + } +} diff --git a/designer-base/src/main/java/com/fr/design/utils/TemplateUtils.java b/designer-base/src/main/java/com/fr/design/utils/TemplateUtils.java index a84c260c3..c4cb64db0 100644 --- a/designer-base/src/main/java/com/fr/design/utils/TemplateUtils.java +++ b/designer-base/src/main/java/com/fr/design/utils/TemplateUtils.java @@ -27,7 +27,16 @@ import java.io.OutputStream; */ public class TemplateUtils { - public static void createAndOpenTemplate(String prefix, FILE file, boolean needOpen) { + /** + * 创建新的模板文件并打开模板 + * @param prefix 模板文件名称前缀 + * @param file 模板文件 + * @param createByEditingTemplate 是否根据 当前编辑模板 来创建新模板 + * 为true时以CurrentEditingTemplate为准创建新模板 + * 为false时以传入的File文件为准创建新模板,此文件可以不是编辑状态 + * @param openNewTemplate 是否需要在创建后打开模板 + */ + public static void createAndOpenTemplate(String prefix, FILE file, boolean createByEditingTemplate, boolean openNewTemplate) { String fileName = file.getName(); String oldPath = file.getPath(); int indexOfLastDot = fileName.lastIndexOf(CoreConstants.DOT); @@ -49,19 +58,18 @@ public class TemplateUtils { if (isOk(result)) { file = fileChooserPane.getSelectedFILE(); - _createAndOpenTemplate(file, oldPath, needOpen); + _createAndOpenTemplate(file, oldPath, createByEditingTemplate, openNewTemplate); } - } - private static void _createAndOpenTemplate(FILE file, String oldPath, boolean needOpen){ + private static void _createAndOpenTemplate(FILE file, String oldPath, boolean createByEditingTemplate, boolean openNewTemplate){ new SwingWorker() { @Override protected Void doInBackground() throws Exception { byte[] content = new byte[0]; - if (!needOpen) { + if (createByEditingTemplate) { // 从当前编辑模板中生成备份文件 JTemplate template = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); content = template.exportData(); @@ -95,7 +103,7 @@ public class TemplateUtils { protected void done() { try { get(); - if (needOpen) { + if (openNewTemplate) { DesignerContext.getDesignerFrame().openTemplate(file); } // 备份成功刷新下目录树 展示出来备份的模板 diff --git a/designer-base/src/main/java/com/fr/design/worker/save/SaveFailureHandler.java b/designer-base/src/main/java/com/fr/design/worker/save/SaveFailureHandler.java index 5d7beb36a..080b9ddbc 100644 --- a/designer-base/src/main/java/com/fr/design/worker/save/SaveFailureHandler.java +++ b/designer-base/src/main/java/com/fr/design/worker/save/SaveFailureHandler.java @@ -22,6 +22,7 @@ import javax.swing.JOptionPane; * @version 11.0 * Created by hades on 2021/12/7 */ +@SuppressWarnings("all") public class SaveFailureHandler implements ThrowableHandler { private static final SaveFailureHandler INSTANCE = new SaveFailureHandler(); @@ -70,7 +71,7 @@ public class SaveFailureHandler implements ThrowableHandler { @Override public boolean process(Throwable e) { if (e.getCause() instanceof UnLockedException || e instanceof UnLockedException) { - processByBack(Toolkit.i18nText("Fine_Design_Template_Has_Been_UnLocked")); + processUnLocked(Toolkit.i18nText("Fine_Design_Template_Has_Been_UnLocked")); return true; } return false; @@ -81,7 +82,7 @@ public class SaveFailureHandler implements ThrowableHandler { @Override public boolean process(Throwable e) { if (e.getCause() instanceof InconsistentLockException || e instanceof InconsistentLockException) { - processByBack(Toolkit.i18nText("Fine_Design_Template_Save_Failed_By_Lock_Inconsistency")); + processInconsistentLock(Toolkit.i18nText("Fine_Design_Template_Save_Failed_By_Lock_Inconsistency")); return true; } return false; @@ -102,7 +103,7 @@ public class SaveFailureHandler implements ThrowableHandler { }; - protected void processByBack(String tip) { + protected void processUnLocked(String tip) { int option = FineJOptionPane.showOptionDialog(DesignerContext.getDesignerFrame(), tip, Toolkit.i18nText("Fine-Design_Basic_Alert"), @@ -113,10 +114,36 @@ public class SaveFailureHandler implements ThrowableHandler { if (option == JOptionPane.YES_OPTION) { JTemplate template = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); if (template != null) { - TemplateUtils.createAndOpenTemplate(Toolkit.i18nText("Fine_Design_Template_Backup"), new FileNodeFILE(new FileNode(template.getPath(), false)), false); + TemplateUtils.createAndOpenTemplate( + Toolkit.i18nText("Fine_Design_Template_Backup"), + new FileNodeFILE(new FileNode(template.getPath(), false)), + true, + false); + } + } + } + + protected void processInconsistentLock(String tip) { + int option = FineJOptionPane.showOptionDialog(DesignerContext.getDesignerFrame(), + tip, + Toolkit.i18nText("Fine-Design_Basic_Alert"), + JOptionPane.YES_NO_OPTION, + JOptionPane.WARNING_MESSAGE, + IOUtils.readIcon("/com/fr/design/images/warnings/warning32.png"), + new Object[] {Toolkit.i18nText("Fine_Design_Template_SaveAs_Backup"), Toolkit.i18nText("Fine-Design_Basic_Button_Cancel")}, null); + if (option == JOptionPane.YES_OPTION) { + JTemplate template = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + if (template != null) { + TemplateUtils.createAndOpenTemplate( + Toolkit.i18nText("Fine_Design_Template_Backup"), + new FileNodeFILE(new FileNode(template.getPath(), false)), + true, + true); + // 创建并打开备份模板后,关闭原模板 + HistoryTemplateListCache.getInstance().closeSelectedReport(template); } } } } -} \ No newline at end of file +} diff --git a/designer-base/src/main/java/com/fr/env/EnvPrepare.java b/designer-base/src/main/java/com/fr/env/EnvPrepare.java new file mode 100644 index 000000000..288d2f09c --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/EnvPrepare.java @@ -0,0 +1,27 @@ +package com.fr.env; + +import com.fr.env.detect.EnvDetectorCenter; +import com.fr.module.Activator; + +/** + * 设计器环境准备 + * 更多的是一些钩子,需要在环境启动、切换时进行处理 + * 使用监听 {@link com.fr.workspace.WorkspaceEvent} 只能满足 + * before -> stop -> start -> after + * 现在支持 => + * before -> stop -> prepare -> start -> after + * + * created by Harrison on 2022/05/29 + **/ +public class EnvPrepare extends Activator { + + @Override + public void start() { + EnvDetectorCenter.getInstance().init(); + } + + @Override + public void stop() { + EnvDetectorCenter.getInstance().destroy(); + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java b/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java new file mode 100644 index 000000000..edaf3d0b3 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java @@ -0,0 +1,226 @@ +package com.fr.env.detect; + +import com.fr.common.util.Collections; +import com.fr.design.components.notification.NotificationDialog; +import com.fr.design.components.notification.NotificationDialogProperties; +import com.fr.design.components.notification.NotificationModel; +import com.fr.design.constants.DesignerLaunchStatus; +import com.fr.design.i18n.Toolkit; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.ui.util.UIUtil; +import com.fr.env.detect.base.DetectorBridge; +import com.fr.env.detect.base.DetectorUtil; +import com.fr.env.detect.base.EnvDetectorConfig; +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorStatus; +import com.fr.event.Event; +import com.fr.event.EventDispatcher; +import com.fr.event.Listener; +import com.fr.event.Null; +import com.fr.log.FineLoggerFactory; +import com.fr.stable.core.UUID; +import com.fr.task.Once; +import com.fr.update.delay.DelayHelper; +import com.fr.workspace.Workspace; +import com.fr.workspace.WorkspaceEvent; + +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 环境检测中心 + * + * created by Harrison on 2022/05/27 + **/ +public class EnvDetectorCenter { + + private final Listener START_UP_COMPLETE_LISTENER = new Listener() { + + @Override + public void on(Event event, Null param) { + // startup 的监听,执行一次即可 + EventDispatcher.stopListen(this); + FineLoggerFactory.getLogger().debug("startup complete, start detecting env"); + stop(); + } + }; + private final Listener AFTER_SWITCH_LISTENER = new Listener() { + @Override + public void on(Event event, Workspace param) { + FineLoggerFactory.getLogger().debug("switch env complete, start detecting env"); + stop(); + } + }; + + private final Once launchOnce = new Once(() -> { + + // 添加启动完成监听 + EventDispatcher.listen(DesignerLaunchStatus.STARTUP_COMPLETE, START_UP_COMPLETE_LISTENER); + // 切换完成后的监听 + EventDispatcher.listen(WorkspaceEvent.AfterSwitch, AFTER_SWITCH_LISTENER); + }); + + private final AtomicReference PROCESS = new AtomicReference<>(); + + /** + * 当前还有什么动作未执行 + * 如果切换环境,这里的动作是要被清空的 + * 从而防止切换环境后, 上一个动作的环境延续过来 + */ + private final Set pendingActions = new HashSet<>(); + + public static EnvDetectorCenter getInstance() { + return EnvDetectorCenterHolder.INSTANCE; + } + + private static class EnvDetectorCenterHolder { + private static final EnvDetectorCenter INSTANCE = new EnvDetectorCenter(); + } + + /** + * 初始化 + */ + public void init() { + + // 如果已经启动了,则不再启动 + if (PROCESS.get() != null) { + return; + } + + start(); + // 默认是启动 + PROCESS.set(DetectorProcess.DESIGN_LAUNCH); + + launchOnce.run(); + } + + + /** + * 销毁,一般用在模块关闭中 + */ + public void destroy() { + + // 清空 + pendingActions.clear(); + + // 重置内容 + DetectorBridge.getInstance().reset(); + // 关闭逻辑 + DetectorBridge.getInstance().stop(); + + PROCESS.set(null); + } + + /** + * 启动 + */ + public void start() { + + DetectorBridge.getInstance().start(); + } + + /** + * 关闭 + */ + public void stop() { + + // 结束 + DetectorBridge.getInstance().stop(); + + // id值 + String uuid = UUID.randomUUID().toString(); + // 确认当前的 action 是否有效 + Supplier validAction = () -> pendingActions.contains(uuid); + + // 30s后执行 + Runnable detectorAction = () -> { + + // 如果当前没开启,则直接返回 + if (!EnvDetectorConfig.getInstance().isEnabled()) { + return; + } + + // 如果当前不包含这个 id, 就不执行 + if (!validAction.get()) { + return; + } + Stream resultStream = DetectorBridge.getInstance().detect(); + + // 展示效果 + NotificationDialogProperties properties = new NotificationDialogProperties(DesignerContext.getDesignerFrame(), Toolkit.i18nText("Fine-Design_Basic_Detect_Notification_Title")); + List notificationModels = resultStream + .filter(Objects::nonNull) + .filter((e) -> e.getStatus() == DetectorStatus.EXCEPTION) + .map(DetectorUtil::convert2Notification) + .collect(Collectors.toList()); + if (Collections.isEmpty(notificationModels)) { + FineLoggerFactory.getLogger().debug("detector not found any exception"); + return; + } + + UIUtil.invokeLaterIfNeeded(() -> { + + // 如果当前不包含这个 id, 就不执行 + if (!validAction.get()) { + return; + } + NotificationDialog dialog = new NotificationDialog(properties, notificationModels); + dialog.open(); + + // 执行完移除 + pendingActions.remove(uuid); + }); + + }; + + // 添加 + pendingActions.add(uuid); + + + DelayHelper.delayCall(EnvDetectorCenter.class.getName(), detectorAction, 30, TimeUnit.SECONDS); + + } + + /** + * 使用预期外的错误进行展示 + * + * @param throwable 异常 + * @return + */ + public List terminate(Throwable throwable) { + + Stream resultStream = DetectorBridge.getInstance().detect(throwable); + return resultStream + .filter((e) -> e.getStatus() == DetectorStatus.EXCEPTION) + .collect(Collectors.toList()); + } + + /** + * 预期外的终止 + * + * @return 检测结果 + */ + public List terminateUnexpectedly() { + + Stream resultStream = DetectorBridge.getInstance().detect(); + return resultStream + .filter((e) -> e.getStatus() == DetectorStatus.EXCEPTION) + .collect(Collectors.toList()); + } + + + private enum DetectorProcess { + + /** + * 设计器启动 + */ + DESIGN_LAUNCH, + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/base/AbstractExceptionDetector.java b/designer-base/src/main/java/com/fr/env/detect/base/AbstractExceptionDetector.java new file mode 100644 index 000000000..5414ba648 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/base/AbstractExceptionDetector.java @@ -0,0 +1,19 @@ +package com.fr.env.detect.base; + +import com.fr.env.detect.bean.DetectorType; + +/** + * created by Harrison on 2022/05/13 + **/ +public abstract class AbstractExceptionDetector implements ExceptionDetector { + + private DetectorType type; + + public AbstractExceptionDetector(DetectorType type) { + this.type = type; + } + + public DetectorType type() { + return type; + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/base/CatchExceptionDetector.java b/designer-base/src/main/java/com/fr/env/detect/base/CatchExceptionDetector.java new file mode 100644 index 000000000..0dbce9c00 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/base/CatchExceptionDetector.java @@ -0,0 +1,55 @@ +package com.fr.env.detect.base; + +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.thowable.ThrowableConverter; +import com.fr.env.detect.thowable.ThrowableStore; + +import java.util.List; + +/** + * created by Harrison on 2022/05/13 + **/ +public abstract class CatchExceptionDetector extends AbstractExceptionDetector { + + private ThrowableStore throwableStore; + + private ThrowableConverter throwableConverter; + + public CatchExceptionDetector(DetectorType type, ThrowableConverter throwableConverter) { + super(type); + this.throwableStore = ThrowableStore.getInstance(); + this.throwableConverter = throwableConverter; + } + + public CatchExceptionDetector(DetectorType type, ThrowableStore throwableStore, ThrowableConverter throwableConverter) { + super(type); + this.throwableStore = throwableStore; + this.throwableConverter = throwableConverter; + } + + public ThrowableStore getThrowableStore() { + return throwableStore; + } + + public ThrowableConverter getThrowableConverter() { + return throwableConverter; + } + + @Override + public DetectorResult detect() { + + List throwableList = throwableStore.getAll(); + for (Throwable throwable : throwableList) { + if (throwableConverter.accept(throwable)) { + + DetectorResult result = throwableConverter.convert(throwable); + if (result == null) { + result = DetectorResult.normal(type()); + } + return result; + } + } + return DetectorResult.normal(type()); + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java b/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java new file mode 100644 index 000000000..798e0ae04 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java @@ -0,0 +1,146 @@ +package com.fr.env.detect.base; + +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.impl.FineDbDirtyDetector; +import com.fr.env.detect.impl.FineDbLockedDetector; +import com.fr.env.detect.impl.FineDbPermissionDetector; +import com.fr.env.detect.impl.JarConflictDetector; +import com.fr.env.detect.impl.JarInconsistentDetector; +import com.fr.env.detect.impl.JarLackDetector; +import com.fr.env.detect.thowable.ThrowableLogAppender; +import com.fr.env.detect.thowable.ThrowableStore; +import com.fr.log.FineLoggerFactory; +import com.fr.value.NotNullLazyValue; +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Stream; + +/** + * 检测器桥接逻辑 + * [detect-core] - bridge - ui + * + * created by Harrison on 2022/05/13 + **/ +public class DetectorBridge { + + public static DetectorBridge getInstance() { + + return DetectorBridgeHolder.INSTANCE; + } + + private static class DetectorBridgeHolder { + + private static final DetectorBridge INSTANCE = new DetectorBridge(); + } + + private final NotNullLazyValue detectorManager = new NotNullLazyValue() { + + @Override + protected @NotNull DetectorManager compute() { + + DetectorManager manager = new DetectorManager(); + + manager.register(new FineDbDirtyDetector()); + manager.register(new FineDbPermissionDetector()); + manager.register(new FineDbLockedDetector()); + + manager.register(new JarInconsistentDetector()); + manager.register(new JarLackDetector()); + manager.register(new JarConflictDetector()); + return manager; + } + }; + + private final AtomicBoolean hasStarted = new AtomicBoolean(false); + + public void start() { + + if (!hasStarted.get() && EnvDetectorConfig.getInstance().isEnabled()) { + // 开始注册异常处理 + ThrowableLogAppender.getInstance().enable(); + hasStarted.set(true); + } + } + + public void stop() { + + if (hasStarted.compareAndSet(true, false)) { + // 关闭异常处理 + ThrowableLogAppender.getInstance().disable(); + } + } + + public void reset() { + + ThrowableStore.getInstance().reset(); + } + + /** + * 针对某一项进行检测 + * 主要用于手动检测时 + * + * @param type 检测类型 + * @return 检测结果 + */ + @NotNull + public DetectorResult detect(DetectorType type) { + + try { + return detectorManager.getValue().detect(type); + } catch (Exception e) { + FineLoggerFactory.getLogger().error("detect failed", e); + // 返回未知错误信息 + return DetectorResult.unknown(type); + } + } + + /** + * 针对某一项 \ 某一个异常进行检测 + * + * @param type 类型 + * @param throwable 异常 + * @return 结果 + */ + @NotNull + public DetectorResult detect(DetectorType type, Throwable throwable) { + + ThrowableStore.getInstance().add(throwable); + + DetectorResult result = detect(type); + + ThrowableStore.getInstance().reset(); + + return result; + + } + + /** + * 异常检测 + * 对异常统一检测 + * + * @return 能够检测出的异常情况 + */ + public Stream detect() { + + return detectorManager.getValue().detect(); + } + + /** + * 异常检测 + * 当遇到异常时,且异常难以处理,直接导致服务器启动失败时,调用 + * 将异常添加进来,统一检测 + * + * @param throwable 异常 + * @return 检测结果 + */ + public Stream detect(Throwable throwable) { + + ThrowableStore.getInstance().add(throwable); + Stream result = detect(); + ThrowableStore.getInstance().reset(); + + return result; + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/base/DetectorConstants.java b/designer-base/src/main/java/com/fr/env/detect/base/DetectorConstants.java new file mode 100644 index 000000000..4e4592dcd --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/base/DetectorConstants.java @@ -0,0 +1,18 @@ +package com.fr.env.detect.base; + +/** + * created by Harrison on 2022/05/25 + **/ +public class DetectorConstants { + + public static final String JAR_HELP_LINK = "https://help.fanruan.com/finereport/doc-view-4700.html?source=3"; + + public static final String FINE_DB_HELP_LINK = "https://help.fanruan.com/finereport/doc-view-4701.html?source=3"; + + public static final String SEPARATOR = "、"; + public static final String BR_TAG = "
"; + + public static final String WEB_LIB_PATH = "%FR_HOME%\\webapps\\webroot\\WEB-INF\\lib:"; + + public static final String FR_HOME_LIB = "%FR_HOME%\\lib:"; +} diff --git a/designer-base/src/main/java/com/fr/env/detect/base/DetectorManager.java b/designer-base/src/main/java/com/fr/env/detect/base/DetectorManager.java new file mode 100644 index 000000000..bc65870da --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/base/DetectorManager.java @@ -0,0 +1,53 @@ +package com.fr.env.detect.base; + +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorType; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 检测器中心 + * + * created by Harrison on 2022/05/24 + **/ +class DetectorManager { + + private List detectors = new ArrayList<>(); + + public void register(ExceptionDetector detector) { + + if (detector != null) { + detectors.add(detector); + } + } + + public Stream detect() { + + Stream results = detectors.stream() + .map(ExceptionDetector::detect) + .filter(Objects::nonNull); + + List resultList = results.collect(Collectors.toList()); + resultList.forEach(DetectorResult::log); + + return resultList.stream(); + } + + public DetectorResult detect(DetectorType type) { + + Optional result = detectors.stream() + .filter((detector -> type == detector.type())) + .findFirst() + .map(ExceptionDetector::detect); + + // 记录日志 + result.ifPresent(DetectorResult::log); + + return result.orElse(DetectorResult.normal(type)); + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/base/DetectorUtil.java b/designer-base/src/main/java/com/fr/env/detect/base/DetectorUtil.java new file mode 100644 index 000000000..b9253ed4c --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/base/DetectorUtil.java @@ -0,0 +1,166 @@ +package com.fr.env.detect.base; + +import com.fr.base.function.ThrowableRunnable; +import com.fr.common.util.Collections; +import com.fr.design.components.notification.NotificationAction; +import com.fr.design.components.notification.NotificationMessage; +import com.fr.design.components.notification.NotificationModel; +import com.fr.design.components.notification.NotificationType; +import com.fr.design.dialog.link.MessageWithLink; +import com.fr.design.utils.LinkStrUtils; +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.ExceptionSolution; +import com.fr.env.detect.bean.ExceptionTips; +import com.fr.env.detect.bean.Message; +import com.fr.env.detect.bean.SolutionAction; +import com.fr.general.build.BuildInfo; +import com.fr.third.org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; + +import javax.swing.JComponent; +import java.awt.Desktop; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; + +import static com.fr.env.detect.base.DetectorConstants.BR_TAG; +import static com.fr.env.detect.base.DetectorConstants.FR_HOME_LIB; +import static com.fr.env.detect.base.DetectorConstants.SEPARATOR; +import static com.fr.env.detect.base.DetectorConstants.WEB_LIB_PATH; + +/** + * created by Harrison on 2022/05/25 + **/ +public class DetectorUtil { + + /** + * 是否是设计器的 jar + * + * @param info 信息 + * @return 是/否 + */ + public static boolean isDesignerJar(BuildInfo info) { + + if (info == null) { + return false; + } + return StringUtils.contains(info.getJar(), "fine-report-designer"); + } + + /** + * 将结果转化为提醒的数据 + * + * @param result 结果 + * @return 数据 + */ + public static NotificationModel convert2Notification(DetectorResult result) { + + List messages = new ArrayList<>(); + + Function> convert2NotificationMsg = message -> { + + NotificationMessage notificationMessage = null; + if (message != null) { + Message.Type type = message.getType(); + switch (type) { + case SIMPLE: + notificationMessage = (new NotificationMessage.SimpleMessage(message.get())); + break; + case LINK: + Message.Link linkMsg = (Message.Link) message; + notificationMessage = new NotificationMessage.LinkMessage(linkMsg.getText(), linkMsg.getLink()); + break; + default: + break; + } + } + return Optional.ofNullable(notificationMessage); + }; + + ExceptionTips tips = result.getTips(); + if (tips != null) { + convert2NotificationMsg + .apply(tips.getMessage()) + .ifPresent(messages::add); + } + + ExceptionSolution solution = result.getSolution(); + if (solution != null) { + convert2NotificationMsg.apply(solution.getMessage()) + .ifPresent(messages::add); + } + + NotificationAction notificationAction = null; + if (solution != null) { + SolutionAction solutionAction = solution.getAction(); + if (solutionAction != null) { + notificationAction = new NotificationAction() { + @Override + public String name() { + return solutionAction.name(); + } + + @Override + public void run(Object... args) { + solutionAction.run(); + } + }; + } + } + + return new NotificationModel(NotificationType.WARNING, notificationAction, messages); + } + + /** + * 将信息转化为展示的组件 + * + * @param message 信息 + * @return 组件 + */ + public static JComponent convert2TextComponent(@NotNull Message message, JComponent template) { + + if (message.getType() == Message.Type.LINK) { + Message.Link linkMsg = (Message.Link) message; + return new MessageWithLink(linkMsg.getText(), ThrowableRunnable.toRunnable(() -> { + Desktop.getDesktop().browse(URI.create(linkMsg.getLink())); + })); + } + return LinkStrUtils.generateLabel(message.get(), template); + } + + /** + * 将 lib 转化成合适的格式 + * %FR_HOME%/lib + * %FR_HOME%/webapps/webroot/WEB-INF/lib + * + * @param libMap jar 路径, key为前缀 + * @return 信息 + */ + public static String concatLibFormatMsg(Map> libMap) { + + String webLibPath = WEB_LIB_PATH; + String homeLibPath = FR_HOME_LIB; + + StringBuilder sb = new StringBuilder(); + + List homeLibs = libMap.get(homeLibPath); + if (!Collections.isEmpty(homeLibs)) { + sb.append(homeLibPath); + sb.append(StringUtils.join(homeLibs, SEPARATOR)); + } + + List webLibs = libMap.get(webLibPath); + if (!Collections.isEmpty(webLibs)) { + if (sb.length() != 0) { + sb.append(BR_TAG); + } + sb.append(webLibPath); + sb.append(StringUtils.join(webLibs, SEPARATOR)); + } + return sb.toString(); + } + +} diff --git a/designer-base/src/main/java/com/fr/env/detect/base/EnvDetectorConfig.java b/designer-base/src/main/java/com/fr/env/detect/base/EnvDetectorConfig.java new file mode 100644 index 000000000..caafdbe46 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/base/EnvDetectorConfig.java @@ -0,0 +1,59 @@ +package com.fr.env.detect.base; + +import com.fr.design.DesignerEnvManager; +import com.fr.stable.xml.XMLPrintWriter; +import com.fr.stable.xml.XMLable; +import com.fr.stable.xml.XMLableReader; + +/** + * created by Harrison on 2022/05/13 + **/ +public class EnvDetectorConfig implements XMLable { + + public static final String XML_TAG = "EnvDetectorConfig"; + + private static final long serialVersionUID = -8170289826729582122L; + + private static final EnvDetectorConfig INSTANCE = new EnvDetectorConfig(); + + public static EnvDetectorConfig getInstance() { + + return INSTANCE; + } + + private boolean enabled = true; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + save(); + } + + private void save() { + + DesignerEnvManager.getEnvManager(false).saveXMLFile(); + } + + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + + @Override + public void readXML(XMLableReader reader) { + if (reader.isAttr()) { + this.setEnabled(reader.getAttrAsBoolean("isEnabled", true)); + } + } + + @Override + public void writeXML(XMLPrintWriter writer) { + writer.startTAG(XML_TAG); + writer.attr("isEnabled", this.isEnabled()); + writer.end(); + } + +} diff --git a/designer-base/src/main/java/com/fr/env/detect/base/ExceptionDetector.java b/designer-base/src/main/java/com/fr/env/detect/base/ExceptionDetector.java new file mode 100644 index 000000000..7713ef739 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/base/ExceptionDetector.java @@ -0,0 +1,25 @@ +package com.fr.env.detect.base; + +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorType; + +/** + * created by Harrison on 2022/05/13 + **/ +public interface ExceptionDetector { + + /** + * 检测类型 + * + * @return TYPE + */ + DetectorType type(); + + /** + * 检测结果 + * + * @return 结果 + */ + DetectorResult detect(); + +} diff --git a/designer-base/src/main/java/com/fr/env/detect/base/ThrowableBridge.java b/designer-base/src/main/java/com/fr/env/detect/base/ThrowableBridge.java new file mode 100644 index 000000000..c8ef7a02c --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/base/ThrowableBridge.java @@ -0,0 +1,40 @@ +package com.fr.env.detect.base; + +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.thowable.ThrowableConverter; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +/** + * created by Harrison on 2022/05/13 + **/ +class ThrowableBridge { + + private List throwableConverters = new ArrayList<>(); + + public void register(ThrowableConverter throwableConverter) { + + if (throwableConverter != null) { + throwableConverters.add(throwableConverter); + } + } + + /** + * 将 throwable 转化成检测的结果 + * + * @param throwable 异常 + * @return 结果 + */ + public Optional convert(Throwable throwable) { + + return throwableConverters + .stream() + .filter((e) -> e.accept(throwable)) + .map((e) -> e.convert(throwable)) + .filter(Objects::nonNull) + .findFirst(); + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/base/package-info.java b/designer-base/src/main/java/com/fr/env/detect/base/package-info.java new file mode 100644 index 000000000..12fef30e1 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/base/package-info.java @@ -0,0 +1,4 @@ +/** + * 设计器环境检测 见 {@see https://kms.fineres.com/pages/viewpage.action?pageId=388333688} + */ +package com.fr.env.detect.base; diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/DetectorResult.java b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorResult.java new file mode 100644 index 000000000..aa9a007bb --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorResult.java @@ -0,0 +1,147 @@ +package com.fr.env.detect.bean; + +import org.jetbrains.annotations.Nullable; + +/** + * 检测结果 + * + * created by Harrison on 2022/05/13 + **/ +public class DetectorResult { + + private DetectorType type; + + private DetectorStatus status; + + private ExceptionTips tips; + + private ExceptionSolution solution; + + private ExceptionLog log; + + private DetectorResult(DetectorType type) { + this.type = type; + } + + private DetectorResult(DetectorType type, ExceptionTips tips, ExceptionSolution solution, ExceptionLog log) { + + this.type = type; + this.tips = tips; + this.solution = solution; + this.log = log; + } + + public static DetectorResult unknown(DetectorType type) { + DetectorResult result = new DetectorResult(type); + result.status = DetectorStatus.UNKNOWN; + return result; + } + + public static DetectorResult normal(DetectorType type) { + + DetectorResult result = new DetectorResult(type); + result.status = DetectorStatus.NORMAL; + return result; + } + + public static DetectorResult exception(DetectorType type, ExceptionTips tips, ExceptionSolution solution, ExceptionLog log) { + + DetectorResult result = new DetectorResult(type, tips, solution, log); + result.status = DetectorStatus.EXCEPTION; + return result; + } + + public DetectorStatus getStatus() { + return status; + } + + public DetectorType getType() { + return type; + } + + @Nullable + public ExceptionTips getTips() { + return tips; + } + + @Nullable + public ExceptionSolution getSolution() { + return solution; + } + + public void log() { + + if (log != null) { + log.log(); + } + } + + public static DetectorResultBuilder builder() { + + return new DetectorResultBuilder() + .withStatus(DetectorStatus.EXCEPTION); + } + + public static final class DetectorResultBuilder { + + private DetectorType type; + private DetectorStatus status; + private ExceptionTips tips; + private ExceptionSolution solution; + private ExceptionLog log; + + private DetectorResultBuilder() { + } + + public DetectorResultBuilder withType(DetectorType type) { + this.type = type; + return this; + } + + public DetectorResultBuilder withStatus(DetectorStatus status) { + this.status = status; + return this; + } + + public DetectorResultBuilder withTips(ExceptionTips tips) { + this.tips = tips; + return this; + } + + public DetectorResultBuilder withTips(String message) { + + Message.Simple simple = new Message.Simple(message); + this.tips = new ExceptionTips(simple); + return this; + } + + public DetectorResultBuilder withSolution(ExceptionSolution solution) { + this.solution = solution; + return this; + } + + public DetectorResultBuilder withSolution(String content, String link) { + + Message.Link message = new Message.Link(content, link); + this.solution = new ExceptionSolution(message, null); + return this; + } + + public DetectorResultBuilder withLog(String log, Object... args) { + + this.log = ExceptionLog.create(log, args); + return this; + } + + public DetectorResultBuilder withLog(ExceptionLog log) { + this.log = log; + return this; + } + + public DetectorResult build() { + DetectorResult detectorResult = new DetectorResult(type, tips, solution, log); + detectorResult.status = this.status; + return detectorResult; + } + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/DetectorStatus.java b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorStatus.java new file mode 100644 index 000000000..b7e5df818 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorStatus.java @@ -0,0 +1,22 @@ +package com.fr.env.detect.bean; + +/** + * created by Harrison on 2022/05/27 + **/ +public enum DetectorStatus { + + /** + * 正常 + */ + NORMAL, + + /** + * 异常 + */ + EXCEPTION, + + /** + * 未知 + */ + UNKNOWN, +} diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/DetectorType.java b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorType.java new file mode 100644 index 000000000..1dc8e4d62 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorType.java @@ -0,0 +1,159 @@ +package com.fr.env.detect.bean; + +import com.fr.design.i18n.Toolkit; + +/** + * 检测的原生数据 + * 其实这里可以继续拆分到不同的逻辑下面的, 比如实际生成 DetectorResult 的地方 {@link com.fr.env.detect.base.ExceptionDetector}。 + * 不过目前没有必要。先这样处理。 + * + * created by Harrison on 2022/05/13 + **/ +public enum DetectorType { + + /** + * 缺少 JAR + */ + LACK_OF_JAR(Kind.JAR, WorkType.LOCAL, + "Fine_Design_Basic_Jar_Lacked_Desc", + "Fine_Design_Basic_Jar_Lack", + "Fine_Design_Basic_Jar_Solution", + "Fine_Design_Basic_Log_Jar_Lack"), + + + /** + * JAR 包版本不一致 + */ + JAR_IN_CONSISTENCE(Kind.JAR, WorkType.SIMPLE, + "Fine_Design_Basic_Jar_InConsistent_Desc", + "Fine_Design_Basic_Jar_InConsistent", + "Fine_Design_Basic_Jar_Solution", + "Fine_Design_Basic_Log_Jar_InConsistent"), + + /** + * JAR 包冲突 + */ + JAR_CONFLICT(Kind.JAR, WorkType.REMOTE, + "Fine_Design_Basic_Jar_Problem_Desc", + "Fine_Design_Basic_Jar_Problem", + "Fine_Design_Basic_Jar_Solution", + "Fine_Design_Basic_Log_Jar_Problem"), + + /** + * FineDB 权限问题 + */ + FINE_DB_PERMISSION(Kind.FINE_DB, WorkType.LOCAL, + "Fine_Design_Basic_FineDB_Permission_Occupied_Desc", + "Fine_Design_Basic_FineDB_Permission_Occupied", + "Fine_Design_Basic_FineDB_Solution", + "Fine_Design_Basic_Log_FineDB_Permission_Occupied"), + + /** + * FineDB 锁 + */ + FINE_DB_LOCKED(Kind.FINE_DB, WorkType.LOCAL, + "Fine_Design_Basic_FineDB_Locked_Desc", + "Fine_Design_Basic_FineDB_Locked", + "Fine_Design_Basic_FineDB_Solution", + "Fine_Design_Basic_Log_FineDB_Locked"), + + /** + * FineDB 脏数据 + */ + FINE_DB_DIRTY(Kind.FINE_DB, WorkType.SIMPLE, + "Fine_Design_Basic_FineDB_Dirty_Data_Desc", + "Fine_Design_Basic_FineDB_Dirty_Data", + "Fine_Design_Basic_FineDB_Dirty_Solution", + "Fine_Design_Basic_Log_FineDB_Dirty_Data") + ; + + private final Kind kind; + + private final WorkType workType; + + private final String descLocale; + + private final String tipsLocale; + + private final String solutionLocale; + + private final String logLocale; + + DetectorType(Kind kind, WorkType workType, String descLocale, String tipsLocale, String solutionLocale, String logLocale) { + + this.kind = kind; + this.workType = workType; + this.descLocale = descLocale; + this.tipsLocale = tipsLocale; + this.solutionLocale = solutionLocale; + this.logLocale = logLocale; + } + + public String getDescription() { + return Toolkit.i18nText(descLocale); + } + + public String getDescLocale() { + return descLocale; + } + + public Kind getKind() { + return kind; + } + + public WorkType getWorkType() { + return workType; + } + + public String getTipsLocale() { + return tipsLocale; + } + + public String getSolutionLocale() { + return solutionLocale; + } + + public String getLogLocale() { + return logLocale; + } + + public enum Kind { + + /** + * JAR 类型 + */ + JAR("Fine_Design_Basic_Jar_Kind_Desc"), + + /** + * FineDB 类型 + */ + FINE_DB("Fine_Design_Basic_FineDB_Kind_Desc"); + + private final String locale; + + Kind(String locale) { + this.locale = locale; + } + + public String getDescription() { + return Toolkit.i18nText(this.locale); + } + } + + public enum WorkType { + + /** + * 本地 + */ + LOCAL, + /** + * 远程 + */ + REMOTE, + /** + * 全部 + */ + SIMPLE + } + +} diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionLog.java b/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionLog.java new file mode 100644 index 000000000..521e90345 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionLog.java @@ -0,0 +1,36 @@ +package com.fr.env.detect.bean; + +import com.fr.log.FineLoggerFactory; + +/** + * created by Harrison on 2022/05/13 + **/ +public class ExceptionLog { + + private final String template; + + private final Object[] args; + + private ExceptionLog(String template, Object... args) { + this.template = template; + this.args = args; + } + + public static ExceptionLog create(String template, Object... args) { + + return new ExceptionLog(template, args); + } + + public void log() { + + FineLoggerFactory.getLogger().error(template, args); + } + + public String getTemplate() { + return template; + } + + public Object[] getArgs() { + return args; + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionSolution.java b/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionSolution.java new file mode 100644 index 000000000..b4f549f67 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionSolution.java @@ -0,0 +1,32 @@ +package com.fr.env.detect.bean; + +import org.jetbrains.annotations.Nullable; + +/** + * created by Harrison on 2022/05/13 + **/ +public class ExceptionSolution { + + private Message message; + + @Nullable + private SolutionAction action; + + public static ExceptionSolution create(String text, String link, SolutionAction action) { + + return new ExceptionSolution(new Message.Link(text, link), action); + } + + public ExceptionSolution(Message message, @Nullable SolutionAction action) { + this.message = message; + this.action = action; + } + + public Message getMessage() { + return message; + } + + public @Nullable SolutionAction getAction() { + return action; + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionTips.java b/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionTips.java new file mode 100644 index 000000000..b7ab6c63f --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionTips.java @@ -0,0 +1,24 @@ +package com.fr.env.detect.bean; + +/** + * 异常提示 + * + * created by Harrison on 2022/05/13 + **/ +public class ExceptionTips { + + private Message message; + + public static ExceptionTips create(String text) { + + return new ExceptionTips(new Message.Simple(text)); + } + + public ExceptionTips(Message message) { + this.message = message; + } + + public Message getMessage() { + return message; + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/Message.java b/designer-base/src/main/java/com/fr/env/detect/bean/Message.java new file mode 100644 index 000000000..2acdec8b8 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/bean/Message.java @@ -0,0 +1,83 @@ +package com.fr.env.detect.bean; + +/** + * created by Harrison on 2022/05/24 + **/ +public interface Message { + + /** + * 消息类型 + * + * @return 类型 + */ + Type getType(); + + /** + * 返回内容 + * + * @return 内容 + */ + String get(); + + enum Type { + + /** + * 简单 + */ + SIMPLE, + + /** + * 链接 + */ + LINK + } + + class Simple implements Message { + + private String message; + + public Simple(String message) { + this.message = message; + } + + @Override + public String get() { + return message; + } + + @Override + public Type getType() { + return Type.SIMPLE; + } + } + + class Link implements Message { + + private String text; + + private String link; + + public Link(String text, String link) { + this.text = text; + this.link = link; + } + + @Override + public String get() { + return getText(); + } + + public String getText() { + return text; + } + + public String getLink() { + return link; + } + + @Override + public Type getType() { + return Type.LINK; + } + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/SolutionAction.java b/designer-base/src/main/java/com/fr/env/detect/bean/SolutionAction.java new file mode 100644 index 000000000..4b959053b --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/bean/SolutionAction.java @@ -0,0 +1,11 @@ +package com.fr.env.detect.bean; + +/** + * created by Harrison on 2022/05/24 + **/ +public interface SolutionAction { + + String name(); + + void run(); +} diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/FineDbDirtyDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/FineDbDirtyDetector.java new file mode 100644 index 000000000..6e33eda43 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/impl/FineDbDirtyDetector.java @@ -0,0 +1,106 @@ +package com.fr.env.detect.impl; + +import com.fr.config.ConfigContext; +import com.fr.config.Configuration; +import com.fr.design.dialog.FineJOptionPane; +import com.fr.design.i18n.Toolkit; +import com.fr.env.detect.base.AbstractExceptionDetector; +import com.fr.env.detect.base.DetectorConstants; +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.bean.ExceptionSolution; +import com.fr.env.detect.bean.Message; +import com.fr.env.detect.bean.SolutionAction; +import com.fr.io.utils.ResourceIOUtils; +import com.fr.log.FineLoggerFactory; +import com.fr.stable.StableUtils; +import com.fr.stable.project.ProjectConstants; +import com.fr.third.org.hibernate.exception.GenericJDBCException; + +import javax.swing.JOptionPane; +import java.util.Iterator; +import java.util.function.Function; + +/** + * created by Harrison on 2022/05/25 + **/ +public class FineDbDirtyDetector extends AbstractExceptionDetector { + + public FineDbDirtyDetector() { + + super(DetectorType.FINE_DB_DIRTY); + } + + @Override + public DetectorResult detect() { + + Iterator tableNames = ConfigContext.getConfigNames(); + while (tableNames.hasNext()) { + String tableName = tableNames.next(); + Class configClass = ConfigContext.getConfigClass(tableName); + Configuration configuration = ConfigContext.getConfigInstance(configClass); + try { + + // 尝试获取每一个值 + configuration.mirror(); + } catch (Throwable e) { + + Function isDirtyExFunction = throwable -> { + while (throwable != null) { + if (throwable instanceof GenericJDBCException) { + return true; + } + throwable = throwable.getCause(); + } + return false; + }; + boolean isDirtyEx = isDirtyExFunction.apply(e); + + if (isDirtyEx) { + DetectorType detectorType = DetectorType.FINE_DB_DIRTY; + DetectorResult.DetectorResultBuilder builder = DetectorResult + .builder() + .withType(detectorType); + + String tipsLocale = Toolkit.i18nText(detectorType.getTipsLocale(), tableName); + + String solutionLocale = detectorType.getSolutionLocale(); + ExceptionSolution exceptionSolution = new ExceptionSolution(new Message.Link(Toolkit.i18nText(solutionLocale, DetectorConstants.FINE_DB_HELP_LINK), DetectorConstants.FINE_DB_HELP_LINK), new SolutionAction() { + @Override + public String name() { + return Toolkit.i18nText("Fine-Design_Basic_Reset_Immediately"); + } + + @Override + public void run() { + boolean success = false; + try { + ResourceIOUtils.copy(StableUtils.pathJoin(ProjectConstants.EMBED_DB_DIRECTORY, ProjectConstants.FINE_DB_NAME), + StableUtils.pathJoin(ProjectConstants.EMBED_DB_DIRECTORY, ProjectConstants.FINE_DB_BAK_NAME)); + success = ResourceIOUtils.delete(StableUtils.pathJoin(ProjectConstants.EMBED_DB_DIRECTORY, ProjectConstants.FINE_DB_NAME)); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + // todo 最好的逻辑是,这里应该和 UI 隔离开的 + if (!success) { + FineJOptionPane.showMessageDialog(null, + Toolkit.i18nText("Fine-Design_Error_Finedb_Backup_Reset_Result", + ResourceIOUtils.getRealPath(StableUtils.pathJoin(ProjectConstants.EMBED_DB_DIRECTORY, ProjectConstants.FINE_DB_NAME))), + Toolkit.i18nText("Fine-Design_Basic_Error"), + JOptionPane.ERROR_MESSAGE); + } + } + }); + builder.withTips(tipsLocale) + .withSolution(exceptionSolution) + .withLog(Toolkit.i18nText(detectorType.getLogLocale(), tableName)); + + return builder.build(); + } + + } + } + + return DetectorResult.normal(DetectorType.FINE_DB_DIRTY); + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/FineDbLockedDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/FineDbLockedDetector.java new file mode 100644 index 000000000..c312ce328 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/impl/FineDbLockedDetector.java @@ -0,0 +1,16 @@ +package com.fr.env.detect.impl; + +import com.fr.env.detect.base.CatchExceptionDetector; +import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.impl.converter.FineDbLockedConverter; + +/** + * created by Harrison on 2022/05/26 + **/ +public class FineDbLockedDetector extends CatchExceptionDetector { + + public FineDbLockedDetector() { + super(DetectorType.FINE_DB_LOCKED, new FineDbLockedConverter()); + } + +} diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/FineDbPermissionDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/FineDbPermissionDetector.java new file mode 100644 index 000000000..4e3a18ced --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/impl/FineDbPermissionDetector.java @@ -0,0 +1,15 @@ +package com.fr.env.detect.impl; + +import com.fr.env.detect.base.CatchExceptionDetector; +import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.impl.converter.FineDbPermissionConverter; + +/** + * created by Harrison on 2022/05/26 + **/ +public class FineDbPermissionDetector extends CatchExceptionDetector { + + public FineDbPermissionDetector() { + super(DetectorType.FINE_DB_PERMISSION, new FineDbPermissionConverter()); + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/JarConflictDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/JarConflictDetector.java new file mode 100644 index 000000000..2eb38d94e --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/impl/JarConflictDetector.java @@ -0,0 +1,15 @@ +package com.fr.env.detect.impl; + +import com.fr.env.detect.base.CatchExceptionDetector; +import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.impl.converter.ClassConflictConvertor; + +/** + * created by Harrison on 2022/05/26 + **/ +public class JarConflictDetector extends CatchExceptionDetector { + + public JarConflictDetector() { + super(DetectorType.JAR_CONFLICT, new ClassConflictConvertor()); + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java new file mode 100644 index 000000000..2a744046a --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java @@ -0,0 +1,156 @@ +package com.fr.env.detect.impl; + +import com.fr.common.util.Collections; +import com.fr.design.i18n.Toolkit; +import com.fr.env.detect.base.AbstractExceptionDetector; +import com.fr.env.detect.base.DetectorConstants; +import com.fr.env.detect.base.DetectorUtil; +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.bean.ExceptionLog; +import com.fr.env.detect.bean.ExceptionSolution; +import com.fr.env.detect.bean.ExceptionTips; +import com.fr.env.detect.bean.Message; +import com.fr.general.build.BuildInfo; +import com.fr.general.build.BuildInfoManager; +import com.fr.general.build.BuildInfoOperator; +import com.fr.general.build.impl.BuildInfoOperatorImpl; +import com.fr.third.guava.collect.MapDifference; +import com.fr.third.guava.collect.Maps; +import com.fr.third.org.apache.commons.lang3.StringUtils; +import com.fr.workspace.WorkContext; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * created by Harrison on 2022/05/25 + **/ +public class JarInconsistentDetector extends AbstractExceptionDetector { + + public static final String SEPARATOR = ","; + + public JarInconsistentDetector() { + + super(DetectorType.JAR_IN_CONSISTENCE); + } + + /** + * 本地 + * 先获取 Designer, 然后对其他的 JAR 信息匹配 + *

+ * 远程 + * 两边一一对应匹配 + * + * @return 结果 + */ + @Override + public DetectorResult detect() { + + if (WorkContext.getCurrent().isLocal()) { + return detectLocal(); + } else { + return detectLocalAndRemote(); + } + } + + @NotNull + private DetectorResult detectLocalAndRemote() { + + // 检测有哪些 JAR 包, 当前是否缺少对应的 JAR 包 + BuildInfoOperator buildInfoOperator = WorkContext.getCurrent().get(BuildInfoOperator.class); + List buildInfos = buildInfoOperator.getBuildInfos(); + + // 远程情况 + List localInfos = BuildInfoManager.getInstance().getInfos(); + Map localMap = groupBy(localInfos); + + List remoteInfos = buildInfos; + Map remoteMap = groupBy(remoteInfos); + + MapDifference difference = Maps.difference(localMap, remoteMap); + // 获取本地远程不一样的部分 + Map> diffs = difference.entriesDiffering(); + + if (diffs.isEmpty()) { + return DetectorResult.normal(type()); + } + + Set inConsistentJars = diffs.keySet(); + + String message = StringUtils.join(inConsistentJars, SEPARATOR); + Message.Simple tipsMessage = new Message.Simple(Toolkit.i18nText("Fine_Design_Basic_Detect_Server") + Toolkit.i18nText(type().getTipsLocale()) + message); + + return DetectorResult.exception(type(), + new ExceptionTips(tipsMessage), + new ExceptionSolution(new Message.Link(Toolkit.i18nText(type().getSolutionLocale(),DetectorConstants.JAR_HELP_LINK) + , DetectorConstants.JAR_HELP_LINK), null), + ExceptionLog.create(Toolkit.i18nText(type().getLogLocale()) + message)); + } + + @NotNull + private DetectorResult detectLocal() { + + // 本地的获取方式 + BuildInfoOperator operator = new BuildInfoOperatorImpl(); + List buildInfos = operator.getBuildInfos(); + + // 获取设计器的 build + Optional designerBuild = buildInfos.stream() + .filter(DetectorUtil::isDesignerJar) + .map(BuildInfo::getGroupBuild) + .filter(StringUtils::isNotEmpty) + .findFirst(); + + // 如果 build + if (!designerBuild.isPresent()) { + return DetectorResult.normal(type()); + } + + // 获取所有的不一致的 build + List inConsistentInfos = buildInfos.stream() + .filter((e) -> { + // 不为空,且不相等 + return StringUtils.isNotEmpty(e.getGroupBuild()) + && !StringUtils.equals(designerBuild.get(), e.getGroupBuild()); + }) + .collect(Collectors.toList()); + + // 没有直接返回 + if (Collections.isEmpty(inConsistentInfos)) { + return DetectorResult.normal(type()); + } + + // 有的话 + List inConsistentJars = inConsistentInfos.stream() + .map(BuildInfo::getJar) + .collect(Collectors.toList()); + String message = StringUtils.join(inConsistentJars, SEPARATOR); + String tipsMessage = Toolkit.i18nText("Fine_Design_Basic_Detect_Local") + Toolkit.i18nText(type().getTipsLocale()) + message; + + return DetectorResult.exception(type(), + new ExceptionTips(new Message.Simple(tipsMessage)), + new ExceptionSolution(new Message.Link(Toolkit.i18nText(type().getSolutionLocale()), DetectorConstants.JAR_HELP_LINK), null), + ExceptionLog.create(Toolkit.i18nText(type().getLogLocale()) + message)); + } + + private Map groupBy(List localInfos) { + + Map localMap = new HashMap<>(); + for (BuildInfo localInfo : localInfos) { + String jar = localInfo.getJar(); + String groupContent = localInfo.getGroupBuild(); + // 不一致的 JAR 检测,忽视缺少的情况 + if (StringUtils.isNotEmpty(groupContent)) { + localMap.put(jar, groupContent); + } + } + return localMap; + } + +} diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java new file mode 100644 index 000000000..322c15d50 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java @@ -0,0 +1,159 @@ +package com.fr.env.detect.impl; + +import com.fr.common.util.Collections; +import com.fr.design.i18n.Toolkit; +import com.fr.env.detect.base.AbstractExceptionDetector; +import com.fr.env.detect.base.DetectorConstants; +import com.fr.env.detect.base.DetectorUtil; +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.bean.ExceptionLog; +import com.fr.env.detect.bean.ExceptionSolution; +import com.fr.env.detect.bean.ExceptionTips; +import com.fr.env.detect.bean.Message; +import com.fr.general.build.BuildInfo; +import com.fr.general.build.BuildInfoManager; +import com.fr.general.build.BuildInfoOperator; +import com.fr.general.build.impl.BuildInfoOperatorImpl; +import com.fr.third.guava.collect.Lists; +import com.fr.third.org.apache.commons.lang3.StringUtils; +import com.fr.workspace.WorkContext; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static com.fr.env.detect.base.DetectorConstants.FR_HOME_LIB; +import static com.fr.env.detect.base.DetectorConstants.WEB_LIB_PATH; + +/** + * created by Harrison on 2022/05/24 + **/ +public class JarLackDetector extends AbstractExceptionDetector { + + public JarLackDetector() { + + super(DetectorType.LACK_OF_JAR); + } + + @Override + public DetectorResult detect() { + + List lackInfos; + + // 远程 + if (!WorkContext.getCurrent().isLocal()) { + // 检测有哪些 JAR 包, 当前是否缺少对应的 JAR 包 + BuildInfoOperator buildInfoOperator = WorkContext.getCurrent().get(BuildInfoOperator.class); + // 远程情况 + List remoteInfos = buildInfoOperator.getBuildInfos(); + // 本地情况 + List localInfos = BuildInfoManager.getInstance().getInfos(); + + Set remoteSet = remoteInfos.stream() + .filter(this::isExistInfo) + .map(BuildInfo::getJar) + .collect(Collectors.toSet()); + + Predicate remoteNotContains = (e) -> !remoteSet.contains(e.getJar()); + + lackInfos = localInfos.stream() + .filter(this::isExistInfo) + // 不是设计器的 JAR + .filter((e) -> !DetectorUtil.isDesignerJar(e)) + .filter(remoteNotContains) + .collect(Collectors.toList()); + + } else { + // 本地 + // 检测有哪些 JAR 包, 当前是否缺少对应的 JAR 包 + BuildInfoOperator buildInfoOperator = new BuildInfoOperatorImpl(); + List buildInfos = buildInfoOperator.getBuildInfos(); + lackInfos = buildInfos.stream() + .filter(this::isLackInfo) + .collect(Collectors.toList()); + } + + if (Collections.isEmpty(lackInfos)) { + return DetectorResult.normal(type()); + } + + Message tipsMsg = tipsMessage(lackInfos); + ExceptionLog exceptionLog = exceptionLog(lackInfos); + + return DetectorResult.exception(type(), + new ExceptionTips(tipsMsg), + new ExceptionSolution( + new Message.Link(Toolkit.i18nText(type().getSolutionLocale()), DetectorConstants.JAR_HELP_LINK), + null), exceptionLog); + } + + private ExceptionLog exceptionLog(List lackInfos) { + + List jarInfos = lackInfos.stream() + .map(BuildInfo::getJar) + .collect(Collectors.toList()); + String message = StringUtils.join(jarInfos, ","); + return ExceptionLog.create(Toolkit.i18nText(type().getLogLocale()) + message); + } + + private boolean isExistInfo(BuildInfo e) { + + return !isLackInfo(e); + } + + private boolean isLackInfo(BuildInfo e) { + + return StringUtils.isEmpty(e.getGroupBuild()); + } + + private Message tipsMessage(List infos) { + + Map> libMap = groupByPath(infos, FR_HOME_LIB, WEB_LIB_PATH); + + String content = DetectorUtil.concatLibFormatMsg(libMap); + DetectorType type = this.type(); + String header = Toolkit.i18nText(type.getTipsLocale()); + return new Message.Simple(header + content); + } + + @NotNull + private Map> groupByPath(List infos, String homeLibPath, String webLibPath) { + + Map> libMap = new HashMap<>(); + for (BuildInfo info : infos) { + String key; + if (inHomeLib(info)) { + key = homeLibPath; + } else { + key = webLibPath; + } + libMap.compute(key, (keyA, value) -> { + if (Collections.isEmpty(value)) { + value = Lists.newArrayList(info.getJar()); + } else { + value.add(info.getJar()); + } + return value; + }); + } + return libMap; + } + + /** + * 在 %FR_HOME%\lib + * 否则都是在 %FR_HOME%\webapps\webroot\WEB-INF\lib + * + * @param info JAR 信息 + * @return 是否位于 HOME\LIB + */ + private boolean inHomeLib(BuildInfo info) { + + return DetectorUtil.isDesignerJar(info); + } + +} diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConflictConvertor.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConflictConvertor.java new file mode 100644 index 000000000..57c97326e --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConflictConvertor.java @@ -0,0 +1,212 @@ +package com.fr.env.detect.impl.converter; + +import com.fr.design.i18n.Toolkit; +import com.fr.env.detect.base.DetectorConstants; +import com.fr.env.detect.base.DetectorUtil; +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.bean.ExceptionLog; +import com.fr.env.detect.bean.ExceptionSolution; +import com.fr.env.detect.bean.ExceptionTips; +import com.fr.env.detect.thowable.ThrowableConverter; +import com.fr.stable.EncodeConstants; +import com.fr.stable.resource.ResourceLoader; +import org.jetbrains.annotations.NotNull; + +import javax.el.MethodNotFoundException; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 类抛出异常的转换 + * 原则:可以找不到,但不要误报 + * + * created by Harrison on 2022/05/24 + **/ +public class ClassConflictConvertor implements ThrowableConverter { + + public static final String CLASSES = "classes"; + public static final String SEPARATOR = "、"; + /** + * 获取对应的 JAR 包名称 + */ + private static final Pattern JAR_NAME_PATTERN = Pattern.compile("([\\w+-\\.]*\\.jar)"); + + private static final String WEB_INF_STRING = "WEB-INF"; + private static final String JAR_URL_SUFFIX = ".jar!"; + private static final String JAR_FILE_SUFFIX = ".jar"; + private static final String FILE_URL_PREFIX = "file:"; + + private final Map, ClassNameConverter> throwableMap = new HashMap<>(); + + public ClassConflictConvertor() { + + // 类异常 + this.throwableMap.put(ClassNotFoundException.class, Converter.CLASS); + this.throwableMap.put(NoClassDefFoundError.class, Converter.CLASS); + this.throwableMap.put(ClassCastException.class, Converter.CLASS); + this.throwableMap.put(IncompatibleClassChangeError.class, Converter.CLASS); + + // 方法异常 + this.throwableMap.put(MethodNotFoundException.class, Converter.METHOD); + this.throwableMap.put(NoSuchMethodException.class, Converter.METHOD); + this.throwableMap.put(NoSuchMethodError.class, Converter.METHOD); + } + + @Override + public boolean accept(Throwable throwable) { + + Throwable sign = throwable; + while (sign != null) { + if (throwableMap.containsKey(sign.getClass())) { + return true; + } + sign = sign.getCause(); + } + return false; + } + + @Override + public DetectorResult convert(Throwable throwable) { + + Iterable classNames = Collections.emptyList(); + Throwable sign = throwable; + while (sign != null) { + if (throwableMap.containsKey(sign.getClass())) { + classNames = throwableMap.get(sign.getClass()) + .converter(throwable); + } + sign = sign.getCause(); + } + + Map> libMap = new HashMap<>(); + libMap.put(DetectorConstants.FR_HOME_LIB, new ArrayList<>()); + libMap.put(DetectorConstants.WEB_LIB_PATH, new ArrayList<>()); + + Set allPath = new HashSet<>(); + for (String className : classNames) { + String classFile = convertClass2Path(className); + try { + Enumeration urls = ResourceLoader.getResources(classFile, this.getClass()); + List urlList = new ArrayList<>(); + while (urls.hasMoreElements()) { + URL url = urls.nextElement(); + urlList.add(url); + } + for (URL url : urlList) { + String file = url.getFile(); + String decodeFileStr = URLDecoder.decode(file, EncodeConstants.ENCODING_UTF_8); + if (decodeFileStr.contains(JAR_URL_SUFFIX)) { + String jarPath = decodeFileStr.substring(FILE_URL_PREFIX.length(), decodeFileStr.indexOf(JAR_URL_SUFFIX) + JAR_FILE_SUFFIX.length()); + String jar = new File(jarPath).getName(); + if (allPath.add(jar)) { + List libPath; + if (file.contains(WEB_INF_STRING)) { + libPath = libMap.get(DetectorConstants.WEB_LIB_PATH); + } else { + libPath = libMap.get(DetectorConstants.FR_HOME_LIB); + } + libPath.add(jar); + } + } + } + } catch (IOException ignore) { + } + } + + // 如果少于两个,则不需要提示 + if (allPath.size() < 2) { + return null; + } + + String msg = DetectorUtil.concatLibFormatMsg(libMap); + + DetectorType type = DetectorType.JAR_CONFLICT; + return DetectorResult.exception(type, + ExceptionTips.create(Toolkit.i18nText(type.getTipsLocale()) + msg), + ExceptionSolution.create(Toolkit.i18nText(type.getSolutionLocale()), DetectorConstants.JAR_HELP_LINK, null), + ExceptionLog.create(Toolkit.i18nText(type.getLogLocale()) + msg)); + } + + @NotNull + private String convertClass2Path(String className) { + + return "/" + className.replaceAll("\\.", "/") + ".class"; + } + + private interface ClassNameConverter { + + /** + * 将异常解析为类名,可能有多个 + * + * @param throwable 异常 + * @return 类名 + */ + Iterable converter(Throwable throwable); + } + + public enum Converter implements ClassNameConverter { + + /** + * 类匹配 + */ + CLASS { + + /** + * 匹配 111.222.333 + * 至少有一个 111. + * 至少有一个 333 + */ + private final Pattern CLASS_PATTERN = Pattern.compile("((\\w+\\.)+(\\w+))"); + + @Override + public Iterable converter(Throwable throwable) { + + String message = throwable.getMessage(); + Matcher matcher = CLASS_PATTERN.matcher(message); + List names = new ArrayList<>(); + while (matcher.find()) { + String className = matcher.group(); + names.add(className); + } + return names; + } + }, + + /** + * 方法匹配 + */ + METHOD { + + /** + * 后面有 .method()xxxx + */ + private final Pattern METHOD_PATTERN = Pattern.compile("((\\w+\\.)+(\\w+))(\\.\\w+\\(\\))"); + + @Override + public Iterable converter(Throwable throwable) { + + String message = throwable.getMessage(); + Matcher matcher = METHOD_PATTERN.matcher(message); + List names = new ArrayList<>(); + while (matcher.find()) { + String className = matcher.group(); + names.add(className); + } + return names; + } + } + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbLockedConverter.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbLockedConverter.java new file mode 100644 index 000000000..8a5e74f0b --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbLockedConverter.java @@ -0,0 +1,59 @@ +package com.fr.env.detect.impl.converter; + +import com.fr.design.i18n.Toolkit; +import com.fr.env.detect.base.DetectorConstants; +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.thowable.ThrowableConverter; +import com.fr.third.org.apache.commons.lang3.StringUtils; +import com.fr.third.org.hsqldb.HsqlException; + +/** + * created by Harrison on 2022/05/24 + **/ +public class FineDbLockedConverter implements ThrowableConverter { + + public static final String LOCK_FILE = "lockFile"; + + @Override + public boolean accept(Throwable throwable) { + + Throwable sign = throwable; + while (sign != null) { + if (sign.getClass() == HsqlException.class) { + return true; + } + sign = sign.getCause(); + } + return false; + } + + /** + * 检测 FineDB 是否锁住 + * + * @param throwable 异常 + * @return 结果 + */ + @Override + public DetectorResult convert(Throwable throwable) { + + Throwable sign = throwable; + while (sign.getClass() != HsqlException.class) { + sign = sign.getCause(); + } + + DetectorType type = DetectorType.FINE_DB_LOCKED; + + String message = sign.getMessage(); + if (StringUtils.containsIgnoreCase(message, LOCK_FILE)) { + + return DetectorResult.builder() + .withType(type) + .withTips(Toolkit.i18nText(type.getTipsLocale())) + .withSolution(Toolkit.i18nText(type.getSolutionLocale(),DetectorConstants.FINE_DB_HELP_LINK), DetectorConstants.FINE_DB_HELP_LINK) + .withLog(Toolkit.i18nText(type.getLogLocale())) + .build(); + } + return null; + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbPermissionConverter.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbPermissionConverter.java new file mode 100644 index 000000000..dd350abdf --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbPermissionConverter.java @@ -0,0 +1,55 @@ +package com.fr.env.detect.impl.converter; + +import com.fr.design.i18n.Toolkit; +import com.fr.env.detect.base.DetectorConstants; +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.thowable.ThrowableConverter; +import com.fr.third.org.apache.commons.lang3.StringUtils; +import com.fr.third.org.hsqldb.HsqlException; + +/** + * HSQL 下的权限检测 + * + * created by Harrison on 2022/05/24 + **/ +public class FineDbPermissionConverter implements ThrowableConverter { + + public static final String PERMISSION = "permission"; + + @Override + public boolean accept(Throwable throwable) { + + Throwable sign = throwable; + while (sign != null) { + if (sign.getClass() == HsqlException.class) { + return true; + } + sign = sign.getCause(); + } + return false; + } + + @Override + public DetectorResult convert(Throwable throwable) { + + Throwable sign = throwable; + while (sign.getClass() != HsqlException.class) { + sign = sign.getCause(); + } + + DetectorType type = DetectorType.FINE_DB_PERMISSION; + String message = sign.getMessage(); + if (StringUtils.containsIgnoreCase(message, PERMISSION)) { + + return DetectorResult.builder() + .withType(type) + .withTips(Toolkit.i18nText(type.getTipsLocale())) + .withSolution(Toolkit.i18nText(type.getSolutionLocale(), DetectorConstants.FINE_DB_HELP_LINK), + DetectorConstants.FINE_DB_HELP_LINK) + .withLog(type.getLogLocale()) + .build(); + } + return null; + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableConverter.java b/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableConverter.java new file mode 100644 index 000000000..6843cb2a8 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableConverter.java @@ -0,0 +1,28 @@ +package com.fr.env.detect.thowable; + +import com.fr.env.detect.bean.DetectorResult; +import org.jetbrains.annotations.Nullable; + +/** + * created by Harrison on 2022/05/13 + **/ +public interface ThrowableConverter { + + /** + * 是否支持该异常 + * + * @param throwable 异常 + * @return 是/否 + */ + boolean accept(Throwable throwable); + + /** + * 将异常转化为结果 + * + * @param throwable 异常 + * @return 转化结果 + */ + @Nullable + DetectorResult convert(Throwable throwable); + +} diff --git a/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableLogAppender.java b/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableLogAppender.java new file mode 100644 index 000000000..3348052f4 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableLogAppender.java @@ -0,0 +1,72 @@ +package com.fr.env.detect.thowable; + +import com.fr.general.FRLogger; +import com.fr.log.LogHandler; +import com.fr.third.apache.logging.log4j.Level; +import com.fr.third.apache.logging.log4j.core.Filter; +import com.fr.third.apache.logging.log4j.core.Layout; +import com.fr.third.apache.logging.log4j.core.LogEvent; +import com.fr.third.apache.logging.log4j.core.appender.AbstractAppender; +import com.fr.third.apache.logging.log4j.core.config.Property; + +import java.io.Serializable; + +/** + * created by Harrison on 2022/05/13 + **/ +public class ThrowableLogAppender extends AbstractAppender { + + public ThrowableLogAppender(String name, Filter filter, Layout layout, boolean ignoreExceptions, Property[] properties) { + super(name, filter, layout, ignoreExceptions, properties); + } + + public static ThrowableLogAppender getInstance() { + return ExceptionLogAppenderHolder.INSTANCE; + } + + private static class ExceptionLogAppenderHolder { + private static final ThrowableLogAppender INSTANCE = new ThrowableLogAppender("exception-detect-appender", null, null, false, null); + } + + private final LogHandler logHandler = toHandler(); + + @Override + public void append(LogEvent logEvent) { + + try { + if (logEvent.getLevel() == Level.ERROR) { + Throwable thrown = logEvent.getThrown(); + if (thrown != null) { + ThrowableStore.getInstance().add(thrown); + } + } + } catch (Throwable ignore) { + } + } + + private LogHandler toHandler() { + + final ThrowableLogAppender appender = this; + appender.start(); + LogHandler handler = new LogHandler() { + @Override + public ThrowableLogAppender getHandler() { + return appender; + } + }; + return handler; + } + + + public void enable() { + + // 初始化一下,别出问题 + logHandler.getHandler().start(); + FRLogger.getLogger().addExtendLogAppender(logHandler); + } + + public void disable() { + + FRLogger.getLogger().removeExtendLogAppender(logHandler); + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableStore.java b/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableStore.java new file mode 100644 index 000000000..346e21899 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableStore.java @@ -0,0 +1,36 @@ +package com.fr.env.detect.thowable; + +import java.util.ArrayList; +import java.util.List; + +/** + * 异常存储中心 + * + * created by Harrison on 2022/05/13 + **/ +public class ThrowableStore { + + public static ThrowableStore getInstance() { + return ThrowableStoreHolder.INSTANCE; + } + + private static class ThrowableStoreHolder { + private static final ThrowableStore INSTANCE = new ThrowableStore(); + } + + private List exceptions = new ArrayList<>(); + + public void add(Throwable throwable) { + if (throwable != null) { + exceptions.add(throwable); + } + } + + public List getAll() { + return exceptions; + } + + public void reset() { + exceptions.clear(); + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/ui/DetectorErrorDialog.java b/designer-base/src/main/java/com/fr/env/detect/ui/DetectorErrorDialog.java new file mode 100644 index 000000000..e636524c8 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/ui/DetectorErrorDialog.java @@ -0,0 +1,159 @@ +package com.fr.env.detect.ui; + +import com.fr.base.svg.IconUtils; +import com.fr.design.RestartHelper; +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.i18n.Toolkit; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.utils.ColorUtils; +import com.fr.design.utils.DesignUtils; +import com.fr.design.utils.gui.GUICoreUtils; +import com.fr.env.detect.base.DetectorUtil; +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.ExceptionSolution; +import com.fr.env.detect.bean.ExceptionTips; +import com.fr.env.detect.bean.Message; +import com.fr.exit.DesignerExiter; + +import javax.swing.BorderFactory; +import javax.swing.JDialog; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.ScrollPaneConstants; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.List; + +/** + * 未知错误框 + * todo 其实这里可以将多个 error-dialog 抽象在一起的。 时间不够, 简单写写 + * 见 {@link com.fr.design.dialog.ErrorDialog} + * + * created by Harrison on 2022/05/29 + **/ +public class DetectorErrorDialog extends JDialog implements ActionListener { + + public static final float FONT_BOLD_HEIGHT = 16f; + private UIButton okButton; + private UIButton restartButton; + + public DetectorErrorDialog(Frame parent, List results) { + + super(parent, true); + + // 可能是还没初始化 UI 的时候出现的问题,初始化一下 UI + DesignUtils.initLookAndFeel(); + + JPanel northPane = FRGUIPaneFactory.createBorderLayout_L_Pane(); + JPanel headerPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + headerPane.setLayout(new BorderLayout(15, 0)); + + UILabel iconLabel = new UILabel(IconUtils.readIcon("/com/fr/design/standard/reminder/reminder_warning_window.svg")); + headerPane.add(iconLabel, BorderLayout.WEST); + + JPanel messagePane = FRGUIPaneFactory.createVerticalFlowLayout_S_Pane(true); + { + UILabel boldHeader = new UILabel(Toolkit.i18nText("Fine-Design_Error_Start_Apology_Message")); + Font font = boldHeader.getFont(); + Font boldFont = font.deriveFont(FONT_BOLD_HEIGHT); + boldHeader.setFont(boldFont); + messagePane.add(boldHeader); + + UILabel description = new UILabel(Toolkit.i18nText("Fine-Design_Send_Report_To_Us")); + messagePane.add(description); + } + headerPane.add(messagePane, BorderLayout.CENTER); + + northPane.add(headerPane); + + JPanel centerPane = FRGUIPaneFactory.createBorderLayout_L_Pane(); + centerPane.setLayout(new BorderLayout(0, 5)); + + UILabel detailDesc = new UILabel(Toolkit.i18nText("Fine-Design_Problem_Detail_Message")); + centerPane.add(detailDesc, BorderLayout.NORTH); + + JPanel detailPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + detailPanel.setBorder(BorderFactory.createEmptyBorder(0, 20, 10, 10)); + detailPanel.setLayout(new BorderLayout(0, 8)); + + for (DetectorResult result : results) { + JPanel detailItemPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); + detailItemPanel.setLayout(new BorderLayout(0, 8)); + ExceptionTips tips = result.getTips(); + + UILabel template = new UILabel(); + template.setBackground(Color.white); + + if (tips != null) { + Message tipsMsg = tips.getMessage(); + detailItemPanel.add(DetectorUtil.convert2TextComponent(tipsMsg, template), BorderLayout.NORTH); + } + ExceptionSolution solution = result.getSolution(); + if (solution != null) { + Message solutionMsg = solution.getMessage(); + detailItemPanel.add(DetectorUtil.convert2TextComponent(solutionMsg, template), BorderLayout.CENTER); + } + detailPanel.add(detailItemPanel, BorderLayout.CENTER); + } + + JScrollPane detailPanelWrapper = new JScrollPane(detailPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + ColorUtils.syncBackground(detailPanelWrapper, Color.WHITE); + centerPane.add(detailPanelWrapper, BorderLayout.CENTER); + + JPanel southPane = FRGUIPaneFactory.createBorderLayout_L_Pane(); + JPanel controlPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + JPanel buttonPane = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 0)); + okButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Ok")); + okButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + okEvent(); + } + }); + buttonPane.add(okButton); + restartButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Restart")); + restartButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + restartEvent(); + } + }); + buttonPane.add(restartButton); + controlPane.add(buttonPane, BorderLayout.EAST); + southPane.add(controlPane); + + this.setTitle(Toolkit.i18nText("Fine-Design_Error_Start_Report")); + this.setResizable(false); + this.add(northPane, BorderLayout.NORTH); + this.add(centerPane, BorderLayout.CENTER); + this.add(southPane, BorderLayout.SOUTH); + this.setSize(new Dimension(650, 500)); + GUICoreUtils.centerWindow(this); + + } + + @Override + public void actionPerformed(ActionEvent e) { + dispose(); + } + + protected void okEvent() { + + dispose(); + DesignerExiter.getInstance().execute(); + } + + protected void restartEvent() { + + dispose(); + RestartHelper.restart(); + } + +} diff --git a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorAction.java b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorAction.java new file mode 100644 index 000000000..7992648f0 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorAction.java @@ -0,0 +1,28 @@ +package com.fr.env.detect.ui; + +import com.fr.design.actions.UpdateAction; +import com.fr.design.i18n.Toolkit; +import com.fr.design.mainframe.DesignerContext; + +import java.awt.event.ActionEvent; + +/** + * 工具栏里面的行为 + * + * created by Harrison on 2022/05/29 + **/ +public class EnvDetectorAction extends UpdateAction { + + public EnvDetectorAction() { + + this.setName(Toolkit.i18nText("Fine-Design_Basic_Detect_Toolbar_Title")); + this.setSmallIcon("com/fr/env/detect/detect_normal.svg"); + } + + @Override + public void actionPerformed(ActionEvent e) { + + EnvDetectorDialog dialog = new EnvDetectorDialog(DesignerContext.getDesignerFrame()); + dialog.setVisible(true); + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java new file mode 100644 index 000000000..ed03d78cc --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java @@ -0,0 +1,569 @@ +package com.fr.env.detect.ui; + +import com.fr.base.svg.IconUtils; +import com.fr.design.components.notification.NotificationDialog; +import com.fr.design.components.notification.NotificationDialogProperties; +import com.fr.design.components.notification.NotificationModel; +import com.fr.design.components.table.TablePanel; +import com.fr.design.constants.DesignerColor; +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.gui.ibutton.UIButtonUI; +import com.fr.design.gui.icheckbox.UICheckBox; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.i18n.Toolkit; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.ui.util.UIUtil; +import com.fr.design.utils.gui.GUICoreUtils; +import com.fr.design.utils.gui.GUIPaintUtils; +import com.fr.env.detect.base.DetectorBridge; +import com.fr.env.detect.base.DetectorUtil; +import com.fr.env.detect.base.EnvDetectorConfig; +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorStatus; +import com.fr.env.detect.bean.DetectorType; +import com.fr.log.FineLoggerFactory; +import org.jetbrains.annotations.NotNull; + +import javax.swing.BorderFactory; +import javax.swing.ImageIcon; +import javax.swing.JDialog; +import javax.swing.JPanel; +import javax.swing.SwingWorker; +import javax.swing.plaf.ButtonUI; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.net.URL; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * created by Harrison on 2022/05/26 + **/ +public class EnvDetectorDialog extends JDialog { + + private static final ImageIcon LOADING_ICON = getLoadingIcon(); + private static final int TIMEOUT = 1000; + + private static final Color SUCCESS_COLOR = new Color(22, 193, 83); + private static final Color DETAIL_FONT_COLOR = new Color(65, 155, 249); + + private final JPanel body; + + private final JPanel headerPanel; + private UIButton detectButton; + private JPanel resultSummaryPane; + + private final TablePanel tablePanel; + + private final JPanel tailPanel; + + /* 数据 model */ + + private EnvDetectorModel model; + + /* 流程 model */ + + /** + * 默认是第一个要检测 + */ + private int currentDetectIndex = 0; + + private EnvDetectorButtonStatus buttonStatus = EnvDetectorButtonStatus.START; + + private SwingWorker detectWorker = null; + + /* config model */ + + private boolean detectOpen = EnvDetectorConfig.getInstance().isEnabled(); + + public EnvDetectorDialog(Frame owner) { + this(owner, null); + } + + public EnvDetectorDialog(Frame owner, EnvDetectorModel model) { + super(owner); + + configProperties(); + + if (model == null) { + this.model = new EnvDetectorModel(); + } else { + this.model = model; + } + + this.body = FRGUIPaneFactory.createBorderLayout_L_Pane(); + Color backgroundColor = new Color(240, 240, 243, 1); + this.body.setBackground( backgroundColor); + + this.headerPanel = createHeaderPanel(); + body.add(headerPanel, BorderLayout.NORTH); + + this.tablePanel = createTablePanel(); + body.add(tablePanel, BorderLayout.CENTER); + + /* tailPanel*/ + this.tailPanel = createTailPanel(); + body.add(tailPanel, BorderLayout.SOUTH); + + add(body); + + Dimension preferredSize = body.getPreferredSize(); + setSize(preferredSize); + + repaint(); + pack(); + + GUICoreUtils.centerWindow(this); + } + + /* header */ + + @NotNull + private JPanel createHeaderPanel() { + + JPanel headerPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + headerPanel.setBorder(BorderFactory.createEmptyBorder(5, 0, 12, 0)); + this.detectButton = new UIButton(buttonStatus.getDesc()) { + @Override + public ButtonUI getUI() { + + return new UIButtonUI() { + @Override + protected void doExtraPainting(UIButton b, Graphics2D g2d, int w, int h, String selectedRoles) { + if (isPressed(b) && b.isPressedPainted()) { + GUIPaintUtils.fillPressed(g2d, 0, 0, w, h, b.isRoundBorder(), b.getRectDirection(), b.isDoneAuthorityEdited(selectedRoles), + DesignerColor.Button.Primary.PRESSED); + } else if (isRollOver(b)) { + GUIPaintUtils.fillRollOver(g2d, 0, 0, w, h, b.isRoundBorder(), b.getRectDirection(), b.isDoneAuthorityEdited(selectedRoles), b.isPressedPainted(), + DesignerColor.Button.Primary.HOVER); + } else if (b.isNormalPainted()) { + GUIPaintUtils.fillNormal(g2d, 0, 0, w, h, b.isRoundBorder(), b.getRectDirection(), b.isDoneAuthorityEdited(selectedRoles), b.isPressedPainted(), + DesignerColor.Button.Primary.NORMAL); + } + } + }; + } + }; + detectButton.setForeground(Color.WHITE); + detectButton.addActionListener(event -> { + if (buttonStatus.isNotExecuting()) { + startDetecting(); + } else { + stopDetecting(detectButton); + } + }); + detectButton.setPreferredSize(new Dimension(68, 20)); + detectButton.setBorderPainted(false); + detectButton.setContentAreaFilled(false); + headerPanel.add(detectButton, BorderLayout.WEST); + + return headerPanel; + } + + private void startDetecting() { + + // 重新检测的时候需要处理一些逻辑 + if (buttonStatus == EnvDetectorButtonStatus.A_NEW) { + reInit(); + } + // 执行前 + buttonStatus = buttonStatus.next(); + UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refreshHeader); + detectWorker = new SwingWorker() { + + @Override + protected Void doInBackground() throws Exception { + List items = model.getItems(); + // 执行刷新 + for (int i = currentDetectIndex; i < items.size(); i++) { + + // 看一下是否关闭了, 有可能已经关闭了。 + if (buttonStatus.isNotExecuting()) { + return null; + } + + // 刷新一下面板-开始执行啦 + UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refreshBody); + + EnvDetectorItem item = items.get(i); + DetectorType type = item.getType(); + + // 执行检测, UI-当前在检测中 + DetectorResult result = UIUtil.waitUntil( + () -> DetectorBridge.getInstance().detect(type), + TIMEOUT, TimeUnit.MILLISECONDS); + // 获取结果 + item.setResult(result); + + // 更新UI + // 只有还在运行中,才会真正的刷新面板 + if (buttonStatus.isExecuting()) { + // 在刷新一下面板 + UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refreshBody); + currentDetectIndex++; + } + + } + return null; + } + + @Override + protected void done() { + + try { + this.get(); + if (buttonStatus.isExecuting()) { + // 执行结束 + buttonStatus = EnvDetectorButtonStatus.A_NEW; + UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refreshHeader); + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error("detect failed", e); + } + } + }; + // 开始执行 + detectWorker.execute(); + } + + private void reInit() { + currentDetectIndex = 0; + for (EnvDetectorItem e : model.getItems()) { + e.setResult(null); + } + // 刷新一下面板-开始执行啦 + UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refreshBody); + } + + private void stopDetecting(UIButton detectButton) { + + buttonStatus = buttonStatus.next(); + + // 先停止 + detectWorker.cancel(false); + // 更改-UI + // 执行中 + UIUtil.invokeLaterIfNeeded(() -> { + // 刷新按钮 + detectButton.setText(buttonStatus.getDesc()); + // 刷新面板 + refreshBody(); + }); + } + + private void updateHeaderPanel() { + + // 刷新按钮 + detectButton.setText(buttonStatus.getDesc()); + if (buttonStatus == EnvDetectorButtonStatus.A_NEW) { + this.resultSummaryPane = new JPanel(); + this.resultSummaryPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0)); + this.resultSummaryPane.setLayout(new BorderLayout(5, 0)); + Boolean success = model.getResults() + .map((e) -> { + if (e != null && e.getStatus() == DetectorStatus.NORMAL) { + return Boolean.TRUE; + } + return Boolean.FALSE; + }).reduce((a, b) -> a && b) + .orElse(Boolean.FALSE); + + if (success) { + resultSummaryPane.add(new UILabel(Toolkit.i18nText("Fine-Design_Basic_Detect_Result_Label")), BorderLayout.WEST); + UILabel successLabel = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Detect_Result_Success")); + successLabel.setForeground(SUCCESS_COLOR); + resultSummaryPane.add(successLabel, BorderLayout.CENTER); + } else { + resultSummaryPane.add(new UILabel(Toolkit.i18nText("Fine-Design_Basic_Detect_Result_Label")), BorderLayout.WEST); + UILabel resultLabel = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Detect_Result_Error")); + resultLabel.setForeground(Color.RED); + resultSummaryPane.add(resultLabel, BorderLayout.CENTER); + } + this.headerPanel.add(BorderLayout.CENTER, resultSummaryPane); + } else { + if (resultSummaryPane != null) { + this.headerPanel.remove(resultSummaryPane); + } + } + } + + /* table */ + + + @NotNull + private TablePanel createTablePanel() { + + TablePanel tablePanel = new TablePanel(18, 3); + tablePanel.updateHeaders(new String[] { + Toolkit.i18nText("Fine-Design_Basic_Detect_Kind"), + Toolkit.i18nText("Fine-Design_Basic_Detect_Item"), + Toolkit.i18nText("Fine-Design_Basic_Detect_Result")}); + + updateTable(tablePanel); + + return tablePanel; + } + + private void updateTable(TablePanel tablePanel) { + + Map> itemMap = model.getItemMap(); + + // 行号, 这边更新是通过 行/列 。 不是索引 + int row = 1; + for (Map.Entry> entry : itemMap.entrySet()) { + + DetectorType.Kind kind = entry.getKey(); + List items = entry.getValue(); + for (int i = 0; i < items.size(); i++) { + if (i == 0) { + tablePanel.updateCell(row, 1, kind.getDescription()); + } + EnvDetectorItem item = items.get(i); + tablePanel.updateCell(row, 2, new UILabel(item.getDescription())); + DetectorResult result = item.getResult(); + + int detectRow = currentDetectIndex + 1; + + if (result == null) { + // 处于非正在检测状态 或者 索引不等于当前行号的时候 + UILabel label; + if (buttonStatus.isExecuting() && detectRow == row) { + // 正在检测索引 + label = new UILabel(LOADING_ICON, UILabel.LEADING); + } else { + label = new UILabel("-"); + } + tablePanel.updateCell(row, 3, label); + } else { + Component resultComponent = createResultComponent(result); + tablePanel.updateCell(row, 3, resultComponent); + } + row++; + } + } + } + + private Component createResultComponent(DetectorResult result) { + + JPanel statusPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + if (result.getStatus() == DetectorStatus.NORMAL) { + statusPanel.add(new UILabel(IconUtils.readIcon("/com/fr/design/standard/reminder/reminder_success.svg")), BorderLayout.WEST); + statusPanel.add(new UILabel(Toolkit.i18nText("Fine-Design_Basic_Normal")), BorderLayout.CENTER); + } else { + JPanel infoPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + { + infoPanel.add(new UILabel(IconUtils.readIcon("/com/fr/design/standard/reminder/reminder_error.svg")), BorderLayout.WEST); + infoPanel.add(new UILabel(Toolkit.i18nText("Fine-Design_Basic_Exception")), BorderLayout.CENTER); + } + statusPanel.add(infoPanel, BorderLayout.WEST); + + // 如果结果是检测出的异常,则出现详细信息。 + if (result.getStatus() == DetectorStatus.EXCEPTION) { + JPanel detailPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + { + detailPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0)); + UILabel detailLabel = new UILabel(Toolkit.i18nText("Fine_Designer_Look_Detail")); + detailLabel.setForeground(DETAIL_FONT_COLOR); + detailLabel.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent mouseEvent) { + NotificationDialogProperties properties = new NotificationDialogProperties((Frame) EnvDetectorDialog.this.getOwner(), Toolkit.i18nText("Fine-Design_Basic_Detect_Notification_Title")); + Stream results = model.getResults(); + List notificationModels = results + .filter(Objects::nonNull) + .filter((e) -> e.getStatus() == DetectorStatus.EXCEPTION) + .map(DetectorUtil::convert2Notification) + .collect(Collectors.toList()); + + NotificationDialog dialog = new NotificationDialog(properties, notificationModels); + dialog.open(); + } + }); + detailPanel.add(detailLabel, BorderLayout.CENTER); + } + statusPanel.add(detailPanel, BorderLayout.CENTER); + } + + } + return statusPanel; + } + + /* tail */ + + @NotNull + private JPanel createTailPanel() { + + JPanel tailPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + tailPanel.setBorder(BorderFactory.createEmptyBorder(20, 0, 0, 0)); + + JPanel configPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + { + UICheckBox checkBox = new UICheckBox(); + checkBox.setSelected(this.detectOpen); + checkBox.addChangeListener(e -> { + UICheckBox source = (UICheckBox) e.getSource(); + EnvDetectorDialog.this.detectOpen = source.isSelected(); + }); + configPanel.add(checkBox, BorderLayout.WEST); + UILabel description = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Detect_Switch")); + configPanel.add(description, BorderLayout.EAST); + } + tailPanel.add(configPanel, BorderLayout.WEST); + + JPanel actionsPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + actionsPanel.setLayout(FRGUIPaneFactory.createM_BorderLayout()); + { + UIButton confirmButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Confirm")); + confirmButton.addActionListener((e) -> { + setVisible(false); + dispose(); + // 配置处理 + EnvDetectorConfig.getInstance().setEnabled(this.detectOpen); + }); + actionsPanel.add(confirmButton, BorderLayout.WEST); + + UIButton cancelButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Cancel")); + cancelButton.addActionListener((e) -> { + setVisible(false); + dispose(); + }); + actionsPanel.add(cancelButton, BorderLayout.EAST); + } + tailPanel.add(actionsPanel, BorderLayout.EAST); + return tailPanel; + } + + private void refreshHeader() { + + updateHeaderPanel(); + pack(); + repaint(); + } + + private void refreshBody() { + + updateTable(this.tablePanel); + pack(); + repaint(); + } + + private static ImageIcon getLoadingIcon() { + + URL resource = EnvDetectorDialog.class.getResource("/com/fr/design/standard/loading/loading-120.gif"); + if (resource == null) { + return null; + } + ImageIcon loadingIcon = new ImageIcon(resource); + int width = 16; + int height = 16; + loadingIcon.setImage(loadingIcon.getImage().getScaledInstance(width, height, Image.SCALE_DEFAULT)); + return loadingIcon; + } + + private void configProperties() { + + setTitle(Toolkit.i18nText("Fine-Design_Basic_Detect_Title")); + setModal(false); + setFocusable(false); + setAutoRequestFocus(false); + setResizable(false); + } + + /** + * 按钮的当前状态 + */ + private enum EnvDetectorButtonStatus { + + /** + * 开始 -> 停止 + */ + START("Fine-Design_Basic_Detect_Start") { + @Override + public EnvDetectorButtonStatus next() { + return STOP; + } + }, + + /** + * 停止 -> 继续 + */ + STOP("Fine-Design_Basic_Detect_Stop") { + @Override + public EnvDetectorButtonStatus next() { + return CONTINUE; + } + }, + + /** + * 继续 -> 停止 + */ + CONTINUE("Fine-Design_Basic_Detect_Continue") { + @Override + public EnvDetectorButtonStatus next() { + return STOP; + } + }, + + /** + * 重新 -> 停止 + */ + A_NEW("Fine-Design_Basic_Detect_A_New") { + @Override + public EnvDetectorButtonStatus next() { + return STOP; + } + } + + ; + + private String descLocale; + + EnvDetectorButtonStatus(String descLocale) { + + this.descLocale = descLocale; + } + + public String getDesc() { + + return Toolkit.i18nText(descLocale); + } + + /** + * 在执行中 + * + * @return 是/否 + */ + public boolean isExecuting() { + + return this == EnvDetectorButtonStatus.STOP; + }; + + /** + * 不在执行中 + * + * @return 是/否 + */ + public boolean isNotExecuting() { + + return !isExecuting(); + } + + public abstract EnvDetectorButtonStatus next(); + } + + private class EnvDetectorHeaderPanel extends JPanel { + + + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorItem.java b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorItem.java new file mode 100644 index 000000000..0f9075313 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorItem.java @@ -0,0 +1,40 @@ +package com.fr.env.detect.ui; + +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorType; + +/** + * 环境检测条目 + * + * created by Harrison on 2022/05/27 + **/ +public class EnvDetectorItem { + + private DetectorType type; + + private DetectorResult result; + + public EnvDetectorItem(DetectorType detectorType) { + this.type = detectorType; + } + + public DetectorType getType() { + return type; + } + + public String getKind() { + return this.type.getKind().getDescription(); + } + + public String getDescription() { + return this.type.getDescription(); + } + + public DetectorResult getResult() { + return result; + } + + public void setResult(DetectorResult result) { + this.result = result; + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorModel.java b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorModel.java new file mode 100644 index 000000000..457734d0b --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorModel.java @@ -0,0 +1,70 @@ +package com.fr.env.detect.ui; + +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorType; +import com.fr.third.guava.collect.Lists; + +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 环境检测数据格式 + * + * created by Harrison on 2022/05/27 + **/ +public class EnvDetectorModel { + + /** + * 类型 -> list [{type-result}] + */ + private final Map> itemMap = new LinkedHashMap<>(); + + public EnvDetectorModel() { + + itemMap.put(DetectorType.Kind.JAR, Lists.newArrayList( + new EnvDetectorItem(DetectorType.LACK_OF_JAR), + new EnvDetectorItem(DetectorType.JAR_IN_CONSISTENCE), + new EnvDetectorItem(DetectorType.JAR_CONFLICT))); + + itemMap.put(DetectorType.Kind.FINE_DB, Lists.newArrayList( + new EnvDetectorItem(DetectorType.FINE_DB_LOCKED), + new EnvDetectorItem(DetectorType.FINE_DB_PERMISSION), + new EnvDetectorItem(DetectorType.FINE_DB_DIRTY))); + } + + public void update(DetectorType type, DetectorResult result) { + + List items = itemMap.get(type.getKind()); + items.stream() + .filter((e) -> e.getType() == type) + .findFirst() + .ifPresent((e) -> { + e.setResult(result); + }); + } + + public Stream getResults() { + + return getItems().stream() + .map(EnvDetectorItem::getResult); + } + + public List getItems() { + + return itemMap.values().stream() + .flatMap(Collection::stream) + .collect(Collectors.toList()); + } + + public Map> getItemMap() { + + return this.itemMap; + } + +} + + diff --git a/designer-base/src/main/java/com/fr/exit/DesignerExiter.java b/designer-base/src/main/java/com/fr/exit/DesignerExiter.java index cda2e015b..babadc5fe 100644 --- a/designer-base/src/main/java/com/fr/exit/DesignerExiter.java +++ b/designer-base/src/main/java/com/fr/exit/DesignerExiter.java @@ -1,10 +1,26 @@ package com.fr.exit; +import com.fr.common.util.Collections; +import com.fr.design.RestartHelper; +import com.fr.design.dialog.ErrorDialog; import com.fr.design.env.DesignerWorkspaceGenerator; +import com.fr.design.i18n.Toolkit; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.DesignerFrame; +import com.fr.design.mainframe.messagecollect.StartErrorMessageCollector; +import com.fr.design.mainframe.messagecollect.entity.DesignerErrorMessage; import com.fr.design.monitor.DesignerLifecycleMonitorContext; +import com.fr.env.detect.EnvDetectorCenter; +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.ui.DetectorErrorDialog; +import com.fr.log.FineLoggerFactory; import com.fr.process.engine.core.FineProcessContext; import com.fr.process.engine.core.FineProcessEngineEvent; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + /** * @author hades * @version 10.0 @@ -17,7 +33,79 @@ public class DesignerExiter { public static DesignerExiter getInstance() { return INSTANCE; } - + + /** + * 预期外的退出 + * 首先检测是否有检测到的异常。如果没有,则运行默认行为 + * + * @param defaultAction 默认行为 + */ + public void exitUnexpectedly(Runnable defaultAction) { + + // 尝试进行检测 + List results = runAndGet(() -> EnvDetectorCenter.getInstance().terminateUnexpectedly(), ArrayList::new); + try { + if (!Collections.isEmpty(results)) { + showNewExitDialog(results); + } + } finally { + // 正常的话上面会直接退出, system.exit(0) + // 只有异常,或者不命中,才会走到这里 + defaultAction.run(); + } + } + + public void exit(Throwable throwable) { + + doThrowableAction(() -> { + FineLoggerFactory.getLogger().error(throwable.getMessage(), throwable); + }, () -> { + throwable.printStackTrace(System.err); + }); + + doThrowableAction(() -> { + StartErrorMessageCollector.getInstance().record(DesignerErrorMessage.UNEXCEPTED_START_FAILED.getId(), + DesignerErrorMessage.UNEXCEPTED_START_FAILED.getMessage(), + throwable.getMessage()); + }); + + // 尝试进行检测 + List results = runAndGet(() -> EnvDetectorCenter.getInstance().terminate(throwable), ArrayList::new); + + if (Collections.isEmpty(results)) { + // 为空,则 + showOldExitDialog(throwable); + } else { + showNewExitDialog(results); + } + } + + private void showNewExitDialog(List results) { + DesignerFrame designerFrame = DesignerContext.getDesignerFrame(); + DetectorErrorDialog errorDialog = new DetectorErrorDialog(designerFrame, results); + errorDialog.setVisible(true); + } + + private void showOldExitDialog(Throwable throwable) { + + ErrorDialog dialog = new ErrorDialog(null, Toolkit.i18nText("Fine-Design_Error_Start_Apology_Message"), + Toolkit.i18nText("Fine-Design_Error_Start_Report"), + throwable.getMessage()) { + @Override + protected void okEvent() { + dispose(); + DesignerExiter.getInstance().execute(); + } + + @Override + protected void restartEvent() { + dispose(); + RestartHelper.restart(); + } + }; + dialog.setVisible(true); + } + public void execute() { DesignerLifecycleMonitorContext.getMonitor().beforeStop(); beforeExit(); @@ -30,4 +118,31 @@ public class DesignerExiter { private void beforeExit() { DesignerWorkspaceGenerator.stop(); } + + /* 忽视异常的调用方法 */ + + private void doThrowableAction(Runnable runnable) { + doThrowableAction(runnable, null); + } + + private void doThrowableAction(Runnable runnable, Runnable defaultRunnable) { + + try { + runnable.run(); + } catch (Throwable ignore) { + if (defaultRunnable != null) { + defaultRunnable.run(); + } + } + } + + private T runAndGet(Supplier supplier, Supplier defaultCallable) { + + try { + return supplier.get(); + } catch (Exception ignore) { + return defaultCallable.get(); + } + } + } diff --git a/designer-base/src/main/java/com/fr/file/StashedFILE.java b/designer-base/src/main/java/com/fr/file/StashedFILE.java index 48f00f95b..a73dd612d 100644 --- a/designer-base/src/main/java/com/fr/file/StashedFILE.java +++ b/designer-base/src/main/java/com/fr/file/StashedFILE.java @@ -87,6 +87,14 @@ public class StashedFILE extends AbstractFILE { return false; } + /** + * 获取内部FILE对象 + * @return + */ + public FILE getInsideFILE() { + return file; + } + @Override public String toString() { return FILEFactory.MEM_PREFIX + getName(); diff --git a/designer-base/src/main/java/com/fr/start/BaseDesigner.java b/designer-base/src/main/java/com/fr/start/BaseDesigner.java index 360e9cdfa..95f27442d 100644 --- a/designer-base/src/main/java/com/fr/start/BaseDesigner.java +++ b/designer-base/src/main/java/com/fr/start/BaseDesigner.java @@ -32,7 +32,6 @@ import com.fr.process.ProcessEventPipe; import com.fr.process.engine.core.CarryMessageEvent; import com.fr.process.engine.core.FineProcessContext; import com.fr.stable.OperatingSystem; - import com.fr.start.event.LazyStartupEvent; import com.fr.workspace.base.WorkspaceStatus; @@ -138,7 +137,7 @@ public abstract class BaseDesigner extends ToolBarMenuDock { if (!isException) { showDesignerFrame(true); } else { - DesignerExiter.getInstance().execute(); + DesignerExiter.getInstance().exit(e); } } } diff --git a/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_disable_left.svg b/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_disable_left.svg new file mode 100755 index 000000000..0a20cf97a --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_disable_left.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_disable_right.svg b/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_disable_right.svg new file mode 100755 index 000000000..9d111eff9 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_disable_right.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_enable_left.svg b/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_enable_left.svg new file mode 100755 index 000000000..4ab4c796e --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_enable_left.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_enable_right.svg b/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_enable_right.svg new file mode 100755 index 000000000..040db18bd --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_enable_right.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/loading/loading-120.gif b/designer-base/src/main/resources/com/fr/design/standard/loading/loading-120.gif new file mode 100644 index 000000000..99f95a0fa Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/standard/loading/loading-120.gif differ diff --git a/designer-base/src/main/resources/com/fr/design/standard/loading/loading-64.gif b/designer-base/src/main/resources/com/fr/design/standard/loading/loading-64.gif new file mode 100644 index 000000000..9db4c738f Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/standard/loading/loading-64.gif differ diff --git a/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_error.svg b/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_error.svg new file mode 100755 index 000000000..6ef70206f --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_error.svg @@ -0,0 +1,4 @@ + + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_success.svg b/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_success.svg new file mode 100755 index 000000000..c26d221b5 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_success.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_warning.svg b/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_warning.svg new file mode 100755 index 000000000..5b0eabe82 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_warning.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_warning_window.svg b/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_warning_window.svg new file mode 100644 index 000000000..66dde5923 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_warning_window.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/designer-base/src/main/resources/com/fr/env/detect/detect.svg b/designer-base/src/main/resources/com/fr/env/detect/detect.svg new file mode 100644 index 000000000..b279a649f --- /dev/null +++ b/designer-base/src/main/resources/com/fr/env/detect/detect.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-base/src/main/resources/com/fr/env/detect/detect_normal.svg b/designer-base/src/main/resources/com/fr/env/detect/detect_normal.svg new file mode 100644 index 000000000..b279a649f --- /dev/null +++ b/designer-base/src/main/resources/com/fr/env/detect/detect_normal.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-base/src/test/java/com/fr/design/components/notification/NotificationDialogTest.java b/designer-base/src/test/java/com/fr/design/components/notification/NotificationDialogTest.java new file mode 100644 index 000000000..b4ae29397 --- /dev/null +++ b/designer-base/src/test/java/com/fr/design/components/notification/NotificationDialogTest.java @@ -0,0 +1,43 @@ +package com.fr.design.components.notification; + +import com.fr.design.utils.DevUtils; +import com.fr.third.guava.collect.Lists; + +import java.awt.Frame; +import java.util.function.Consumer; + +public class NotificationDialogTest { + + public static void main(String[] args) { + + testShow(); + } + + public static void testShow() { + + DevUtils.show(new Consumer() { + @Override + public void accept(Frame frame) { + + NotificationModel model1 = new NotificationModel(NotificationType.WARNING, null, new NotificationMessage.LinkMessage("test", "abc"), new NotificationMessage.LinkMessage("abc", "aaa")); + + NotificationModel model2 = new NotificationModel(NotificationType.INFO, new NotificationAction() { + @Override + public String name() { + return "action"; + } + + @Override + public void run(Object... args) { + System.out.println("1111"); + } + }, new NotificationMessage.LinkMessage("1111 2222 33333333 4444 555 6666 66555 888 999 333
3333
444
555
", ""),new NotificationMessage.LinkMessage("display model2 test", "abc")); + + NotificationDialogProperties properties = new NotificationDialogProperties(frame, "test"); + NotificationDialog dialog = new NotificationDialog(properties, Lists.newArrayList(model1, model2)); + dialog.setVisible(true); + } + }); + } + +} \ No newline at end of file diff --git a/designer-base/src/test/java/com/fr/design/utils/DevDebugUtils.java b/designer-base/src/test/java/com/fr/design/utils/DevDebugUtils.java new file mode 100644 index 000000000..af1d984ea --- /dev/null +++ b/designer-base/src/test/java/com/fr/design/utils/DevDebugUtils.java @@ -0,0 +1,12 @@ +package com.fr.design.utils; + +/** + * created by Harrison on 2022/05/26 + **/ +public class DevDebugUtils { + + public static void main(String[] args) { + + org.swingexplorer.Launcher.main(new String[]{"com.fr.design.utils.DevUtils"}); + } +} diff --git a/designer-base/src/test/java/com/fr/design/utils/DevUtils.java b/designer-base/src/test/java/com/fr/design/utils/DevUtils.java new file mode 100644 index 000000000..2d8827755 --- /dev/null +++ b/designer-base/src/test/java/com/fr/design/utils/DevUtils.java @@ -0,0 +1,60 @@ +package com.fr.design.utils; + +import com.fr.config.dao.DaoContext; +import com.fr.config.dao.impl.LocalClassHelperDao; +import com.fr.config.dao.impl.LocalEntityDao; +import com.fr.config.dao.impl.LocalXmlEntityDao; +import com.fr.design.ui.util.UIUtil; +import com.fr.transaction.Configurations; +import com.fr.transaction.LocalConfigurationHelper; + +import javax.swing.JFrame; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Toolkit; +import java.util.function.Consumer; + +/** + * 设计器的开发时工具 + * 帮助进行 UI 页面的调试 + *

+ * created by Harrison on 2022/05/23 + **/ +public class DevUtils { + + private static void prepare() { + + DaoContext.setEntityDao(new LocalEntityDao()); + DaoContext.setClassHelperDao(new LocalClassHelperDao()); + DaoContext.setXmlEntityDao(new LocalXmlEntityDao()); + Configurations.setHelper(new LocalConfigurationHelper()); + } + + public static void show(Consumer consumer) { + + DesignUtils.initLookAndFeel(); + + UIUtil.invokeLaterIfNeeded(() -> { + + JFrame frame = new JFrame("dev-util"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize(); + frame.setSize(dimension); + + consumer.accept(frame); + + frame.setVisible(true); + }); + + } + + public static void main(String[] args) { + + DevUtils.prepare(); + DevUtils.show(new Consumer() { + @Override + public void accept(Frame frame) { + } + }); + } +} diff --git a/designer-base/src/test/java/com/fr/env/detect/impl/converter/ClassConflictConvertorTest.java b/designer-base/src/test/java/com/fr/env/detect/impl/converter/ClassConflictConvertorTest.java new file mode 100644 index 000000000..cdc789b01 --- /dev/null +++ b/designer-base/src/test/java/com/fr/env/detect/impl/converter/ClassConflictConvertorTest.java @@ -0,0 +1,23 @@ +package com.fr.env.detect.impl.converter; + +import org.junit.Test; + +public class ClassConflictConvertorTest { + + @Test + public void testInnerFinder() { + + ClassNotFoundException ex1 = new ClassNotFoundException("Class 111.222.333 not found"); + Iterable names = ClassConflictConvertor.Converter.CLASS.converter(ex1); + + System.out.println(); + } + + @Test + public void testConverter() { + + ClassNotFoundException ex1 = new ClassNotFoundException("com.zaxxer.hikari.HikariConfig"); + ClassConflictConvertor convertor = new ClassConflictConvertor(); + convertor.convert(ex1); + } +} \ No newline at end of file diff --git a/designer-base/src/test/java/com/fr/env/detect/ui/DetectorErrorDialogTest.java b/designer-base/src/test/java/com/fr/env/detect/ui/DetectorErrorDialogTest.java new file mode 100644 index 000000000..b54288ea5 --- /dev/null +++ b/designer-base/src/test/java/com/fr/env/detect/ui/DetectorErrorDialogTest.java @@ -0,0 +1,33 @@ +package com.fr.env.detect.ui; + +import com.fr.design.utils.DevUtils; +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.bean.ExceptionLog; +import com.fr.env.detect.bean.ExceptionSolution; +import com.fr.env.detect.bean.ExceptionTips; +import com.fr.third.guava.collect.Lists; + +import java.awt.Frame; +import java.util.function.Consumer; + +import static org.junit.Assert.*; + +public class DetectorErrorDialogTest { + + public static void main(String[] args) { + + DevUtils.show(new Consumer() { + @Override + public void accept(Frame frame) { + + DetectorErrorDialog dialog = new DetectorErrorDialog(frame, Lists.newArrayList(DetectorResult.exception(DetectorType.FINE_DB_PERMISSION, + ExceptionTips.create("test"), + ExceptionSolution.create("111test222", "www.baidu.com", null), + ExceptionLog.create("log")))); + dialog.setVisible(true); + } + }); + } + +} \ No newline at end of file diff --git a/designer-base/src/test/java/com/fr/env/detect/ui/EnvDetectorDialogTest.java b/designer-base/src/test/java/com/fr/env/detect/ui/EnvDetectorDialogTest.java new file mode 100644 index 000000000..fd63b1714 --- /dev/null +++ b/designer-base/src/test/java/com/fr/env/detect/ui/EnvDetectorDialogTest.java @@ -0,0 +1,37 @@ +package com.fr.env.detect.ui; + +import com.fr.design.utils.DevUtils; +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorStatus; +import com.fr.env.detect.bean.DetectorType; + +import java.awt.Frame; +import java.util.function.Consumer; + +public class EnvDetectorDialogTest { + + public static void main(String[] args) { + + testShow(); + } + + private static void testShow() { + + DevUtils.show(new Consumer() { + @Override + public void accept(Frame frame) { + + EnvDetectorModel envDetectorModel = new EnvDetectorModel(); + envDetectorModel.update(DetectorType.JAR_CONFLICT, DetectorResult.builder() + .withTips("test") + .withSolution("test", "abc") + .withStatus(DetectorStatus.EXCEPTION) + .build()); + + EnvDetectorDialog envDetectorDialog = new EnvDetectorDialog(frame, envDetectorModel); + envDetectorDialog.setVisible(true); + } + }); + } + +} \ No newline at end of file diff --git a/designer-chart/src/main/java/com/fr/design/chart/series/SeriesCondition/ChartConditionPane.java b/designer-chart/src/main/java/com/fr/design/chart/series/SeriesCondition/ChartConditionPane.java index 73ee26234..4085c7f10 100644 --- a/designer-chart/src/main/java/com/fr/design/chart/series/SeriesCondition/ChartConditionPane.java +++ b/designer-chart/src/main/java/com/fr/design/chart/series/SeriesCondition/ChartConditionPane.java @@ -81,7 +81,10 @@ public class ChartConditionPane extends LiteConditionPane { return this; } }); - conditionValuePane = ValueEditorPaneFactory.createAllValueEditorPane(); + Editor[] editors = ValueEditorPaneFactory.allEditors(); + ColSelectedWithSummaryMethodEditor colSelectedWithSummaryMethodEditor = new ColSelectedWithSummaryMethodEditor(); + Editor[] allEditors = ArrayUtils.add(editors,colSelectedWithSummaryMethodEditor); + conditionValuePane = ValueEditorPaneFactory.createValueEditorPane(allEditors,StringUtils.EMPTY,StringUtils.EMPTY); conditionKeyComboBox.setPreferredSize(new Dimension(175, conditionKeyComboBox.getPreferredSize().height)); conditionOPComboBox.setPreferredSize(new Dimension(80, 20)); Component[][] components = { diff --git a/designer-chart/src/main/java/com/fr/design/chart/series/SeriesCondition/ColSelectedWithSummaryMethodEditor.java b/designer-chart/src/main/java/com/fr/design/chart/series/SeriesCondition/ColSelectedWithSummaryMethodEditor.java index a949e9ecd..5388684e7 100644 --- a/designer-chart/src/main/java/com/fr/design/chart/series/SeriesCondition/ColSelectedWithSummaryMethodEditor.java +++ b/designer-chart/src/main/java/com/fr/design/chart/series/SeriesCondition/ColSelectedWithSummaryMethodEditor.java @@ -38,11 +38,11 @@ public class ColSelectedWithSummaryMethodEditor extends Editor childrenList = xCreator.getTargetChildrenList(); - if (!childrenList.isEmpty()) { - for (int i = 0; i < childrenList.size(); i++) { - XWTabFitLayout tabLayout = (XWTabFitLayout) childrenList.get(i); - tabLayout.updateBoundsWidget(); - } + if (xCreator.acceptType(XWCardMainBorderLayout.class)){ + ((XWCardMainBorderLayout) xCreator).updateBoundsWidget(); } } diff --git a/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardMainBorderLayout.java b/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardMainBorderLayout.java index bdefcc84a..7e4d970a8 100644 --- a/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardMainBorderLayout.java +++ b/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardMainBorderLayout.java @@ -17,6 +17,7 @@ import com.fr.design.designer.creator.XWidgetCreator; import com.fr.design.mainframe.CoverReportPane; import com.fr.design.mainframe.EditingMouseListener; import com.fr.design.mainframe.FormDesigner; +import com.fr.design.utils.gui.LayoutUtils; import com.fr.form.event.Listener; import com.fr.form.ui.CardSwitchButton; import com.fr.form.ui.LayoutBorderStyle; @@ -438,6 +439,7 @@ public class XWCardMainBorderLayout extends XWBorderLayout { } xwCardTitleLayout.setBounds(parentBounds); this.addCardPart((XWCardLayout)this.getComponent(0)); + LayoutUtils.layoutContainer(this); } @Override @@ -461,4 +463,14 @@ public class XWCardMainBorderLayout extends XWBorderLayout { public void setShowOuterShadowBorder(boolean showOuterShadowBorder) { this.showOuterShadowBorder = showOuterShadowBorder; } + + public void updateBoundsWidget() { + ArrayList childrenList = this.getTargetChildrenList(); + if (!childrenList.isEmpty()) { + for (int i = 0; i < childrenList.size(); i++) { + XWTabFitLayout tabLayout = (XWTabFitLayout) childrenList.get(i); + tabLayout.updateBoundsWidget(); + } + } + } } diff --git a/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardTagLayout.java b/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardTagLayout.java index ec070ad86..dbffab21a 100644 --- a/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardTagLayout.java +++ b/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardTagLayout.java @@ -278,7 +278,6 @@ public class XWCardTagLayout extends XWHorizontalBoxLayout { @Override public void doLayout() { - setTabsAndAdjust(); //设置布局 super.doLayout(); } diff --git a/designer-form/src/main/java/com/fr/design/form/util/FontTransformUtil.java b/designer-form/src/main/java/com/fr/design/form/util/FontTransformUtil.java index 769707ebe..eab2a64bb 100644 --- a/designer-form/src/main/java/com/fr/design/form/util/FontTransformUtil.java +++ b/designer-form/src/main/java/com/fr/design/form/util/FontTransformUtil.java @@ -5,6 +5,7 @@ import com.fr.design.ExtraDesignClassManager; import com.fr.design.fun.FormAdaptiveConfigUIProcessor; import com.fr.stable.Constants; import com.fr.stable.unit.PT; +import java.math.BigDecimal; /** @@ -43,4 +44,11 @@ public class FontTransformUtil { return value * (double) Constants.DEFAULT_FONT_PAINT_RESOLUTION / (double) getDesignerFontResolution(); } + + public static int roundUp(double num) { + String numStr = Double.toString(num); + numStr = new BigDecimal(numStr).setScale(0, BigDecimal.ROUND_HALF_UP).toString(); + return Integer.valueOf(numStr); + } + } diff --git a/designer-form/src/main/java/com/fr/design/form/util/HtmlPaintUtils.java b/designer-form/src/main/java/com/fr/design/form/util/HtmlPaintUtils.java new file mode 100644 index 000000000..f8620f52d --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/form/util/HtmlPaintUtils.java @@ -0,0 +1,42 @@ +package com.fr.design.form.util; + +import com.fr.base.Style; +import com.fr.general.FRFont; +import com.fr.log.FineLoggerFactory; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 富文本导出工具栏 + * + * @author hades + * @version 11.0 + * Created by hades on 2022/5/19 + */ +public class HtmlPaintUtils { + + + private static final Pattern FONT_SIZE_PATTERN = Pattern.compile(Pattern.quote("font-size:") + "(.*?)" + Pattern.quote("px")); + + /** + * 设置单元格字体为富文本中的最大字体 + * + * @param style + */ + public static Style deriveMaxFontFromRichChar(Style style, String html) { + int maxSize = style.getFRFont().getSize(); + Matcher matcher = FONT_SIZE_PATTERN.matcher(html); + while (matcher.find()) { + String value = matcher.group(1); + try { + double pxSize = Double.parseDouble(value); + int ptSize = FontTransformUtil.roundUp(FontTransformUtil.px2pt(pxSize)); + maxSize = Math.max(maxSize, ptSize); + } catch (Throwable e) { + FineLoggerFactory.getLogger().debug(e.getMessage(), e); + } + } + FRFont cellFont = style.getFRFont(); + return style.deriveFRFont(cellFont.applySize(maxSize)); + } +} diff --git a/designer-form/src/main/java/com/fr/design/mainframe/FormArea.java b/designer-form/src/main/java/com/fr/design/mainframe/FormArea.java index 7f9dbfc76..759c0b6f7 100644 --- a/designer-form/src/main/java/com/fr/design/mainframe/FormArea.java +++ b/designer-form/src/main/java/com/fr/design/mainframe/FormArea.java @@ -4,6 +4,7 @@ import com.fr.design.actions.UpdateAction; import com.fr.design.constants.UIConstants; import com.fr.design.designer.beans.events.DesignerEvent; import com.fr.design.designer.creator.XCreator; +import com.fr.design.designer.creator.XCreatorUtils; import com.fr.design.designer.creator.XLayoutContainer; import com.fr.design.designer.creator.XWBorderLayout; import com.fr.design.designer.creator.XWFitLayout; @@ -447,7 +448,7 @@ public class FormArea extends JComponent implements ScrollRulerComponent { XLayoutContainer root = FormArea.this.designer.getRootComponent(); if (root.acceptType(XWFitLayout.class)) { XWFitLayout layout = (XWFitLayout) root; - traverAndAdjust(layout, 0.0); + XCreatorUtils.traverAndAdjust(layout); layout.adjustCreatorsWhileSlide(0.0); // 拖动滑块,先将内部组件百分比大小计算,再计算容器大小 Dimension d = new Dimension(layout.getWidth(), layout.getHeight()); @@ -469,19 +470,6 @@ public class FormArea extends JComponent implements ScrollRulerComponent { } } - //循环遍历布局,按百分比调整子组件大小 - private void traverAndAdjust(XCreator creator, double percent) { - for (int i = 0; i < creator.getComponentCount(); i++) { - Object object = creator.getComponent(i); - if (object instanceof XCreator) { - XCreator temp = (XCreator) object; - temp.adjustCompSize(percent); - traverAndAdjust(temp, percent); - } - } - - } - /** * 增加刻度条 */ diff --git a/designer-form/src/main/java/com/fr/design/mainframe/FormSelectionUtils.java b/designer-form/src/main/java/com/fr/design/mainframe/FormSelectionUtils.java index 5e5740f03..331a1f3fe 100644 --- a/designer-form/src/main/java/com/fr/design/mainframe/FormSelectionUtils.java +++ b/designer-form/src/main/java/com/fr/design/mainframe/FormSelectionUtils.java @@ -16,6 +16,7 @@ import com.fr.design.designer.creator.XWTitleLayout; import com.fr.design.designer.creator.cardlayout.XWTabFitLayout; import com.fr.design.fun.FormWidgetOptionProvider; import com.fr.design.utils.ComponentUtils; +import com.fr.design.utils.gui.LayoutUtils; import com.fr.form.main.Form; import com.fr.form.ui.Widget; import com.fr.form.ui.container.WTitleLayout; @@ -290,11 +291,15 @@ public class FormSelectionUtils { public static XCreator copyXcreator(Form form, XCreator xCreator) throws CloneNotSupportedException{ Widget copied = (Widget) xCreator.toData().clone(); XCreator copiedCreator = XCreatorUtils.createXCreator(copied, xCreator.getSize()); + //主要用来处理组件间隔和padding,保证界面上展示的组件尺寸是计算过padding和组件间隔的 + LayoutUtils.layoutContainer(copiedCreator); + XCreatorUtils.traverAndAdjust(copiedCreator); ArrayList nameSpace = new ArrayList<>(); copyWidgetName(form, nameSpace, copiedCreator); return copiedCreator; } + /** * 拷贝组件 * @param form 当前表单 diff --git a/designer-form/src/main/java/com/fr/design/widget/ui/designer/TreeEditorDefinePane.java b/designer-form/src/main/java/com/fr/design/widget/ui/designer/TreeEditorDefinePane.java index e4d99802c..9746c605d 100644 --- a/designer-form/src/main/java/com/fr/design/widget/ui/designer/TreeEditorDefinePane.java +++ b/designer-form/src/main/java/com/fr/design/widget/ui/designer/TreeEditorDefinePane.java @@ -4,18 +4,19 @@ import com.fr.design.data.DataCreatorUI; import com.fr.design.designer.IntervalConstants; import com.fr.design.designer.creator.XCreator; import com.fr.design.gui.icheckbox.UICheckBox; - import com.fr.design.gui.ilable.UILabel; import com.fr.design.gui.itree.refreshabletree.TreeRootPane; import com.fr.design.layout.TableLayout; import com.fr.design.layout.TableLayoutHelper; - import com.fr.design.mainframe.widget.accessibles.AccessibleTreeModelEditor; import com.fr.form.ui.TreeEditor; - -import javax.swing.*; -import java.awt.*; +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import java.awt.Component; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; /* @@ -35,14 +36,19 @@ public class TreeEditorDefinePane extends CustomWritableRepeatEditorPane这是一条测试数据"; + Style style0 = Style.DEFAULT_STYLE; + Assert.assertEquals(16, HtmlPaintUtils.deriveMaxFontFromRichChar(style0, testHtml0).getFRFont().getSize()); + + // 单元格字体size更大 + String testHtml1 = "这是一条测试数据"; + Style style1 = Style.DEFAULT_STYLE; + int oldFontSize = style1.getFRFont().getSize(); + Assert.assertEquals(oldFontSize, HtmlPaintUtils.deriveMaxFontFromRichChar(style1, testHtml1).getFRFont().getSize()); + + // 富文本字体size更大 不同文本 有不同size + String testHtml2 = "这是一条测试数"; + Style style2 = Style.DEFAULT_STYLE; + Assert.assertEquals(23, HtmlPaintUtils.deriveMaxFontFromRichChar(style2, testHtml2).getFRFont().getSize()); + + + // 异常场景1 + String testHtml3 = "xxxx奇怪的格式xxxx"; + Style style3 = Style.DEFAULT_STYLE; + oldFontSize = style1.getFRFont().getSize(); + Assert.assertEquals(oldFontSize, HtmlPaintUtils.deriveMaxFontFromRichChar(style3, testHtml3).getFRFont().getSize()); + + // 异常场景2 + String testHtml4 = "这是一条测试数据"; + Style style4 = Style.DEFAULT_STYLE; + oldFontSize = style1.getFRFont().getSize(); + Assert.assertEquals(oldFontSize, HtmlPaintUtils.deriveMaxFontFromRichChar(style4, testHtml4).getFRFont().getSize()); + + + } + + +} \ No newline at end of file diff --git a/designer-realize/src/main/java/com/fr/design/cell/editor/RichTextCellEditor.java b/designer-realize/src/main/java/com/fr/design/cell/editor/RichTextCellEditor.java index 101928135..1d2a5eda2 100644 --- a/designer-realize/src/main/java/com/fr/design/cell/editor/RichTextCellEditor.java +++ b/designer-realize/src/main/java/com/fr/design/cell/editor/RichTextCellEditor.java @@ -1,5 +1,8 @@ package com.fr.design.cell.editor; +import com.fr.design.mainframe.DesignerContext; +import com.fr.general.GeneralUtils; +import com.fr.design.form.util.HtmlPaintUtils; import java.awt.Component; import javax.swing.SwingUtilities; @@ -42,6 +45,11 @@ public class RichTextCellEditor extends AbstractCellEditor implements @Override public void doOk() { RichTextCellEditor.this.fireEditingStopped(); + CellElement newCellElement = parentTplEC.getTemplateCellElement(cellElement.getColumn(), cellElement.getRow()); + if (cellElement.getCellGUIAttr().isShowAsHTML()) { + newCellElement.setStyle(HtmlPaintUtils.deriveMaxFontFromRichChar(newCellElement.getStyle(), GeneralUtils.objectToString(newCellElement.getValue()))); + DesignerContext.getDesignerFrame().refreshToolbar(); + } } @Override @@ -49,7 +57,6 @@ public class RichTextCellEditor extends AbstractCellEditor implements RichTextCellEditor.this.fireEditingCanceled(); } }); - richTextDialog.addDialogActionListener(this); this.richTextPane.populate(parentTplEC, cellElement); setShowAsHtml(cellElement); diff --git a/designer-realize/src/main/java/com/fr/design/cell/editor/RichTextToolBar.java b/designer-realize/src/main/java/com/fr/design/cell/editor/RichTextToolBar.java index f95681be3..74654cd2e 100644 --- a/designer-realize/src/main/java/com/fr/design/cell/editor/RichTextToolBar.java +++ b/designer-realize/src/main/java/com/fr/design/cell/editor/RichTextToolBar.java @@ -366,9 +366,7 @@ public class RichTextToolBar extends BasicPane { }; private int roundUp(double num) { - String numStr = Double.toString(num); - numStr = new BigDecimal(numStr).setScale(0, BigDecimal.ROUND_HALF_UP).toString(); - return Integer.valueOf(numStr); + return FontTransformUtil.roundUp(num); } private CaretListener textCareListener = new CaretListener() { diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/ProductNewsList.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/ProductNewsList.java index 9e4d1c402..1a8e0a2a2 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/ProductNewsList.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/ProductNewsList.java @@ -4,6 +4,7 @@ import com.fr.design.DesignerEnvManager; import com.fr.design.mainframe.alphafine.AlphaFineConstants; import com.fr.design.mainframe.alphafine.AlphaFineHelper; import com.fr.design.mainframe.alphafine.model.ProductNews; +import com.fr.design.utils.BrowseUtils; import com.fr.log.FineLoggerFactory; import java.awt.Cursor; @@ -63,11 +64,7 @@ public class ProductNewsList extends JList { private void dealWithClick() { ProductNews productNews = getSelectedValue(); - try { - Desktop.getDesktop().browse(new URI(productNews.getUrl())); - } catch (Exception e) { - FineLoggerFactory.getLogger().error(e.getMessage(), e); - } + BrowseUtils.browser(productNews.getUrl()); DesignerEnvManager.getEnvManager().getAlphaFineConfigManager().getReadSet().add(productNews.getId()); AlphaFineHelper.getAlphaFineDialog().repaint(); } diff --git a/designer-realize/src/main/java/com/fr/design/report/ExportUniversalPane.java b/designer-realize/src/main/java/com/fr/design/report/ExportUniversalPane.java new file mode 100644 index 000000000..3714d66a2 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/design/report/ExportUniversalPane.java @@ -0,0 +1,81 @@ +package com.fr.design.report; + +import com.fr.base.CustomConfig; +import com.fr.config.Configuration; +import com.fr.design.constants.UIConstants; +import com.fr.design.dialog.BasicPane; +import com.fr.design.gui.icheckbox.UICheckBox; +import com.fr.design.gui.ilable.ActionLabel; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.i18n.Toolkit; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.utils.BrowseUtils; +import com.fr.general.CloudCenter; +import com.fr.transaction.Configurations; +import com.fr.transaction.WorkerFacade; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.BorderFactory; +import javax.swing.JPanel; + +/** + * @author hades + * @version 11.0 + * Created by hades on 2022/5/26 + */ +public class ExportUniversalPane extends BasicPane { + + private static final String HELP_URL = CloudCenter.getInstance().acquireUrlByKind("help.alt_font.zh_CN", "https://help.fanruan.com/finereport/doc-view-4707.html"); + + private UICheckBox specialCharacterExport; + + public ExportUniversalPane() { + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + this.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + JPanel outerNorthPane =FRGUIPaneFactory.createTitledBorderPane(Toolkit.i18nText("Fine-Design_Report_Universal_Export_Config")); + JPanel northPane = FRGUIPaneFactory.createY_AXISBoxInnerContainer_M_Pane(); + JPanel specialCharacterExportPane =FRGUIPaneFactory.createNormalFlowInnerContainer_M_Pane(); + specialCharacterExport = new UICheckBox(Toolkit.i18nText("Fine-Design_Report_Universal_Export_Special_Character")); + specialCharacterExport.setSelected(true); + specialCharacterExportPane.add(specialCharacterExport); + northPane.add(specialCharacterExportPane); + JPanel labelPane = new JPanel(new BorderLayout()); + labelPane.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 0)); + UILabel centerLabel = new UILabel(Toolkit.i18nText("Fine-Design_Report_Universal_Export_Special_Character_Tip")); + centerLabel.setForeground(Color.GRAY); + ActionLabel rightLabel = new ActionLabel(Toolkit.i18nText("Fine-Design_Report_Universal_Export_More_Alternative_Fonts"), UIConstants.FLESH_BLUE); + rightLabel.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + BrowseUtils.browser(HELP_URL); + } + }); + labelPane.add(centerLabel, BorderLayout.CENTER); + labelPane.add(rightLabel, BorderLayout.EAST); + northPane.add(labelPane); + outerNorthPane.add(northPane); + this.add(outerNorthPane); + } + + @Override + protected String title4PopupWindow() { + return "ExportUniversalPane"; + } + + public void populate() { + this.specialCharacterExport.setSelected(CustomConfig.getInstance().isOptimizedSpecialCharacterExport()); + } + + public void update() { + Configurations.modify(new WorkerFacade(CustomConfig.class) { + @Override + public void run() { + CustomConfig.getInstance().setOptimizedSpecialCharacterExport(specialCharacterExport.isSelected()); + } + }); + } + + +} diff --git a/designer-realize/src/main/java/com/fr/design/report/ReportExportAttrPane.java b/designer-realize/src/main/java/com/fr/design/report/ReportExportAttrPane.java index a284764d2..2de520d27 100644 --- a/designer-realize/src/main/java/com/fr/design/report/ReportExportAttrPane.java +++ b/designer-realize/src/main/java/com/fr/design/report/ReportExportAttrPane.java @@ -5,6 +5,7 @@ import com.fr.design.beans.BasicStorePane; import com.fr.design.dialog.BasicPane; import com.fr.design.fun.ExportAttrTabProvider; import com.fr.design.gui.frpane.UITabbedPane; +import com.fr.design.i18n.Toolkit; import com.fr.io.attr.ReportExportAttr; import javax.swing.*; @@ -18,12 +19,15 @@ public class ReportExportAttrPane extends BasicPane { private ExcelExportPane excelExportPane; private PDFExportPane pdfExportPane; private WordExportPane wordExportPane; + private ExportUniversalPane exportUniversalPane; private List> paneList; public ReportExportAttrPane() { uiTabbedPane = new UITabbedPane(); this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); this.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + exportUniversalPane = new ExportUniversalPane(); + uiTabbedPane.addTab(Toolkit.i18nText("Fine-Design_Report_Universal_Export"), exportUniversalPane); excelExportPane = new ExcelExportPane(); uiTabbedPane.addTab("Excel", excelExportPane); pdfExportPane = new PDFExportPane(); @@ -56,6 +60,10 @@ public class ReportExportAttrPane extends BasicPane { reportExportAttr = new ReportExportAttr(); } + if (this.exportUniversalPane != null) { + this.exportUniversalPane.populate(); + } + if (this.excelExportPane != null) { this.excelExportPane.populate(reportExportAttr.getExcelExportAttr()); } @@ -76,6 +84,11 @@ public class ReportExportAttrPane extends BasicPane { public ReportExportAttr update() { ReportExportAttr reportExportAttr = new ReportExportAttr(); + + if (this.exportUniversalPane != null) { + this.exportUniversalPane.update(); + } + if (this.excelExportPane != null) { reportExportAttr.setExcelExportAttr(this.excelExportPane.update()); } 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 1f8d181ce..c2ec299ea 100644 --- a/designer-realize/src/main/java/com/fr/start/Designer.java +++ b/designer-realize/src/main/java/com/fr/start/Designer.java @@ -1,6 +1,7 @@ package com.fr.start; import com.fr.design.os.impl.SupportOSImpl; +import com.fr.exit.DesignerExiter; import com.fr.log.FineLoggerFactory; /** @@ -22,11 +23,26 @@ public class Designer { // 创建进程 DesignerLauncher.getInstance().start(args); } - } catch (Exception e) { - runNonGuardianDesigner(args); - FineLoggerFactory.getLogger().error(e.getMessage(), e); + } catch (Throwable ex1) { + try { + FineLoggerFactory.getLogger().error(ex1.getMessage(), ex1); + runNonGuardianDesigner(args); + } catch (Throwable ex2) { + // 异常退出 + exitExceptionally(ex2); + } } } + + /** + * 异常退出 + * + * @param throwable 异常 + */ + private static void exitExceptionally(Throwable throwable) { + + DesignerExiter.getInstance().exit(throwable); + } /** * 启动非守护设计器 diff --git a/designer-realize/src/main/java/com/fr/start/DesignerSubListener.java b/designer-realize/src/main/java/com/fr/start/DesignerSubListener.java index c76e6b072..52d364f6c 100644 --- a/designer-realize/src/main/java/com/fr/start/DesignerSubListener.java +++ b/designer-realize/src/main/java/com/fr/start/DesignerSubListener.java @@ -4,6 +4,7 @@ import com.fr.design.mainframe.DesignerContext; import com.fr.event.Event; import com.fr.event.Listener; import com.fr.event.Null; +import com.fr.exit.DesignerExiter; import com.fr.process.engine.core.CarryMessageEvent; import com.fr.process.engine.core.FineProcessContext; import com.fr.process.engine.core.FineProcessEngineEvent; @@ -31,7 +32,10 @@ public class DesignerSubListener { @Override public void on(Event event, Null param) { if (DesignerContext.getDesignerFrame() == null || !DesignerContext.getDesignerFrame().isShowing()) { - FineProcessContext.getParentPipe().fire(new CarryMessageEvent(DesignerProcessType.INSTANCE.obtain())); + + DesignerExiter.getInstance().exitUnexpectedly(() -> { + FineProcessContext.getParentPipe().fire(new CarryMessageEvent(DesignerProcessType.INSTANCE.obtain())); + }); } } }); diff --git a/designer-realize/src/main/java/com/fr/start/DesignerSuperListener.java b/designer-realize/src/main/java/com/fr/start/DesignerSuperListener.java index f5c917464..ee4b26502 100644 --- a/designer-realize/src/main/java/com/fr/start/DesignerSuperListener.java +++ b/designer-realize/src/main/java/com/fr/start/DesignerSuperListener.java @@ -95,6 +95,7 @@ public class DesignerSuperListener { public void run() { cancel = true; ProcessEventPipe pipe = process.getPipe(); + // 确认设计器是否启动完成 pipe.fire(FineProcessEngineEvent.READY); if (StringUtils.isNotEmpty(pipe.info())) { frameReport(); diff --git a/designer-realize/src/main/java/com/fr/start/FineDesigner.java b/designer-realize/src/main/java/com/fr/start/FineDesigner.java index a3e202308..e70575708 100644 --- a/designer-realize/src/main/java/com/fr/start/FineDesigner.java +++ b/designer-realize/src/main/java/com/fr/start/FineDesigner.java @@ -1,5 +1,7 @@ package com.fr.start; +import com.fr.exit.DesignerExiter; +import com.fr.process.engine.FineProcessUtils; import com.fr.process.engine.core.FineProcessEntry; /** @@ -10,5 +12,21 @@ import com.fr.process.engine.core.FineProcessEntry; * Created by hades on 2020/3/24 */ public class FineDesigner extends FineProcessEntry { - + + public static void main(String[] args) { + + FineDesigner fineDesigner = new FineDesigner(); + FineProcessUtils.run(fineDesigner, args); + } + + @Override + public void run(String[] args) { + + try { + super.run(args); + } catch (Throwable throwable) { + // 守护进程启动时,需要捕获异常。并且退出。 + DesignerExiter.getInstance().exit(throwable); + } + } } diff --git a/designer-realize/src/main/java/com/fr/start/LifecycleFatalErrorHandler.java b/designer-realize/src/main/java/com/fr/start/LifecycleFatalErrorHandler.java index 446253e90..92b8ac6c6 100644 --- a/designer-realize/src/main/java/com/fr/start/LifecycleFatalErrorHandler.java +++ b/designer-realize/src/main/java/com/fr/start/LifecycleFatalErrorHandler.java @@ -1,12 +1,19 @@ package com.fr.start; import com.fr.common.report.ReportState; +import com.fr.common.util.Collections; import com.fr.design.RestartHelper; -import com.fr.design.dialog.ErrorDialog; import com.fr.design.dialog.FineJOptionPane; import com.fr.design.i18n.Toolkit; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.DesignerFrame; import com.fr.design.mainframe.messagecollect.StartErrorMessageCollector; import com.fr.design.mainframe.messagecollect.entity.DesignerErrorMessage; +import com.fr.env.detect.base.DetectorBridge; +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorStatus; +import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.ui.DetectorErrorDialog; import com.fr.exit.DesignerExiter; import com.fr.general.IOUtils; import com.fr.io.utils.ResourceIOUtils; @@ -14,14 +21,19 @@ import com.fr.log.FineLoggerFactory; import com.fr.process.engine.core.CarryMessageEvent; import com.fr.process.engine.core.FineProcessContext; import com.fr.stable.StableUtils; +import com.fr.stable.StringUtils; import com.fr.stable.lifecycle.ErrorType; import com.fr.stable.lifecycle.ErrorTypeHelper; import com.fr.stable.lifecycle.FineLifecycleFatalError; import com.fr.stable.project.ProjectConstants; -import javax.swing.*; +import javax.swing.JOptionPane; +import java.util.EnumMap; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author hades @@ -60,16 +72,42 @@ public class LifecycleFatalErrorHandler { * finedb处理 */ enum FineDBHandler implements Handler { - + + /** + * 自检测 + */ SELF { + final EnumMap solutionMap = new EnumMap<>(DetectorType.class); + + { + solutionMap.put(DetectorType.FINE_DB_LOCKED, "Fine-Design_Error_Finedb_Dirty_Backup_Reset"); + solutionMap.put(DetectorType.FINE_DB_PERMISSION, "Fine-Design_Error_Finedb_Permission_Backup_Reset"); + solutionMap.put(DetectorType.FINE_DB_DIRTY, "Fine-Design_Error_Finedb_Dirty_Backup_Reset"); + } + @Override public void handle(FineLifecycleFatalError fatal) { + + Stream resultStream = DetectorBridge.getInstance().detect(fatal); + List results = resultStream + .filter((e) -> e.getStatus() == DetectorStatus.EXCEPTION) + .collect(Collectors.toList()); + + String showText = generateShowText(results); + // 如果还是异常,说明并不是 DB 的异常,抛出预期外的错误 + if (StringUtils.isEmpty(showText)) { + DesignerFrame designerFrame = DesignerContext.getDesignerFrame(); + DetectorErrorDialog errorDialog = new DetectorErrorDialog(designerFrame, results); + errorDialog.setVisible(true); + return; + } + StartErrorMessageCollector.getInstance().record(DesignerErrorMessage.FINEDB_PROBLEM.getId(), DesignerErrorMessage.FINEDB_PROBLEM.getMessage(), fatal.getMessage()); FineLoggerFactory.getLogger().error(DesignerErrorMessage.FINEDB_PROBLEM.getId() + ": " + DesignerErrorMessage.FINEDB_PROBLEM.getMessage()); int result = FineJOptionPane.showOptionDialog(null, - Toolkit.i18nText("Fine-Design_Error_Finedb_Backup_Reset"), + showText, Toolkit.i18nText("Fine-Design_Basic_Error_Tittle"), JOptionPane.YES_NO_OPTION, JOptionPane.ERROR_MESSAGE, @@ -95,8 +133,30 @@ public class LifecycleFatalErrorHandler { DesignerExiter.getInstance().execute(); } } - + + /** + * 生成展示信息 + */ + private String generateShowText(List results) { + + String showText = StringUtils.EMPTY; + if (Collections.isEmpty(results)) { + showText = Toolkit.i18nText("Fine-Design_Error_Finedb_Backup_Reset"); + } else { + for (DetectorResult result : results) { + DetectorType type = result.getType(); + String solutionLocale = solutionMap.get(type); + if (StringUtils.isNotEmpty(solutionLocale)) { + showText = Toolkit.i18nText(solutionLocale); + break; + } + } + } + return showText; + } + private void afterBackupFailed() { + FineJOptionPane.showMessageDialog(null, Toolkit.i18nText("Fine-Design_Error_Finedb_Backup_Reset_Result", ResourceIOUtils.getRealPath(StableUtils.pathJoin(ProjectConstants.EMBED_DB_DIRECTORY, ProjectConstants.FINE_DB_NAME))), @@ -111,29 +171,15 @@ public class LifecycleFatalErrorHandler { * 默认处理 */ enum DefaultHandler implements Handler { + + /** + * 自处理 + */ SELF { @Override public void handle(FineLifecycleFatalError fatal) { - FineLoggerFactory.getLogger().error(fatal.getMessage(), fatal); - StartErrorMessageCollector.getInstance().record(DesignerErrorMessage.UNEXCEPTED_START_FAILED.getId(), - DesignerErrorMessage.UNEXCEPTED_START_FAILED.getMessage(), - fatal.getMessage()); - ErrorDialog dialog = new ErrorDialog(null, Toolkit.i18nText("Fine-Design_Error_Start_Apology_Message"), - Toolkit.i18nText("Fine-Design_Error_Start_Report"), - fatal.getMessage()) { - @Override - protected void okEvent() { - dispose(); - DesignerExiter.getInstance().execute(); - } - - @Override - protected void restartEvent() { - dispose(); - RestartHelper.restart(); - } - }; - dialog.setVisible(true); + + DesignerExiter.getInstance().exit(fatal); } } }