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/data/datapane/TableDataTreePane.java b/designer-base/src/main/java/com/fr/design/data/datapane/TableDataTreePane.java index e42575177..112adff56 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/TableDataTreePane.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/TableDataTreePane.java @@ -1133,7 +1133,10 @@ public class TableDataTreePane extends BasicTableDataTreePane { // 处理数据集名称 String dsName = getNoRepeatedDsName4Paste(dataWrapperEntry.getKey()); AbstractTableDataWrapper wrapper = dataWrapperEntry.getValue(); - addDataPane(TableDataFollowingPasteUtils.generateTableDataPaneWhenPaste(wrapper), dsName); + AbstractTableDataPane tableDataPane = TableDataFollowingPasteUtils.generateTableDataPaneWhenPaste(wrapper); + if (tableDataPane != null) { + addDataPane(tableDataPane, dsName); + } } } } diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionListDialogActionAdapter.java b/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionListDialogActionAdapter.java index 2428c9a00..947cb6e75 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionListDialogActionAdapter.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionListDialogActionAdapter.java @@ -2,11 +2,15 @@ package com.fr.design.data.datapane.connect; import com.fr.design.dialog.BasicDialog; import com.fr.design.dialog.DialogActionAdapter; +import com.fr.design.dialog.FineJOptionPane; import com.fr.design.editlock.EditLockUtils; +import com.fr.design.i18n.Toolkit; import com.fr.design.mainframe.DesignerContext; import com.fr.file.ConnectionConfig; import com.fr.report.LockItem; +import javax.swing.JOptionPane; + /** * @author hades * @version 11.0 @@ -32,7 +36,13 @@ public class ConnectionListDialogActionAdapter extends DialogActionAdapter { connectionListDialog.setDoOKSucceed(false); return; } - connectionManagerPane.update(connectionConfig); + try { + connectionManagerPane.update(connectionConfig); + } catch (Exception e) { + connectionListDialog.setDoOKSucceed(false); + FineJOptionPane.showMessageDialog(connectionManagerPane, e.getMessage(), Toolkit.i18nText("Fine-Design_Basic_Error"), JOptionPane.ERROR_MESSAGE); + return; + } DesignerContext.getDesignerBean("databasename").refreshBeanElement(); // 关闭定义数据连接页面,为其解锁 EditLockUtils.unlock(LockItem.CONNECTION); diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionListPane.java b/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionListPane.java index 12c62b435..e4deae04b 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionListPane.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionListPane.java @@ -1,6 +1,7 @@ package com.fr.design.data.datapane.connect; import com.fr.config.RemoteConfigEvent; +import com.fr.data.core.db.JDBCSecurityChecker; import com.fr.data.impl.Connection; import com.fr.data.impl.ConnectionBean; import com.fr.data.impl.JDBCDatabaseConnection; @@ -28,6 +29,7 @@ import com.fr.transaction.WorkerFacade; import com.fr.workspace.WorkContext; import java.awt.Window; +import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -166,7 +168,7 @@ public class ConnectionListPane extends JListControlPane implements ConnectionSh /** * Update. */ - public void update(ConnectionConfig connectionConfig) { + public void update(ConnectionConfig connectionConfig) throws Exception { // Nameable[]居然不能强转成NameObject[],一定要这么写... Nameable[] res = this.update(); Map updatedMap = new LinkedHashMap<>(); @@ -188,10 +190,27 @@ public class ConnectionListPane extends JListControlPane implements ConnectionSh } }); - + this.checkSecurity(addedOrUpdatedConnections); this.alterConnections(removedConnNames, addedOrUpdatedConnections); } + + private void checkSecurity(List addedOrUpdatedConnections) throws Exception { + + for (ConnectionBean connectionBean : addedOrUpdatedConnections) { + Connection connection = connectionBean.getConnection(); + if (connection instanceof JDBCDatabaseConnection) { + try { + JDBCSecurityChecker.checkURL(((JDBCDatabaseConnection) connection).getURL()); + JDBCSecurityChecker.checkValidationQuery(((JDBCDatabaseConnection) connection).getDbcpAttr().getValidationQuery()); + } catch (SQLException e) { + throw new SQLException(Toolkit.i18nText("Fine-Design_Basic_Database_Connection_Invalid_Config", connectionBean.getName()) + ", " + e.getMessage(), e.getCause()); + } + } + } + + } + private void alterConnections(List removedConnNames, List addedOrUpdatedConnections) { try { diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionManagerPane.java b/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionManagerPane.java index 6c768145f..eadf456e0 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionManagerPane.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionManagerPane.java @@ -4,9 +4,8 @@ import com.fr.design.gui.frpane.LoadingBasicPane; import com.fr.design.layout.FRGUIPaneFactory; import com.fr.file.ConnectionConfig; - -import javax.swing.*; -import java.awt.*; +import javax.swing.JPanel; +import java.awt.BorderLayout; import java.util.HashMap; public class ConnectionManagerPane extends LoadingBasicPane implements ConnectionShowPane { @@ -39,7 +38,7 @@ public class ConnectionManagerPane extends LoadingBasicPane implements Connectio this.connectionListPane.populate(datasourceManager); } - public void update(ConnectionConfig datasourceManager) { + public void update(ConnectionConfig datasourceManager) throws Exception { this.connectionListPane.update(datasourceManager); } diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionShowPane.java b/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionShowPane.java index b5ae09be8..2b74ddcf9 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionShowPane.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionShowPane.java @@ -7,7 +7,7 @@ import com.fr.file.ConnectionConfig; * 数据链接显示面板 */ public interface ConnectionShowPane { - void update(ConnectionConfig connectionConfig); + void update(ConnectionConfig connectionConfig) throws Exception; void populate(ConnectionConfig connectionConfig); diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/connect/SslPane.java b/designer-base/src/main/java/com/fr/design/data/datapane/connect/SslPane.java index c44007991..8789a39a1 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/connect/SslPane.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/connect/SslPane.java @@ -70,16 +70,16 @@ public class SslPane extends BasicPane { JPanel filePanelCa = TableLayoutHelper.createCommonTableLayoutPane(new Component[][]{{keyPathCa, fileChooserButtonCa}}, new double[]{p}, new double[]{f, 20}, 0); Component[] compCa = {new UILabel(Toolkit.i18nText("Fine-Design_Basic_Ssl_Ca") + ":", SwingConstants.RIGHT), filePanelCa}; Component[] compVerifyCa = {null, verifyCa}; - JPanel filePanelClientCert = TableLayoutHelper.createCommonTableLayoutPane(new Component[][]{{keyPathClientCert, fileChooserButtonClientCert}}, new double[]{p}, new double[]{f, 20}, 0); - Component[] compClientCert = {new UILabel(Toolkit.i18nText("Fine-Design_Basic_Ssl_Client_Cert") + ":", SwingConstants.RIGHT), filePanelClientCert}; JPanel filePanelClientKey = TableLayoutHelper.createCommonTableLayoutPane(new Component[][]{{keyPathClientKey, fileChooserButtonClientKey}}, new double[]{p}, new double[]{f, 20}, 0); Component[] compClientKey = {new UILabel(Toolkit.i18nText("Fine-Design_Basic_Ssl_Client_Key") + ":", SwingConstants.RIGHT), filePanelClientKey}; + JPanel filePanelClientCert = TableLayoutHelper.createCommonTableLayoutPane(new Component[][]{{keyPathClientCert, fileChooserButtonClientCert}}, new double[]{p}, new double[]{f, 20}, 0); + Component[] compClientCert = {new UILabel(Toolkit.i18nText("Fine-Design_Basic_Ssl_Client_Cert") + ":", SwingConstants.RIGHT), filePanelClientCert}; // Component[] comCipher = {new UILabel(Toolkit.i18nText("Fine-Design_Basic_Ssl_Cipher") + ":", SwingConstants.RIGHT), cipher}; usingComps = new Component[][]{ compCa, compVerifyCa, - compClientCert, compClientKey, + compClientCert // comCipher }; usingSsl.setSelected(true); diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/management/clip/TableDataTreeClipboard.java b/designer-base/src/main/java/com/fr/design/data/datapane/management/clip/TableDataTreeClipboard.java index 44ae90b2c..3094df145 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/management/clip/TableDataTreeClipboard.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/management/clip/TableDataTreeClipboard.java @@ -1,6 +1,9 @@ package com.fr.design.data.datapane.management.clip; +import com.fr.base.TableData; +import com.fr.design.data.tabledata.paste.TableDataFollowingPasteUtils; import com.fr.design.data.tabledata.wrapper.AbstractTableDataWrapper; +import com.fr.design.data.tabledata.wrapper.TemplateTableDataWrapper; import com.fr.general.NameObject; import java.util.HashMap; @@ -45,7 +48,10 @@ public class TableDataTreeClipboard { return resultMap; } for (NameObject selectedNameObject : selectedNameObjects) { - resultMap.put(selectedNameObject.getName(), (AbstractTableDataWrapper) selectedNameObject.getObject()); + TableData cloned = TableDataFollowingPasteUtils.cloneTableData(((AbstractTableDataWrapper) selectedNameObject.getObject()).getTableData()); + if (cloned != null) { + resultMap.put(selectedNameObject.getName(), new TemplateTableDataWrapper(cloned)); + } } return resultMap; } diff --git a/designer-base/src/main/java/com/fr/design/data/tabledata/paste/TableDataFollowingPasteUtils.java b/designer-base/src/main/java/com/fr/design/data/tabledata/paste/TableDataFollowingPasteUtils.java index e90b8a420..4c39e2854 100644 --- a/designer-base/src/main/java/com/fr/design/data/tabledata/paste/TableDataFollowingPasteUtils.java +++ b/designer-base/src/main/java/com/fr/design/data/tabledata/paste/TableDataFollowingPasteUtils.java @@ -28,6 +28,7 @@ import com.fr.report.cell.FloatElement; import com.fr.report.cell.tabledata.ElementUsedTableDataProvider; import com.fr.report.worksheet.FormElementCase; import com.fr.stable.StringUtils; +import org.jetbrains.annotations.Nullable; import java.util.Arrays; import java.util.HashMap; @@ -65,7 +66,10 @@ public class TableDataFollowingPasteUtils { continue; } AbstractTableDataWrapper tableDataWrapper = new TemplateTableDataWrapper(dataWrapperEntry.getValue(), dsName); - tableDataTreePane.addDataPane(generateTableDataPaneWhenPaste(tableDataWrapper), dsName); + AbstractTableDataPane tableDataPane = generateTableDataPaneWhenPaste(tableDataWrapper); + if (tableDataPane != null) { + tableDataTreePane.addDataPane(tableDataPane, dsName); + } } } @@ -97,20 +101,32 @@ public class TableDataFollowingPasteUtils { } } } - // 处理存储过程名称问题 - return dealWithStoreProcedure(tempMap); + return dealWithTableDataMap(tempMap); } catch (Exception e) { FineLoggerFactory.getLogger().error("transfer widget tabledata failed", e); } return new HashMap<>(); } + /** + * 处理结果集 + * @param tableDataMap + * @return + */ + private static Map dealWithTableDataMap(Map tableDataMap) { + // 处理存储过程名称问题,名称问题需要优先处理,否则就找不到匹配的TableData了 + tableDataMap = dealWithStoreProcedure(tableDataMap); + // 对TableData做clone处理 + tableDataMap = dealWithTableData4Clone(tableDataMap); + return tableDataMap; + } + /** * 处理结果集,将结果集中的存储过程子表替换为原本的存储过程,否则跟随粘贴过去的存储过程名称有问题 * * @param tableDataMap */ - public static Map dealWithStoreProcedure(Map tableDataMap) { + private static Map dealWithStoreProcedure(Map tableDataMap) { Map resultMap = new HashMap<>(); if (tableDataMap == null) { return resultMap; @@ -150,7 +166,8 @@ public class TableDataFollowingPasteUtils { for (Map.Entry dataWrapperEntry : dataWrapperMap.entrySet()) { String tdName = dataWrapperEntry.getKey(); TableData td = dataWrapperEntry.getValue().getTableData(); - if (ComparatorUtils.equals(td, tableData)) { + // 有些数据集的equals方法有问题,这里直接判断对象地址 + if (td == tableData) { return tdName; } } @@ -179,14 +196,30 @@ public class TableDataFollowingPasteUtils { collectTableDataInChartCollection(templateTableData, tempMap, widget); collectTableDataInElementCaseEditor(templateTableData, tempMap, widget); } - // 处理存储过程名称问题 - return dealWithStoreProcedure(tempMap); + return dealWithTableDataMap(tempMap); } catch (Exception e) { FineLoggerFactory.getLogger().error("transfer widget tabledata failed", e); } return new HashMap<>(); } + /** + * 对Map中所有的TableData做clone处理 + * @param tempMap + */ + private static Map dealWithTableData4Clone(Map tempMap) { + Map resultMap = new HashMap<>(); + for (Map.Entry entry : tempMap.entrySet()) { + String name = entry.getKey(); + TableData tableData = entry.getValue(); + TableData clonedTableData = cloneTableData(tableData); + if (clonedTableData != null) { + resultMap.put(name, clonedTableData); + } + } + return resultMap; + } + /** * 收集控件-报表块中使用的数据集 * @@ -342,15 +375,37 @@ public class TableDataFollowingPasteUtils { * 生成粘贴(新建)时使用的AbstractTableDataPane * 主要是为了处理StoreProcedure,它在远程情况下,无法及时获取参数。这边因为是复制粘贴,所以直接用原TableData的参数即可 * @param tableDataWrapper wrapper - * @return AbstractTableDataPane + * @return AbstractTableDataPane,当TableData clone失败时,方法返回null */ + @Nullable public static AbstractTableDataPane generateTableDataPaneWhenPaste(AbstractTableDataWrapper tableDataWrapper) { - AbstractTableDataPane tableDataPane = tableDataWrapper.creatTableDataPane(); - TableData tableData = tableDataWrapper.getTableData(); + // 粘贴时再做一次TableData的clone + TableData clonedTableData = cloneTableData(tableDataWrapper.getTableData()); + if (clonedTableData == null) { + return null; + } + AbstractTableDataWrapper clonedWrapper = new TemplateTableDataWrapper(clonedTableData); + AbstractTableDataPane tableDataPane = clonedWrapper.creatTableDataPane(); + TableData tableData = clonedWrapper.getTableData(); if (tableData instanceof StoreProcedure) { StoreProcedure storeProcedure = (StoreProcedure) tableData; ((ProcedureDataPane) tableDataPane).populateParameters(storeProcedure.getParameters()); } return tableDataPane; } + + /** + * clone数据源,如果失败,打印日志,并返回null,谨慎使用 + * @param tableData + * @return + */ + @Nullable + public static TableData cloneTableData(TableData tableData) { + try { + return (TableData) tableData.clone(); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e, "clone table data {} failed", tableData.getName()); + } + return null; + } } diff --git a/designer-base/src/main/java/com/fr/design/data/tabledata/tabledatapane/DBTableDataPane.java b/designer-base/src/main/java/com/fr/design/data/tabledata/tabledatapane/DBTableDataPane.java index 1746330f1..e73f64eb3 100644 --- a/designer-base/src/main/java/com/fr/design/data/tabledata/tabledatapane/DBTableDataPane.java +++ b/designer-base/src/main/java/com/fr/design/data/tabledata/tabledatapane/DBTableDataPane.java @@ -66,6 +66,7 @@ import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JToolBar; +import javax.swing.SwingUtilities; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import java.awt.BorderLayout; @@ -303,7 +304,7 @@ public class DBTableDataPane extends AbstractTableDataPane { //显示对应的配置 strategyConfigPane.populateBean(populateStrategyConfig); - BasicDialog dlg = strategyConfigPane.showMediumWindow(DesignerContext.getDesignerFrame(), new DialogActionAdapter() { + BasicDialog dlg = strategyConfigPane.showMediumWindow(SwingUtilities.getWindowAncestor(DBTableDataPane.this), new DialogActionAdapter() { @Override public void doOk() { super.doOk(); @@ -314,7 +315,7 @@ public class DBTableDataPane extends AbstractTableDataPane { } } }); - dlg.setAlwaysOnTop(true); + //dlg.setAlwaysOnTop(true); dlg.setVisible(true); } }); 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/formula/FormulaPane.java b/designer-base/src/main/java/com/fr/design/formula/FormulaPane.java index ef321e708..d4737f084 100644 --- a/designer-base/src/main/java/com/fr/design/formula/FormulaPane.java +++ b/designer-base/src/main/java/com/fr/design/formula/FormulaPane.java @@ -38,6 +38,7 @@ import com.fr.design.i18n.Toolkit; import com.fr.design.layout.FRGUIPaneFactory; import com.fr.design.mainframe.DesignerContext; import com.fr.design.mainframe.JTemplate; +import com.fr.design.parameter.ParameterInputNoneListenerPane; import com.fr.design.parameter.ParameterInputPane; import com.fr.design.utils.gui.GUICoreUtils; import com.fr.general.ComparatorUtils; @@ -885,9 +886,9 @@ public class FormulaPane extends BasicPane implements KeyListener, UIFormula { if (checkResult.isValid()) { showMessageDialog(checkResult.getTips(), checkResult.isValid(), false); } else { - int columns = checkResult.getFormulaCoordinates().getColumns(); + int columns = checkResult.getFormulaCoordinates().getColumns() + 1; String position = StringUtils.EMPTY; - if (columns >= 0) { + if (columns > 0) { position = Toolkit.i18nText("Fine-Design_Basic_Formula_The") + columns + Toolkit.i18nText("Fine-Design_Basic_Formula_Error_Position") + " "; } @@ -964,7 +965,7 @@ public class FormulaPane extends BasicPane implements KeyListener, UIFormula { if (parameters.length < 1 && editor4CalPane.update().size() < 1) { return parameterMap; } - ParameterInputPane pPane = new ParameterInputPane(parameters); + ParameterInputPane pPane = new ParameterInputNoneListenerPane(parameters); pPane.showSmallWindow(new JFrame(), new DialogActionAdapter() { @Override public void doOk() { 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/EastRegionContainerPane.java b/designer-base/src/main/java/com/fr/design/mainframe/EastRegionContainerPane.java index 43097e366..d640f84f4 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/EastRegionContainerPane.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/EastRegionContainerPane.java @@ -287,7 +287,7 @@ public class EastRegionContainerPane extends UIEastResizableContainer { // 悬浮元素 PropertyItem floatElement = new PropertyItem(KEY_FLOAT_ELEMENT, com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Float_Element"), "floatelement", new PropertyMode[]{PropertyMode.REPORT, PropertyMode.REPORT_PARA, PropertyMode.REPORT_PARA_WIDGET, PropertyMode.REPORT_FLOAT, PropertyMode.POLY, PropertyMode.POLY_CHART}, - new PropertyMode[]{PropertyMode.REPORT, PropertyMode.REPORT_FLOAT, PropertyMode.POLY_REPORT, PropertyMode.DUCHAMP_REPORT}); + new PropertyMode[]{PropertyMode.REPORT, PropertyMode.REPORT_FLOAT, PropertyMode.POLY_REPORT}); // 控件设置 PropertyItem widgetSettings = new PropertyItem(KEY_WIDGET_SETTINGS, com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Component_Settings"), "widgetsettings", new PropertyMode[]{PropertyMode.REPORT, PropertyMode.REPORT_PARA, PropertyMode.REPORT_PARA_WIDGET, PropertyMode.REPORT_FLOAT, PropertyMode.FORM, PropertyMode.POLY}, 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 aacbd2fbf..7d1461bf3 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 @@ -190,6 +190,10 @@ public abstract class JTemplate> public JTemplate(T t, FILE file, boolean isNewFile, Parameter[] parameters) { super(t); + if (isNewFile) { + // REPORT-58486: 必须在初始的UndoState创建前设置主题,使得初始的UndoState就包含了主题效果 + setUpTheme4NewTemplate(); + } beforeInit(); // 判断是否切换设计器状态到禁止拷贝剪切 if (t.getAttrMark(DesignBanCopyAttrMark.XML_TAG) != null) { @@ -442,20 +446,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 +516,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) { @@ -1014,7 +1049,7 @@ public abstract class JTemplate> // 过滤掉本地文件 boolean localFile = getEditingFILE() instanceof FileFILE; boolean inconsistent = !localFile && getEditingFILE().exists() - && !WorkContext.getCurrent().get(LockInfoOperator.class).isConsistentLock(getEditingFILE().getPath()); + && !WorkContext.getCurrent().get(LockInfoOperator.class).isConsistentLock(getEditingFILE().getPath()); if (inconsistent) { throw new InconsistentLockException(); } @@ -1112,6 +1147,20 @@ public abstract class JTemplate> this.listenerList.remove(JTemplateActionListener.class, l); } + /** + * 模板保存前触发 + */ + public void fireJTemplateSaveBefore() { + Object[] listeners = listenerList.getListenerList(); + + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == JTemplateActionListener.class) { + ((JTemplateActionListener) listeners[i + 1]).templateSaveBefore(this); + } + } + this.repaint(30); + } + /** * 触发模板关闭 */ @@ -1622,6 +1671,7 @@ public abstract class JTemplate> } private CallbackSaveWorker save(boolean showLoc) { + fireJTemplateSaveBefore(); FILE editingFILE = this.getEditingFILE(); // carl:editingFILE没有,当然不存了,虽然不会有这种情况 if (editingFILE == null) { @@ -1738,6 +1788,7 @@ public abstract class JTemplate> refreshToolArea(); } DesignerFrameFileDealerPane.getInstance().refresh(); + DesignerFrameFileDealerPane.getInstance().stateChange(); } }); return worker; @@ -1879,10 +1930,10 @@ public abstract class JTemplate> protected void setUpTheme4NewTemplate() { TemplateTheme theme = getUsingTemplateThemeConfig().cachedFetchTheme4NewTemplate(); - TemplateThemeAttrMark themeAttrMark = template.getAttrMark(TemplateThemeAttrMark.XML_TAG); + TemplateThemeAttrMark themeAttrMark = getTarget().getAttrMark(TemplateThemeAttrMark.XML_TAG); if (themeAttrMark == null) { themeAttrMark = new TemplateThemeAttrMark(); - template.addAttrMark(themeAttrMark); + getTarget().addAttrMark(themeAttrMark); } themeAttrMark.setName(theme.getName()); themeAttrMark.setDark(theme.isDark()); @@ -1898,10 +1949,12 @@ public abstract class JTemplate> @Override public void setTemplateTheme(TemplateTheme newTheme, TemplateThemeCompatible compatible) { - ThemedTemplate.super.setTemplateTheme(newTheme, compatible); - String name = newTheme.getName(); - templateThemeButton.setText(name); - templateThemeButton.setToolTipText(name); + if (templateThemeButton != null) { + ThemedTemplate.super.setTemplateTheme(newTheme, compatible); + String name = newTheme.getName(); + templateThemeButton.setText(name); + templateThemeButton.setToolTipText(name); + } } /** @@ -1917,8 +1970,8 @@ public abstract class JTemplate> } - public void setDesignerUIMode(){ + public void setDesignerUIMode() { DesignerUIModeConfig.getInstance().setAbsoluteMeasureUIMode(); } -} \ No newline at end of file +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/JTemplateActionListener.java b/designer-base/src/main/java/com/fr/design/mainframe/JTemplateActionListener.java index 5e1787369..ecfd52dbe 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/JTemplateActionListener.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/JTemplateActionListener.java @@ -4,9 +4,15 @@ import java.util.EventListener; public interface JTemplateActionListener extends EventListener { - public void templateOpened(JTemplate jt); + default void templateOpened(JTemplate jt) { + } - public void templateSaved(JTemplate jt); + default void templateSaved(JTemplate jt) { + } - public void templateClosed(JTemplate jt); + default void templateClosed(JTemplate jt) { + } + + default void templateSaveBefore(JTemplate jt) { + } } \ No newline at end of file diff --git a/designer-base/src/main/java/com/fr/design/mainframe/JTemplateFactory.java b/designer-base/src/main/java/com/fr/design/mainframe/JTemplateFactory.java index 9aa8204d6..310a051a4 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/JTemplateFactory.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/JTemplateFactory.java @@ -1,7 +1,12 @@ package com.fr.design.mainframe; import com.fr.base.io.BaseBook; +import com.fr.base.theme.FineColorGather; +import com.fr.base.theme.FineColorManager; +import com.fr.base.theme.FineColorSynchronizer; +import com.fr.base.theme.TemplateThemeCompatible; import com.fr.file.FILE; +import com.fr.form.main.Form; import com.fr.stable.CoreConstants; import com.fr.stable.StringUtils; @@ -36,6 +41,7 @@ public final class JTemplateFactory { if (defaultAppExtension.equalsIgnoreCase(fileExtension)) { JTemplate jt = app.openTemplate(file); if (jt != null) { + jt.checkAndResetTheme(); return jt; } } diff --git a/designer-base/src/main/java/com/fr/design/mainframe/NorthRegionContainerPane.java b/designer-base/src/main/java/com/fr/design/mainframe/NorthRegionContainerPane.java index 544dc392a..fcfa4292f 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/NorthRegionContainerPane.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/NorthRegionContainerPane.java @@ -100,8 +100,7 @@ public class NorthRegionContainerPane extends JPanel { //优先级为-1,保证最后全面刷新一次 GeneralContext.listenPluginRunningChanged(pluginOnRunOrStopListener, pluginFilter); // 在设计器启动时仅在最后一个插件启用时候进行刷新一次 如果插件启用过程中存在实现了设计器接口的插件 - boolean needRefresh = DesignerContext.getDesignerFrame() != null && DesignerContext.getDesignerFrame().isVisible() && existDesignExtraPlugin; - if (needRefresh) { + if (existDesignExtraPlugin) { refreshAll(northEastPane, ad); } } 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/theme/TemplateThemeEditorPane.java b/designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemeEditorPane.java index 0b26f6f8e..8cb180277 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemeEditorPane.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemeEditorPane.java @@ -2,6 +2,7 @@ package com.fr.design.mainframe.theme; import com.fr.base.theme.FineColorFlushUtils; import com.fr.base.theme.FineColorManager; +import com.fr.base.theme.FineColorSynchronizer; import com.fr.base.theme.TemplateTheme; import com.fr.base.theme.TemplateThemeConfig; import com.fr.base.theme.settings.ThemedCellStyleList; @@ -157,10 +158,8 @@ public abstract class TemplateThemeEditorPane extends J return container; } private void onColorSchemeChanged(List colors) { - FineColorManager.FineColorReplaceByColorScheme replaceByColorScheme = new FineColorManager.FineColorReplaceByColorScheme(colors); T theme = updateBean(); - FineColorFlushUtils.replaceCacheObject(theme, replaceByColorScheme); - FineColorManager.traverse(theme, replaceByColorScheme); + theme = (T) FineColorSynchronizer.flush(theme, colors); populateBean4CustomEditors(theme); this.repaint(); } diff --git a/designer-base/src/main/java/com/fr/design/mainframe/theme/ThemedFeatureController.java b/designer-base/src/main/java/com/fr/design/mainframe/theme/ThemedFeatureController.java index 2e0fc5c38..47cf1bcdd 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/theme/ThemedFeatureController.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/theme/ThemedFeatureController.java @@ -1,7 +1,6 @@ package com.fr.design.mainframe.theme; import com.fr.workspace.WorkContext; -import com.fr.workspace.server.theme.SupportThemedCellInnerBorderFeature; import com.fr.workspace.server.theme.ThemedCellBorderFeature; /** @@ -12,6 +11,6 @@ import com.fr.workspace.server.theme.ThemedCellBorderFeature; public class ThemedFeatureController { public static boolean isCellStyleSupportInnerBorder() { ThemedCellBorderFeature controller = WorkContext.getCurrent().get(ThemedCellBorderFeature.class); - return controller instanceof SupportThemedCellInnerBorderFeature; + return controller.isSupport(); } } diff --git a/designer-base/src/main/java/com/fr/design/mainframe/theme/edit/ui/ColorListExtendedPane.java b/designer-base/src/main/java/com/fr/design/mainframe/theme/edit/ui/ColorListExtendedPane.java deleted file mode 100644 index 9d02180e7..000000000 --- a/designer-base/src/main/java/com/fr/design/mainframe/theme/edit/ui/ColorListExtendedPane.java +++ /dev/null @@ -1,144 +0,0 @@ -package com.fr.design.mainframe.theme.edit.ui; - -import com.fr.base.theme.FineColorManager; -import com.fr.design.layout.FRGUIPaneFactory; - -import javax.swing.JPanel; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.util.ArrayList; -import java.util.List; - -/** - * @author Starryi - * @version 1.0 - * Created by Starryi on 2021/8/7 - */ -public class ColorListExtendedPane extends JPanel implements MouseListener { - public static final int DEFAULT_COLOR_COUNT = 8; - public static final int DEFAULT_EXTENDED_COUNT = 5; - public static final int DEFAULT_COLOR_SIZE = 16; - public static final int DEFAULT_COLOR_GAP = 3; - - public static final ExtendedColorComputer DEFAULT_EXTENDED_COMPUTER = new ExtendedColorComputer() { - @Override - public Color computeExtendedColor(Color color, int index, int count) { - return FineColorManager.computeExtendedColor(color, index, count); - } - }; - - private final boolean selectable; - private final int colorCount; - private final int extendedCount; - private final int boxSize; - private final int boxGap; - - private final List colorList = new ArrayList<>(); - private ExtendedColorComputer extendedColorComputer = DEFAULT_EXTENDED_COMPUTER; - - private int selectedColorIndex = -1; - private int selectedExtendedIndex = -1; - - public ColorListExtendedPane() { - this(false, DEFAULT_COLOR_COUNT, DEFAULT_EXTENDED_COUNT, DEFAULT_COLOR_SIZE, DEFAULT_COLOR_GAP); - } - - public ColorListExtendedPane(boolean selectable, int colorCount, int extendedCount, int boxSize, int boxGap) { - setLayout(FRGUIPaneFactory.createBorderLayout()); - this.selectable = selectable; - this.colorCount = Math.max(1, colorCount); - this.extendedCount = extendedCount; - this.boxSize = boxSize; - this.boxGap = boxGap; - - for (int i = 0; i < colorCount; i++) { - colorList.add(Color.WHITE); - } - - int width = colorCount * boxSize + (colorCount - 1) * boxGap; - int height = extendedCount * boxSize + (extendedCount - 1) * boxGap; - setPreferredSize(new Dimension(width, height)); - } - - public void populate(List colors) { - if (colors.size() > 0) { - colorList.clear(); - colorList.addAll(colors); - repaint(); - } - } - - public List update() { - return new ArrayList<>(colorList); - } - - public void setExtendedColorComputer(ExtendedColorComputer extendedColorComputer) { - this.extendedColorComputer = extendedColorComputer; - } - - @Override - public void mouseClicked(MouseEvent e) { - int x = e.getX(); - int y = e.getY(); - - int colorIndex = x / (boxSize + boxGap); - int colorX = colorIndex * (boxSize + boxGap); - - int extendedIndex = y / boxSize; - int extendedY = extendedIndex * boxSize; - - if (colorX <= x && x <= colorX + boxSize && extendedY <= y && y <= extendedY + boxSize) { - selectedColorIndex = colorIndex; - selectedExtendedIndex = extendedIndex; - } else { - selectedColorIndex = -1; - selectedExtendedIndex = -1; - } - repaint(); - } - - @Override - public void mousePressed(MouseEvent e) { - - } - - @Override - public void mouseReleased(MouseEvent e) { - - } - - @Override - public void mouseEntered(MouseEvent e) { - - } - - @Override - public void mouseExited(MouseEvent e) { - - } - - public interface ExtendedColorComputer { - Color computeExtendedColor(Color color, int index, int count); - } - - @Override - public void paint(Graphics g) { - super.paint(g); - Color oldColor = g.getColor(); - - for (int i = 0; i < colorCount; i++) { - int x = i * (boxSize + boxGap); - for (int j = 0; j < extendedCount; j++) { - Color color = extendedColorComputer.computeExtendedColor(colorList.get(i), j, extendedCount); - g.setColor(color); - int y = j * (boxSize + boxGap); - g.fillRect(x, y, boxSize, boxSize); - } - } - - g.setColor(oldColor); - } -} 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/parameter/ParameterInputNoneListenerPane.java b/designer-base/src/main/java/com/fr/design/parameter/ParameterInputNoneListenerPane.java new file mode 100644 index 000000000..2484a4b20 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/parameter/ParameterInputNoneListenerPane.java @@ -0,0 +1,30 @@ +package com.fr.design.parameter; + +import com.fr.design.editor.ValueEditorPane; +import com.fr.stable.ParameterProvider; + +/** + * 与ParameterInputPane区别在于 输入参数不会触发模板修改事件 + * + * 适用于参数不需要存储在模板中的场景 + * + * @author hades + * @version 11.0 + * Created by hades on 2022/5/13 + */ +public class ParameterInputNoneListenerPane extends ParameterInputPane { + + public ParameterInputNoneListenerPane(ParameterProvider[] parameters) { + super(parameters); + } + + public ParameterInputNoneListenerPane(ParameterProvider[] parameters, boolean allowBlank) { + super(parameters, allowBlank); + } + + @Override + protected void initTextListener(ValueEditorPane textF) { + // 这边输入参数 不用联动模板变化激活保存按钮 相关参数并不存储在模板 + // do nothing + } +} diff --git a/designer-base/src/main/java/com/fr/design/record/analyzer/DesignerAnalyzerActivator.java b/designer-base/src/main/java/com/fr/design/record/analyzer/DesignerAnalyzerActivator.java index 0a83d81c2..411789e1f 100644 --- a/designer-base/src/main/java/com/fr/design/record/analyzer/DesignerAnalyzerActivator.java +++ b/designer-base/src/main/java/com/fr/design/record/analyzer/DesignerAnalyzerActivator.java @@ -41,7 +41,7 @@ public class DesignerAnalyzerActivator extends Activator implements Prepare { @Override public void start() { - OptimizeUtil.open(() -> { + OptimizeUtil.open(OptimizeUtil.Module.ANALYZER,() -> { AnalyzerAssemblyFactory basicFactory = createBasicFactory(); 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/style/color/ColorPicker.java b/designer-base/src/main/java/com/fr/design/style/color/ColorPicker.java index 93b9ba3fd..2e5f56981 100644 --- a/designer-base/src/main/java/com/fr/design/style/color/ColorPicker.java +++ b/designer-base/src/main/java/com/fr/design/style/color/ColorPicker.java @@ -4,6 +4,7 @@ package com.fr.design.style.color; * Created by plough on 2016/12/22. */ +import com.fr.design.DesignerEnvManager; import com.fr.design.utils.gui.GUICoreUtils; import com.fr.general.IOUtils; import com.fr.log.FineLoggerFactory; @@ -137,6 +138,7 @@ public class ColorPicker extends JDialog implements ActionListener { timer.stop(); if (setColor) { colorSelectable.setColor(colorToSet); + DesignerEnvManager.getEnvManager().getColorConfigManager().addToColorQueue(new Color(colorToSet.getRGB())); } this.dispose(); } 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..c7159eec8 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java @@ -0,0 +1,152 @@ +package com.fr.env.detect.base; + +import com.fr.common.annotations.Careful; +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.impl.DetectorChain; +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 + **/ +@Careful +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(DetectorChain.construct( + new FineDbLockedDetector(), + new FineDbPermissionDetector(), + new FineDbDirtyDetector())); + + 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..2ebbf331e --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/base/DetectorManager.java @@ -0,0 +1,82 @@ +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.DetectorChain; + +import java.util.ArrayList; +import java.util.Collection; +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 chains = new ArrayList<>(); + + /** + * 单纯的检测器的列表 + */ + private List detectors = new ArrayList<>(); + + public void register(DetectorChain chain) { + + if (chain != null) { + chains.add(chain); + } + } + + public void register(ExceptionDetector detector) { + + if (detector != null) { + detectors.add(detector); + } + } + + public Stream detect() { + + Stream results = detectors.stream() + .map(ExceptionDetector::detect) + .filter(Objects::nonNull); + + Stream chainResults = chains.stream() + .map(DetectorChain::detect) + .filter(Objects::nonNull); + + List resultList = Stream.concat(results, chainResults) + .collect(Collectors.toList()); + + resultList.forEach(DetectorResult::log); + + return resultList.stream(); + } + + public DetectorResult detect(DetectorType type) { + + Stream chainDetectors = chains.stream() + .map(DetectorChain::getDetectors) + .flatMap(Collection::stream); + + Stream allDetectors = Stream.concat(detectors.stream(), chainDetectors); + + Optional result = allDetectors + .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/DetectorChain.java b/designer-base/src/main/java/com/fr/env/detect/impl/DetectorChain.java new file mode 100644 index 000000000..352e72c6b --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/impl/DetectorChain.java @@ -0,0 +1,46 @@ +package com.fr.env.detect.impl; + +import com.fr.env.detect.base.ExceptionDetector; +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorStatus; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 检测链 + * 责任链模式, 有先后顺序,哪一个先获得,哪一个先终止。 + * + * created by Harrison on 2022/06/16 + **/ +public class DetectorChain { + + private List detectors = new ArrayList<>(); + + public static DetectorChain construct(ExceptionDetector... detectors) { + + DetectorChain detectorChain = new DetectorChain(); + detectorChain.detectors = Arrays.stream(detectors).collect(Collectors.toList()); + return detectorChain; + } + + @Nullable + public DetectorResult detect() { + + for (ExceptionDetector detector : detectors) { + DetectorResult result = detector.detect(); + if (result != null && result.getStatus() == DetectorStatus.EXCEPTION) { + return result; + } + } + return null; + } + + public List getDetectors() { + + return this.detectors; + } +} 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..2b627a7b2 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbLockedConverter.java @@ -0,0 +1,69 @@ +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; +import com.fr.workspace.WorkContext; + +/** + * 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) { + + // 远程不执行 + if (!WorkContext.getCurrent().isLocal()) { + return null; + } + + Throwable sign = throwable; + while (sign != null && sign.getClass() != HsqlException.class) { + sign = sign.getCause(); + } + + if (sign == null) { + return null; + } + + 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..d34e7c245 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbPermissionConverter.java @@ -0,0 +1,84 @@ +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.stable.project.ProjectConstants; +import com.fr.third.org.apache.commons.io.FileUtils; +import com.fr.third.org.hsqldb.HsqlException; +import com.fr.workspace.WorkContext; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.RandomAccessFile; +import java.util.Collection; + +/** + * HSQL 下的权限检测 + *

+ * created by Harrison on 2022/05/24 + **/ +public class FineDbPermissionConverter implements ThrowableConverter { + + public static final String EMBED_DB_NAME = "finedb"; + + @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) { + + // 远程不执行 + if (!WorkContext.getCurrent().isLocal()) { + return null; + } + + Throwable sign = throwable; + while (sign != null && sign.getClass() != HsqlException.class) { + sign = sign.getCause(); + } + + if (sign == null) { + return null; + } + + String fineDbDirectory = WorkContext.getCurrent().getPath() + File.separator + ProjectConstants.EMBED_DB_DIRECTORY + File.separator + EMBED_DB_NAME; + Collection files = FileUtils.listFiles(new File(fineDbDirectory), null, true); + Boolean isPermitted = files.stream() + .map((file -> { + try { + // 进行权限判断 + new RandomAccessFile(file, "rw"); + return true; + } catch (FileNotFoundException e) { + return false; + } + })) + .reduce((a, b) -> a & b) + .orElse(Boolean.FALSE); + + if (!isPermitted) { + DetectorType type = DetectorType.FINE_DB_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/ConfigToPropMigrator.java b/designer-base/src/main/java/com/fr/exit/ConfigToPropMigrator.java index 909768bee..ea3e72c3f 100644 --- a/designer-base/src/main/java/com/fr/exit/ConfigToPropMigrator.java +++ b/designer-base/src/main/java/com/fr/exit/ConfigToPropMigrator.java @@ -10,6 +10,8 @@ import com.fr.workspace.WorkContext; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; import java.sql.Blob; import java.sql.Connection; import java.sql.DriverManager; @@ -40,7 +42,14 @@ public class ConfigToPropMigrator { } public void execute() { + try { + _execute(); + } catch (Throwable throwable) { + FineLoggerFactory.getLogger().warn(throwable.getMessage(), throwable); + } + } + private void _execute() { if (WorkContext.getCurrent().isLocal()) { String url = "jdbc:hsqldb:file://" + WorkContext.getCurrent().getPath() + "/" + ProjectConstants.EMBED_DB_DIRECTORY + "/finedb/db;hsqldb.tx=mvcc"; @@ -55,9 +64,9 @@ public class ConfigToPropMigrator { initDirectory(); try (Connection c = DriverManager.getConnection(url); - FileOutputStream entityOut = new FileOutputStream(PropertiesConstants.ENTITY_PROP_PATH); - FileOutputStream classHelperOut = new FileOutputStream(PropertiesConstants.CLASS_NAME_PROP_PATH); - FileOutputStream xmlEntityOut = new FileOutputStream(PropertiesConstants.XML_ENTITY_PROP_PATH)) { + OutputStreamWriter xmlEntityOut = new OutputStreamWriter(new FileOutputStream(PropertiesConstants.XML_ENTITY_PROP_PATH), StandardCharsets.UTF_8); + OutputStreamWriter entityOut = new OutputStreamWriter(new FileOutputStream(PropertiesConstants.ENTITY_PROP_PATH), StandardCharsets.UTF_8); + OutputStreamWriter classHelperOut = new OutputStreamWriter(new FileOutputStream(PropertiesConstants.CLASS_NAME_PROP_PATH), StandardCharsets.UTF_8)) { processClassOrEntity(c, new Properties(), SELECT_FOR_ENTITY, entityOut); processClassOrEntity(c, new Properties(), SELECT_FOR_CLASSNAME, classHelperOut); @@ -77,7 +86,7 @@ public class ConfigToPropMigrator { } } - private void processClassOrEntity(Connection c, Properties map, String sql, FileOutputStream outputStream) throws SQLException, IOException { + private void processClassOrEntity(Connection c, Properties map, String sql, OutputStreamWriter writer) throws SQLException, IOException { PreparedStatement query = c.prepareStatement(sql); ResultSet resultSet = query.executeQuery(); while (resultSet.next()) { @@ -87,19 +96,19 @@ public class ConfigToPropMigrator { map.setProperty(id, value); } } - map.store(outputStream, null); + map.store(writer, null); } - private void processXmlEntity(Connection c, Properties map, FileOutputStream outputStream) throws SQLException, IOException { + private void processXmlEntity(Connection c, Properties map, OutputStreamWriter writer) throws SQLException, IOException { PreparedStatement query = c.prepareStatement(SELECT_FOR_XML_ENTITY); ResultSet resultSet = query.executeQuery(); while (resultSet.next()) { String id = resultSet.getString(1); Blob value = resultSet.getBlob(2); byte[] bytes = value.getBytes(1L, (int) value.length()); - map.setProperty(id, new String(bytes)); + map.setProperty(id, new String(bytes, StandardCharsets.UTF_8)); } - map.store(outputStream, null); + map.store(writer, null); } public void deletePropertiesCache() { 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/i18n/dimension_zh.properties b/designer-base/src/main/resources/com/fr/design/i18n/dimension_zh.properties index 3c398efc0..055a81109 100644 --- a/designer-base/src/main/resources/com/fr/design/i18n/dimension_zh.properties +++ b/designer-base/src/main/resources/com/fr/design/i18n/dimension_zh.properties @@ -13,7 +13,7 @@ com.fr.design.actions.server.dialog=700*630 com.fr.design.report.fit.templatePane.dialog=600*400 com.fr.design.report.fit.firstColumn=80*20 com.fr.design.report.fit.column=100*20 -com.fr.design.lock.LockInfoDialog=400*160 +com.fr.design.lock.LockInfoDialog=400*180 com.fr.design.mainframe.ForbiddenPane.refreshButton=68*24 com.fr.design.cell.expand.sort.pane=227*155 com.fr.design.sort.rule.item=80*20 diff --git a/designer-base/src/main/resources/com/fr/design/i18n/dimension_zh_TW.properties b/designer-base/src/main/resources/com/fr/design/i18n/dimension_zh_TW.properties index 7130938c7..2ee112455 100644 --- a/designer-base/src/main/resources/com/fr/design/i18n/dimension_zh_TW.properties +++ b/designer-base/src/main/resources/com/fr/design/i18n/dimension_zh_TW.properties @@ -12,7 +12,7 @@ com.fr.design.actions.server.dialog=700*630 com.fr.design.report.fit.templatePane.dialog=600*400 com.fr.design.report.fit.firstColumn=80*20 com.fr.design.report.fit.column=100*20 -com.fr.design.lock.LockInfoDialog=400*160 +com.fr.design.lock.LockInfoDialog=400*180 com.fr.design.mainframe.ForbiddenPane.refreshButton=68*24 com.fr.design.cell.expand.sort.pane=227*155 com.fr.design.sort.rule.item=80*20 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/ColSelectedWithSummaryMethodEditor.java b/designer-chart/src/main/java/com/fr/design/chart/series/SeriesCondition/ColSelectedWithSummaryMethodEditor.java index 875bc705a..6d7ba7699 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,18 +38,17 @@ public class ColSelectedWithSummaryMethodEditor extends Editor()); + } + + private static String uniqueName(Form form, String widgetName, Set duplicated) { //先保存默认名字 String raw = widgetName; int i = 0; @@ -74,5 +83,4 @@ public abstract class ModelUtil { duplicated.add(widgetName); return widgetName; } - } diff --git a/designer-form/src/main/java/com/fr/design/designer/creator/XCreatorUtils.java b/designer-form/src/main/java/com/fr/design/designer/creator/XCreatorUtils.java index 1be6f0d33..395521a7f 100644 --- a/designer-form/src/main/java/com/fr/design/designer/creator/XCreatorUtils.java +++ b/designer-form/src/main/java/com/fr/design/designer/creator/XCreatorUtils.java @@ -7,6 +7,7 @@ import com.fr.base.chart.BaseChartCollection; import com.fr.base.theme.FineColorFlushUtils; import com.fr.base.theme.FineColorGather; import com.fr.base.theme.FineColorManager; +import com.fr.base.theme.FineColorSynchronizer; import com.fr.base.theme.FormTheme; import com.fr.base.theme.TemplateTheme; import com.fr.base.theme.TemplateThemeAware; @@ -235,13 +236,12 @@ public class XCreatorUtils { } public static XCreator createThemedXCreator(Widget widget) { - XCreator creator = createXCreator(widget); JTemplate template = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); TemplateTheme theme = template.getTemplateTheme(); if (theme instanceof FormTheme) { - setupTemplateTheme(creator, true, (FormTheme) theme, TemplateThemeCompatible.NONE); + widget = setupTemplateTheme(widget, true, (FormTheme) theme, TemplateThemeCompatible.NONE); } - return creator; + return createXCreator(widget); } /** @@ -386,14 +386,12 @@ public class XCreatorUtils { } - public static void setupTemplateTheme(XCreator container, final boolean forceFollowingTheme, final FormTheme currentTemplateUsingTheme, TemplateThemeCompatible compatible) { - FineColorGather colorGather = new FineColorManager.FineColorReplaceByTheme(currentTemplateUsingTheme, compatible); - Widget root = container.toData(); - List chartCollections = root.getChartCollections(); + public static Widget setupTemplateTheme(Widget widget, final boolean forceFollowingTheme, final FormTheme currentTemplateUsingTheme, TemplateThemeCompatible compatible) { + List chartCollections = widget.getChartCollections(); for (BaseChartCollection chartCollection: chartCollections) { chartCollection.onTemplateUsingThemeChange(currentTemplateUsingTheme, compatible); } - Form.traversalWidget(root, new WidgetGather() { + Form.traversalWidget(widget, new WidgetGather() { @Override public void dealWith(Widget widget) { TemplateThemeAware themedWidget = (TemplateThemeAware) widget; @@ -408,8 +406,7 @@ public class XCreatorUtils { return true; } }, TemplateThemeAware.class); - FineColorFlushUtils.replaceCacheObject(container.toData(), colorGather); - FineColorManager.traverse(container.toData(), colorGather); + return (Widget) FineColorSynchronizer.flush(widget, currentTemplateUsingTheme, compatible); } @@ -472,4 +469,21 @@ public class XCreatorUtils { } return container; } + + + /** + * 循环遍历组件,调整组件间隙 + * @param creator + */ + public static void traverAndAdjust(XCreator creator) { + for (int i = 0; i < creator.getComponentCount(); i++) { + Object object = creator.getComponent(i); + if (object instanceof XCreator) { + XCreator temp = (XCreator) object; + temp.adjustCompSize(0.0); + traverAndAdjust(temp); + } + } + + } } \ No newline at end of file diff --git a/designer-form/src/main/java/com/fr/design/designer/creator/XWAbsoluteLayout.java b/designer-form/src/main/java/com/fr/design/designer/creator/XWAbsoluteLayout.java index 5bec34d37..0067a50f5 100644 --- a/designer-form/src/main/java/com/fr/design/designer/creator/XWAbsoluteLayout.java +++ b/designer-form/src/main/java/com/fr/design/designer/creator/XWAbsoluteLayout.java @@ -7,7 +7,7 @@ import com.fr.base.GraphHelper; import com.fr.design.designer.beans.LayoutAdapter; import com.fr.design.designer.beans.adapters.layout.FRAbsoluteLayoutAdapter; import com.fr.design.designer.beans.location.Direction; -import com.fr.design.designer.creator.cardlayout.XWTabFitLayout; +import com.fr.design.designer.creator.cardlayout.XWCardMainBorderLayout; import com.fr.design.designer.properties.mobile.MobileBooKMarkUsePropertyUI; import com.fr.design.form.layout.FRAbsoluteLayout; import com.fr.design.form.util.FormDesignerUtils; @@ -34,7 +34,6 @@ import java.awt.Rectangle; import java.awt.event.ContainerEvent; import java.awt.event.MouseEvent; import java.beans.IntrospectionException; -import java.util.ArrayList; import java.util.HashMap; /** @@ -119,12 +118,8 @@ public class XWAbsoluteLayout extends XLayoutContainer { ((XWAbsoluteLayout) xCreator).updateBoundsWidget(); } // 如果子组件时tab布局,则tab布局内部的组件的wiget也要更新,否则保存后重新打开大小不对 - ArrayList 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/XWCardLayout.java b/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardLayout.java index 578b509ec..1f9eb4572 100644 --- a/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardLayout.java +++ b/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardLayout.java @@ -7,6 +7,7 @@ import com.fr.base.background.ColorBackground; import com.fr.design.designer.beans.LayoutAdapter; import com.fr.design.designer.beans.adapters.layout.FRCardLayoutAdapter; import com.fr.design.designer.beans.events.DesignerEvent; +import com.fr.design.designer.beans.models.ModelUtil; import com.fr.design.designer.beans.models.SelectionModel; import com.fr.design.designer.creator.CRPropertyDescriptor; import com.fr.design.designer.creator.XCreator; @@ -21,6 +22,7 @@ import com.fr.design.mainframe.WidgetPropertyPane; import com.fr.design.mainframe.widget.editors.BooleanEditor; import com.fr.design.mainframe.widget.editors.CardTagWLayoutBorderStyleEditor; import com.fr.design.mainframe.widget.editors.DoubleEditor; +import com.fr.form.main.Form; import com.fr.form.ui.CardAddButton; import com.fr.form.ui.CardSwitchButton; import com.fr.form.ui.LayoutBorderStyle; @@ -39,6 +41,7 @@ import com.fr.general.cardtag.DefaultTemplateStyle; import com.fr.general.cardtag.TemplateStyle; import com.fr.stable.ArrayUtils; import com.fr.stable.Constants; +import com.fr.stable.StringUtils; import com.fr.stable.core.PropertyChangeAdapter; import javax.swing.border.Border; @@ -246,7 +249,13 @@ public class XWCardLayout extends XLayoutContainer { */ @Override protected void setWrapperName(XLayoutContainer parentPanel, String widgetName) { - parentPanel.toData().setWidgetName("tablayout" + widgetName.replaceAll(createDefaultName(), "")); + FormDesigner formDesigner = WidgetPropertyPane.getInstance().getEditingFormDesigner(); + Form form = formDesigner.getTarget(); + String name = "tablayout" + widgetName.replaceAll(createDefaultName(), StringUtils.EMPTY); + if (form.isNameExist(name)) { + name = ModelUtil.uniqueName(formDesigner.getTarget(), parentPanel.createDefaultName()); + } + parentPanel.toData().setWidgetName(name); } /** 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 e09b31fb0..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; @@ -52,6 +53,8 @@ import java.util.List; * @date: 2014-12-9-下午9:59:31 */ public class XWCardMainBorderLayout extends XWBorderLayout { + private static final String DEFAULT_NAME = "tablayout"; + private static final int BORDER_WIDTH = 4; private static final Color OUTER_BORDER_COLOR = new Color(65, 155, 249, 30); @@ -85,6 +88,17 @@ public class XWCardMainBorderLayout extends XWBorderLayout { return (WCardMainBorderLayout) super.toData(); } + /** + * 获取默认名字 + * + * @return 默认名 + * @date 2014-11-25-下午6:22:40 + */ + @Override + public String createDefaultName() { + return DEFAULT_NAME; + } + /** * 添加标题区域 * @@ -425,6 +439,7 @@ public class XWCardMainBorderLayout extends XWBorderLayout { } xwCardTitleLayout.setBounds(parentBounds); this.addCardPart((XWCardLayout)this.getComponent(0)); + LayoutUtils.layoutContainer(this); } @Override @@ -448,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/gui/xpane/LayoutBorderPane.java b/designer-form/src/main/java/com/fr/design/gui/xpane/LayoutBorderPane.java index cf460f9ce..7a00075d6 100644 --- a/designer-form/src/main/java/com/fr/design/gui/xpane/LayoutBorderPane.java +++ b/designer-form/src/main/java/com/fr/design/gui/xpane/LayoutBorderPane.java @@ -333,8 +333,8 @@ public class LayoutBorderPane extends BasicPane { JPanel rightPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); defaultPane.add(rightPane, BorderLayout.EAST); rightPane.add(initRightBottomPane(), BorderLayout.CENTER); - JTemplate jTemplate = HistoryTemplateListPane.getInstance().getCurrentEditingTemplate(); - if (!jTemplate.isJWorkBook() && ((JForm)jTemplate).isSelectRootPane()){ + JTemplate jTemplate = HistoryTemplateListPane.getInstance().getCurrentEditingTemplate(); + if (!jTemplate.isJWorkBook() && jTemplate instanceof JForm && ((JForm)jTemplate).isSelectRootPane()){ //界面上表单主体只有背景和透明度可以设置 rightPane.add(initBodyRightTopPane(), BorderLayout.NORTH); } else { diff --git a/designer-form/src/main/java/com/fr/design/gui/xpane/LayoutStylePane.java b/designer-form/src/main/java/com/fr/design/gui/xpane/LayoutStylePane.java index 4a034e9be..5cbddadb4 100644 --- a/designer-form/src/main/java/com/fr/design/gui/xpane/LayoutStylePane.java +++ b/designer-form/src/main/java/com/fr/design/gui/xpane/LayoutStylePane.java @@ -66,8 +66,10 @@ public class LayoutStylePane extends BasicBeanPane { this.setLayout(FRGUIPaneFactory.createBorderLayout()); this.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - JTemplate currentEditingTemplate = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); - boolean isRootLayout = currentEditingTemplate != null && !currentEditingTemplate.isJWorkBook() && ((JForm)currentEditingTemplate).isSelectRootPane(); + JTemplate currentEditingTemplate = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + boolean isRootLayout = currentEditingTemplate != null && + !currentEditingTemplate.isJWorkBook() && + currentEditingTemplate instanceof JForm && ((JForm)currentEditingTemplate).isSelectRootPane(); namedTitleStylePane = createNamedTitleStylePane(isRootLayout); namedBodyStylePane = createNamedBodyStylePane(isRootLayout); 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 d989aae21..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); - } - } - - } - /** * 增加刻度条 */ @@ -771,15 +759,24 @@ public class FormArea extends JComponent implements ScrollRulerComponent { heightPane.setValue(height); reCalculateHeight((int) height); } - if (designer.getRootComponent().acceptType(XWFitLayout.class) ) { - XWFitLayout layout = (XWFitLayout) designer.getRootComponent(); - // 撤销时先refreshRoot了,此处去掉内边距再增加间隔 - layout.moveContainerMargin(); - layout.addCompInterval(layout.getAcualInterval()); - } + traverAndAdjust(designer.getRootComponent()); this.slidePane.setShowValue((int) slide); } + // 撤销时先refreshRoot了,此处去掉内边距再增加间隔,循环遍历布局,调整fitlayout布局的间隔 + private void traverAndAdjust(XCreator creator) { + if (creator instanceof XWFitLayout) { + XWFitLayout temp = (XWFitLayout) creator; + temp.moveContainerMargin(); + temp.addCompInterval(temp.getAcualInterval()); + } + for (int i = 0; i < creator.getComponentCount(); i++) { + Object object = creator.getComponent(i); + if (object instanceof XCreator){ + traverAndAdjust((XCreator) object); + } + } + } /** * 计算滚动条的值和max diff --git a/designer-form/src/main/java/com/fr/design/mainframe/FormParaWidgetPane.java b/designer-form/src/main/java/com/fr/design/mainframe/FormParaWidgetPane.java index 2f78a98f0..57e49780c 100644 --- a/designer-form/src/main/java/com/fr/design/mainframe/FormParaWidgetPane.java +++ b/designer-form/src/main/java/com/fr/design/mainframe/FormParaWidgetPane.java @@ -199,7 +199,9 @@ public class FormParaWidgetPane extends JPanel { JPanel componentsPara = new JPanel(new FlowLayout(FlowLayout.LEFT)); WidgetOption[] chartOptions = loadChartOptions(); for (WidgetOption chartOption : chartOptions) { - componentsPara.add(new ToolBarButton(chartOption)); + ToolBarButton button = new ToolBarButton(chartOption); + button.setPreferredSize(new Dimension(widgetButtonWidth, widgetButtonHeight)); + componentsPara.add(button); } int x = COMMON_CHAR_NUM * (widgetButtonWidth + smallGAP); int y = (int) Math.ceil(chartOptions.length / ((double) COMMON_CHAR_NUM)) * (widgetButtonHeight + smallGAP); 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/mainframe/JForm.java b/designer-form/src/main/java/com/fr/design/mainframe/JForm.java index 493a7fa74..0f5b90307 100644 --- a/designer-form/src/main/java/com/fr/design/mainframe/JForm.java +++ b/designer-form/src/main/java/com/fr/design/mainframe/JForm.java @@ -6,6 +6,9 @@ import com.fr.base.Parameter; import com.fr.base.Releasable; import com.fr.base.extension.FileExtension; import com.fr.base.iofile.attr.ExtendSharableAttrMark; +import com.fr.base.theme.FineColorGather; +import com.fr.base.theme.FineColorManager; +import com.fr.base.theme.FineColorSynchronizer; import com.fr.base.theme.FormTheme; import com.fr.base.theme.TemplateTheme; import com.fr.base.theme.TemplateThemeCompatible; @@ -1210,6 +1213,7 @@ public class JForm extends JTemplate implements BaseJForm implements BaseJForm template = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); TemplateTheme theme = template.getTemplateTheme(); if (theme instanceof FormTheme) { @@ -64,8 +63,9 @@ public class ShareComponentUtils { StringUtils.isNotEmpty(suitableTemplateThemeName) && StringUtils.equals(theme.getName(), suitableTemplateThemeName) && !StringUtils.equals(theme.getName(), themeName4LegacyTemplate); - XCreatorUtils.setupTemplateTheme(creator, false, (FormTheme) theme, isCurrentUsingThemeSuitSharedComponent ? TemplateThemeCompatible.NONE : TemplateThemeCompatible.ABSENT); + creatorSource = XCreatorUtils.setupTemplateTheme(creatorSource, false, (FormTheme) theme, isCurrentUsingThemeSuitSharedComponent ? TemplateThemeCompatible.NONE : TemplateThemeCompatible.ABSENT); } + XCreator creator = createXCreator(creatorSource, shareId, provider); return creator; } 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 jTemplate = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + if (jTemplate.isJWorkBook() || !((jTemplate instanceof JForm) && ((JForm)jTemplate).isSelectRootPane())) { ratioLockedButton = new AspectRatioLockedButton(width, height); ratioLockedButton.setGlobalName(i18nText("Fine-Design_Form_Coords_And_Size")); ratioLockedButton.setLockEnabled(false); diff --git a/designer-form/src/test/java/com/fr/design/form/util/HtmlPaintUtilsTest.java b/designer-form/src/test/java/com/fr/design/form/util/HtmlPaintUtilsTest.java new file mode 100644 index 000000000..445e865ab --- /dev/null +++ b/designer-form/src/test/java/com/fr/design/form/util/HtmlPaintUtilsTest.java @@ -0,0 +1,49 @@ +package com.fr.design.form.util; + +import com.fr.base.Style; +import junit.framework.TestCase; +import org.junit.Assert; + + +/** + * @author hades + * @version 11.0 + * Created by hades on 2022/5/25 + */ +public class HtmlPaintUtilsTest extends TestCase { + + public void testDeriveMaxFontFromRichChar() { + // 富文本字体size更大 + String testHtml0 = "这是一条测试数据"; + 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/JWorkBook.java b/designer-realize/src/main/java/com/fr/design/mainframe/JWorkBook.java index 20b674a48..29af88fe5 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/JWorkBook.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/JWorkBook.java @@ -6,6 +6,9 @@ import com.fr.base.Parameter; import com.fr.base.ScreenResolution; import com.fr.base.TRL; import com.fr.base.extension.FileExtension; +import com.fr.base.theme.FineColorGather; +import com.fr.base.theme.FineColorManager; +import com.fr.base.theme.FineColorSynchronizer; import com.fr.base.theme.ReportTheme; import com.fr.base.theme.TemplateTheme; import com.fr.base.theme.TemplateThemeCompatible; @@ -77,6 +80,7 @@ import com.fr.file.FILEChooserPane; import com.fr.file.FileNodeFILE; import com.fr.file.filetree.FileNode; import com.fr.file.filter.ChooseFileFilter; +import com.fr.form.main.Form; import com.fr.general.ComparatorUtils; import com.fr.general.ModuleContext; import com.fr.grid.Grid; @@ -1289,6 +1293,7 @@ public class JWorkBook extends JTemplate { protected void setUpTheme4NewTemplate() { super.setUpTheme4NewTemplate(); getTarget().setTemplateTheme(getTarget().getTemplateTheme()); + setTarget((WorkBook) FineColorSynchronizer.flush(getTarget(), getTemplateTheme())); } @@ -1308,6 +1313,7 @@ public class JWorkBook extends JTemplate { boolean shouldCreateUndoState = compatible == TemplateThemeCompatible.NONE && !StringUtils.equals(oldTheme.getName(), newTheme.getName()); getTarget().setTemplateTheme(newTheme, compatible); + setTarget((WorkBook) FineColorSynchronizer.flush(getTarget(), newTheme, compatible)); if (HistoryTemplateListCache.getInstance().getCurrentEditingTemplate() == this) { fireTargetModified(shouldCreateUndoState); diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/AlphaFineConstants.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/AlphaFineConstants.java index f4440fb5c..8de2a0d63 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/AlphaFineConstants.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/AlphaFineConstants.java @@ -48,6 +48,17 @@ public class AlphaFineConstants { public static final int HOT_ITEMS = 6; + /** + * 限制搜索字符长度 + */ + public static final int LEN_LIMIT = 50; + + /** + * 帮助文档搜索间隔(ms) api限制了1s之内只能搜索一次 + * + */ + public static final long DOCUMENT_SEARCH_GAP = 1000; + public static final Dimension FULL_SIZE = new Dimension(680, 460); @@ -180,7 +191,9 @@ public class AlphaFineConstants { public static final Icon BULB_ICON = IconUtils.readIcon("com/fr/design/mainframe/alphafine/images/bulb.svg"); - public static final Icon BLUE_BULB_ICON = IconUtils.readIcon("com/fr/design/mainframe/alphafine/images/blue_bulb.svg"); + public static final Icon YELLOW_BULB_ICON = IconUtils.readIcon("com/fr/design/mainframe/alphafine/images/yellow_bulb.svg"); + + public static final Icon LIGHT_YELLOW_BULB_ICON = IconUtils.readIcon("com/fr/design/mainframe/alphafine/images/light_yellow_bulb.svg"); public static final String HOT_SEARCH = Toolkit.i18nText("Fine-Design_Report_AlphaFine_Hot_Search"); diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/AlphaFineUtil.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/AlphaFineUtil.java index 0d23a6fc9..5648ab98d 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/AlphaFineUtil.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/AlphaFineUtil.java @@ -2,6 +2,7 @@ package com.fr.design.mainframe.alphafine; import com.fr.design.DesignerEnvManager; import com.fr.design.mainframe.alphafine.search.manager.impl.ProductNewsSearchManager; +import com.fr.stable.ArrayUtils; import com.fr.stable.StringUtils; import java.util.Set; @@ -13,10 +14,15 @@ import java.util.Set; public class AlphaFineUtil { public static String highLightModelName(String modelName, String[] strings) { - if (strings == null) { + if (strings == null || ArrayUtils.isEmpty(strings)) { return modelName; } for (String string : strings) { + // 高亮分词 跳过高亮颜色本身的字符 + boolean skipHighLight = modelName.contains(AlphaFineConstants.HIGH_LIGHT_COLOR) && AlphaFineConstants.HIGH_LIGHT_COLOR.contains(string); + if (skipHighLight) { + continue; + } String primaryStr = getReplacedString(modelName, string); if (StringUtils.isNotEmpty(primaryStr)) { modelName = modelName.replaceAll("(?i)" + primaryStr, "|" + primaryStr + "|"); @@ -32,6 +38,10 @@ public class AlphaFineUtil { private static String getReplacedString(String modelName, String string) { + // 如果是直接包含了高亮字符 返回 + if (StringUtils.contains(modelName, string)) { + return string; + } //需要考虑modelName有空格的情况 //比如现在是work boo k 搜索词是workb,应该要替换的部分是work b //先去掉已经匹配替换过的部分,因为考虑到分词的情况,可能会进行多次替换 @@ -53,6 +63,18 @@ public class AlphaFineUtil { return result.toString(); } + public static String escapeExprSpecialWord(String keyword) { + if (StringUtils.isNotBlank(keyword)) { + String[] fbsArr = { "\\", "$", "(", ")", "*", "+", ".", "[", "]", "?", "^", "{", "}", "|" }; + for (String key : fbsArr) { + if (keyword.contains(key)) { + keyword = keyword.replace(key, "\\" + key); + } + } + } + return keyword; + } + public static boolean unread() { Set readSet = DesignerEnvManager.getEnvManager().getAlphaFineConfigManager().getReadSet(); Set idSet = ProductNewsSearchManager.getInstance().getIdSet(); diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/AlphaFineFrame.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/AlphaFineFrame.java index d93a7b296..b6819f56c 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/AlphaFineFrame.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/AlphaFineFrame.java @@ -33,6 +33,7 @@ import com.fr.design.mainframe.alphafine.search.manager.impl.PluginSearchManager import com.fr.design.mainframe.alphafine.search.manager.impl.ProductNewsSearchManager; import com.fr.design.mainframe.alphafine.search.manager.impl.SegmentationManager; import com.fr.design.utils.DesignUtils; +import com.fr.design.utils.gui.GUICoreUtils; import com.fr.general.ComparatorUtils; import com.fr.stable.StringUtils; import java.awt.BorderLayout; @@ -66,8 +67,11 @@ import javax.swing.Icon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; +import javax.swing.JPopupMenu; import javax.swing.SwingConstants; import javax.swing.Timer; +import javax.swing.event.PopupMenuEvent; +import javax.swing.event.PopupMenuListener; /** * @author hades @@ -146,6 +150,8 @@ public class AlphaFineFrame extends JFrame { private UILabel clearLabel; + private JPanel tabPane; + private CellType selectedType; private String beforeSearchStr = StringUtils.EMPTY; @@ -239,13 +245,10 @@ public class AlphaFineFrame extends JFrame { topRightPane.setBackground(Color.WHITE); JPanel tipPane = new JPanel(new BorderLayout()); tipPane.setBackground(Color.WHITE); - String toolTip = AlphaFineShortCutUtil.getDisplayShortCut(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_AlphaFine_Short_Cut", DesignerEnvManager.getEnvManager().getAlphaFineConfigManager().getShortcuts())); tipIconLabel = new UILabel(AlphaFineConstants.BULB_ICON); tipIconLabel.addMouseListener(tipMouseListener); - tipIconLabel.setToolTipText(toolTip); useTipLabel = new UILabel(SKILLS); useTipLabel.addMouseListener(tipMouseListener); - useTipLabel.setToolTipText(toolTip); useTipLabel.setForeground(AlphaFineConstants.FOREGROUND_COLOR_6); tipPane.add(tipIconLabel, BorderLayout.WEST); tipPane.add(useTipLabel, BorderLayout.CENTER); @@ -262,19 +265,63 @@ public class AlphaFineFrame extends JFrame { private MouseAdapter tipMouseListener = new MouseAdapter() { + private JPopupMenu popupMenu; + @Override public void mouseEntered(MouseEvent e) { - useTipLabel.setForeground(UIConstants.FLESH_BLUE); - tipIconLabel.setIcon(AlphaFineConstants.BLUE_BULB_ICON); + tipIconLabel.setIcon(AlphaFineConstants.YELLOW_BULB_ICON); } @Override public void mouseExited(MouseEvent e) { - useTipLabel.setForeground(AlphaFineConstants.FOREGROUND_COLOR_6); - tipIconLabel.setIcon(AlphaFineConstants.BULB_ICON); + if (popupMenu == null || !popupMenu.isShowing()) { + useTipLabel.setForeground(AlphaFineConstants.FOREGROUND_COLOR_6); + tipIconLabel.setIcon(AlphaFineConstants.BULB_ICON); + } + } + + @Override + public void mousePressed(MouseEvent e) { + useTipLabel.setForeground(UIConstants.FLESH_BLUE); + tipIconLabel.setIcon(AlphaFineConstants.LIGHT_YELLOW_BULB_ICON); + popupMenu = createTipPop(); + GUICoreUtils.showPopupMenu(popupMenu, e.getComponent(), e.getComponent().getX() - 60, e.getComponent().getY() + 20); } }; + + private JPopupMenu createTipPop() { + JPanel panel = new JPanel(new BorderLayout()); + String toolTip = AlphaFineShortCutUtil.getDisplayShortCut(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_AlphaFine_Short_Cut", DesignerEnvManager.getEnvManager().getAlphaFineConfigManager().getShortcuts())); + UILabel label = new UILabel(toolTip); + label.setForeground(AlphaFineConstants.FOREGROUND_COLOR_8); + label.setBackground(Color.WHITE); + panel.add(label); + panel.setBackground(Color.WHITE); + JPopupMenu popupMenu = new JPopupMenu(); + popupMenu.setBorder(BorderFactory.createEmptyBorder(20, 5, 10, 5)); + popupMenu.add(panel); + popupMenu.setBackground(Color.WHITE); + popupMenu.addPopupMenuListener(new PopupMenuListener() { + @Override + public void popupMenuWillBecomeVisible(PopupMenuEvent e) { + // do nothing + } + + @Override + public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { + useTipLabel.setForeground(AlphaFineConstants.FOREGROUND_COLOR_6); + tipIconLabel.setIcon(AlphaFineConstants.BULB_ICON); + } + + @Override + public void popupMenuCanceled(PopupMenuEvent e) { + // do nothing + } + }); + return popupMenu; + } + private JPanel createSearchPane() { JPanel searchPane = new JPanel(new BorderLayout()); searchPane.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 20)); @@ -365,8 +412,89 @@ public class AlphaFineFrame extends JFrame { labelPane.add(labelContentPane); labelPane.setPreferredSize(new Dimension(AlphaFineConstants.FULL_SIZE.width, 30)); - JPanel tabPane = new JPanel(new FlowLayout(FlowLayout.LEFT, 20, 10)); + tabPane = new JPanel(new FlowLayout(FlowLayout.LEFT, 20, 10)); tabPane.setBackground(Color.WHITE); + List selectedLabelList = createSelectedLabelList(); + selectedType = selectedLabelList.get(0).getCellType(); + // 第一个tab 非产品动态 + if (selectedType != CellType.PRODUCT_NEWS) { + tabLabel.setText(selectedLabelList.get(0).getText()); + readLabel.setVisible(false); + } + for (SelectedLabel selectedLabel : selectedLabelList) { + selectedLabel.addMouseListener(createMouseListener(selectedLabelList, selectedLabel, tabPane, tabLabel, readLabel)); + tabPane.add(selectedLabel); + } + showPane.add(tabPane, BorderLayout.NORTH); + showPane.add(labelPane, BorderLayout.CENTER); + showPane.add(resultPane, BorderLayout.SOUTH); + return showPane; + } + + private MouseAdapter createMouseListener(List selectedLabelList, SelectedLabel selectedLabel, + JPanel tabPane, UILabel tabLabel, UILabel readLabel) { + return new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + for (SelectedLabel label : selectedLabelList) { + label.setSelected(false); + label.setForeground(AlphaFineConstants.FOREGROUND_COLOR_8); + } + selectedLabel.setSelected(true); + // 处理产品动态 tab与下方文字展示不一致 + if (ComparatorUtils.equals(selectedLabel.getText().trim(), PRODUCT_NEWS)) { + tabLabel.setText(PRODUCT_DYNAMICS); + } else { + tabLabel.setText(selectedLabel.getText()); + } + readLabel.setVisible(false); + tabPane.repaint(); + switch (selectedLabel.getCellType()) { + case PRODUCT_NEWS: + readLabel.setVisible(true); + switchType(CellType.PRODUCT_NEWS); + break; + case ACTION: + currentSearchWorkerManager = settingSearchWorkerManager; + switchType(CellType.ACTION); + break; + case FILE: + currentSearchWorkerManager = fileSearchWorkerManager; + switchType(CellType.FILE); + break; + case DOCUMENT: + currentSearchWorkerManager = documentWorkerManager; + switchType(CellType.DOCUMENT); + break; + case PLUGIN: + currentSearchWorkerManager = pluginSearchWorkerManager; + switchType(CellType.PLUGIN); + break; + } + if (currentSearchWorkerManager != null) { + AlphaFineList alphaFineList = currentSearchWorkerManager.getSearchResultList(); + if (alphaFineList != null) { + alphaFineList.setSelectedIndex(0); + } + } + } + + private Color defaultColor; + + @Override + public void mouseEntered(MouseEvent e) { + defaultColor = selectedLabel.getForeground(); + selectedLabel.setForeground(AlphaFineConstants.SUSPENDED_COLOR); + } + + @Override + public void mouseExited(MouseEvent e) { + selectedLabel.setForeground(defaultColor); + } + }; + } + + private List createSelectedLabelList() { List selectedLabelList = new ArrayList<>(); AlphaFineConfigManager alphaFineConfigManager = DesignerEnvManager.getEnvManager().getAlphaFineConfigManager(); if (alphaFineConfigManager.isProductDynamics()) { @@ -384,79 +512,7 @@ public class AlphaFineFrame extends JFrame { if (alphaFineConfigManager.isContainPlugin()) { selectedLabelList.add(new SelectedLabel(PLUGIN, CellType.PLUGIN)); } - selectedType = selectedLabelList.get(0).getCellType(); - // 第一个tab 非产品动态 - if (selectedType != CellType.PRODUCT_NEWS) { - tabLabel.setText(selectedLabelList.get(0).getText()); - readLabel.setVisible(false); - } - for (SelectedLabel selectedLabel : selectedLabelList) { - - selectedLabel.addMouseListener(new MouseAdapter() { - @Override - public void mousePressed(MouseEvent e) { - for (SelectedLabel label : selectedLabelList) { - label.setSelected(false); - label.setForeground(AlphaFineConstants.FOREGROUND_COLOR_8); - } - selectedLabel.setSelected(true); - // 处理产品动态 tab与下方文字展示不一致 - if (ComparatorUtils.equals(selectedLabel.getText().trim(), PRODUCT_NEWS)) { - tabLabel.setText(PRODUCT_DYNAMICS); - } else { - tabLabel.setText(selectedLabel.getText()); - } - readLabel.setVisible(false); - tabPane.repaint(); - switch (selectedLabel.getCellType()) { - case PRODUCT_NEWS: - readLabel.setVisible(true); - switchType(CellType.PRODUCT_NEWS); - break; - case ACTION: - currentSearchWorkerManager = settingSearchWorkerManager; - switchType(CellType.ACTION); - break; - case FILE: - currentSearchWorkerManager = fileSearchWorkerManager; - switchType(CellType.FILE); - break; - case DOCUMENT: - currentSearchWorkerManager = documentWorkerManager; - switchType(CellType.DOCUMENT); - break; - case PLUGIN: - currentSearchWorkerManager = pluginSearchWorkerManager; - switchType(CellType.PLUGIN); - break; - } - if (currentSearchWorkerManager != null) { - AlphaFineList alphaFineList = currentSearchWorkerManager.getSearchResultList(); - if (alphaFineList != null) { - alphaFineList.setSelectedIndex(0); - } - } - } - - private Color defaultColor; - - @Override - public void mouseEntered(MouseEvent e) { - defaultColor = selectedLabel.getForeground(); - selectedLabel.setForeground(AlphaFineConstants.SUSPENDED_COLOR); - } - - @Override - public void mouseExited(MouseEvent e) { - selectedLabel.setForeground(defaultColor); - } - }); - tabPane.add(selectedLabel); - } - showPane.add(tabPane, BorderLayout.NORTH); - showPane.add(labelPane, BorderLayout.CENTER); - showPane.add(resultPane, BorderLayout.SOUTH); - return showPane; + return selectedLabelList; } private void fireOneClickRead() { @@ -615,6 +671,7 @@ public class AlphaFineFrame extends JFrame { clearLabel.setVisible(true); SearchTooltipPopup.getInstance().show(searchTextFieldWrapperPane); } + tabPane.repaint(); }); timer.start(); @@ -635,28 +692,58 @@ public class AlphaFineFrame extends JFrame { beforeSearchStr = StringUtils.EMPTY; return; } + String lowerCaseSearchText = preProcessSearchText(searchTextField.getText().toLowerCase()); if (DesignerEnvManager.getEnvManager().getAlphaFineConfigManager().isNeedSegmentationCheckbox()) { - //是高级搜索 - if (searchTextField.getText().toLowerCase().startsWith(ADVANCED_SEARCH_MARK)) { - segmentationResult = SegmentationManager.getInstance().startSegmentation(getStoreText(searchTextField.getText().toLowerCase())); - } - //是普通搜索 - else { - segmentationResult = SegmentationManager.getInstance().startSegmentation(searchTextField.getText().toLowerCase()); - } + dealSegmentationSearch(lowerCaseSearchText); } else { - if (StringUtils.isEmpty(getRealSearchText(searchTextField.getText()))) { - segmentationResult = null; - } else { - segmentationResult = new String[]{getRealSearchText(searchTextField.getText())}; - } + dealNormalSearch(); } DesignerEnvManager.getEnvManager().getAlphaFineConfigManager().getHistorySearch().push(searchTextField.getText()); - doSearch(searchTextField.getText().toLowerCase()); + doSearch(lowerCaseSearchText); beforeSearchStr = searchTextField.getText(); SearchTooltipPopup.getInstance().hide(); } + /** + * 普通搜索 + */ + private void dealNormalSearch() { + String searchText = preProcessSearchText(searchTextField.getText()); + if (StringUtils.isEmpty(getRealSearchText(searchText))) { + segmentationResult = null; + } else { + segmentationResult = new String[]{getRealSearchText(searchText)}; + } + } + + /** + * 分词搜索 + */ + private void dealSegmentationSearch(String lowerCaseSearchText) { + //是高级搜索 + if (searchTextField.getText().toLowerCase().startsWith(ADVANCED_SEARCH_MARK)) { + segmentationResult = SegmentationManager.getInstance().startSegmentation(getStoreText(lowerCaseSearchText)); + } + //是普通搜索 + else { + segmentationResult = SegmentationManager.getInstance().startSegmentation(lowerCaseSearchText); + } + } + + /** + * 文本预处理 限制长度 + * + * @param text + * @return + */ + private String preProcessSearchText(String text) { + if (text.length() > AlphaFineConstants.LEN_LIMIT) { + return text.substring(0, AlphaFineConstants.LEN_LIMIT); + } else { + return text; + } + } + private void dealWithSearchResult() { final AlphaCellModel model = searchResultList.getSelectedValue(); if (model != null) { @@ -699,7 +786,7 @@ public class AlphaFineFrame extends JFrame { } else if (searchText.startsWith(DS_MARK)) { return new SearchTextBean(getStoreText(searchText), new String[]{DS_NAME + getStoreText(searchText)}); } else { - return new SearchTextBean(searchText, segmentationResult); + return new SearchTextBean(searchText, segmentationResult == null ? new String[]{} : segmentationResult); } } @@ -708,7 +795,7 @@ public class AlphaFineFrame extends JFrame { * */ private void reSearch() { - String text = this.searchTextField.getText().toLowerCase(); + String text = preProcessSearchText(this.searchTextField.getText().toLowerCase()); if (StringUtils.isEmpty(text)) { return; } 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/mainframe/alphafine/question/QuestionPane.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/question/QuestionPane.java index 0d0d9ab63..b2c186a9e 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/question/QuestionPane.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/question/QuestionPane.java @@ -2,6 +2,7 @@ package com.fr.design.mainframe.alphafine.question; import com.fr.base.svg.SVGLoader; import com.fr.base.svg.SystemScaleUtils; +import com.fr.design.DesignerEnvManager; import com.fr.design.mainframe.alphafine.AlphaFineUtil; import com.fr.design.utils.SvgPaintUtils; import java.awt.Color; @@ -37,7 +38,7 @@ public class QuestionPane extends JPanel { int width = SystemScaleUtils.isJreHiDPIEnabled() ? (int) (getWidth() * SVGLoader.SYSTEM_SCALE) : getWidth(); int height = SystemScaleUtils.isJreHiDPIEnabled() ? (int) (getHeight() * SVGLoader.SYSTEM_SCALE) : getHeight(); - if (AlphaFineUtil.unread()) { + if (AlphaFineUtil.unread() && DesignerEnvManager.getEnvManager().getAlphaFineConfigManager().isProductDynamics()) { g2.drawImage(NEW_MESSAGE_IMAGE, 0, 0, this); } else { g2.drawImage(QUESTION_BACKGROUND_IMAGE, 0, 0, this); diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/question/QuestionWindow.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/question/QuestionWindow.java index 079eecadb..e5d9edfec 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/question/QuestionWindow.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/question/QuestionWindow.java @@ -1,5 +1,6 @@ package com.fr.design.mainframe.alphafine.question; +import com.fr.design.DesignerEnvManager; import com.fr.design.i18n.Toolkit; import com.fr.design.mainframe.DesignerContext; import com.fr.design.mainframe.alphafine.AlphaFineHelper; @@ -55,6 +56,7 @@ public class QuestionWindow extends JWindow { @Override public void windowDeactivated(WindowEvent e) { + QuestionWindow.getInstance().dispose(); QuestionWindow.getInstance().setVisible(false); } }); @@ -67,6 +69,14 @@ public class QuestionWindow extends JWindow { DesignerContext.getDesignerFrame().getHeight() - 100); } + @Override + public void setVisible(boolean visible) { + if (visible && !DesignerEnvManager.getEnvManager().getAlphaFineConfigManager().isEnabled()) { + return; + } + super.setVisible(visible); + } + public static QuestionWindow getInstance() { return INSTANCE; } diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/search/manager/impl/ActionSearchManager.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/search/manager/impl/ActionSearchManager.java index a2a1a7037..54d6d1402 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/search/manager/impl/ActionSearchManager.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/search/manager/impl/ActionSearchManager.java @@ -17,7 +17,9 @@ import com.fr.json.JSONObject; import com.fr.stable.ArrayUtils; import com.fr.stable.StringUtils; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** * Created by XiaXiang on 2017/3/27. @@ -68,12 +70,17 @@ public class ActionSearchManager implements AlphaFineSearchProvider { return lessModelList; } List updateActions = UpdateActionManager.getUpdateActionManager().getUpdateActions(); + Set searchKeySet = new HashSet<>(); for (UpdateActionModel updateActionModel : updateActions) { for (int j = 0; j < searchText.length; j++) { AlphaFineHelper.checkCancel(); if (StringUtils.isNotBlank(updateActionModel.getSearchKey())) { - if (updateActionModel.getSearchKey().contains(searchText[j]) && updateActionModel.getAction().isEnabled()) { + if (updateActionModel.getSearchKey().contains(searchText[j]) + && updateActionModel.getAction().isEnabled() + // 老alphaFine就有问题 加个去重 + && !searchKeySet.contains(updateActionModel.getSearchKey())) { filterModelList.add(new ActionModel(updateActionModel.getActionName(), updateActionModel.getParentName(), updateActionModel.getAction())); + searchKeySet.add(updateActionModel.getSearchKey()); } } } diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/search/manager/impl/DocumentSearchManager.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/search/manager/impl/DocumentSearchManager.java index bc3b214d0..3ea1f2db2 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/search/manager/impl/DocumentSearchManager.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/search/manager/impl/DocumentSearchManager.java @@ -15,6 +15,7 @@ import com.fr.json.JSONException; import com.fr.json.JSONObject; import com.fr.log.FineLoggerFactory; import com.fr.stable.ArrayUtils; +import java.util.concurrent.TimeUnit; /** @@ -67,6 +68,7 @@ public class DocumentSearchManager implements AlphaFineSearchProvider { } searchResult = new SearchResult(); for (int j = 0; j < searchText.length; j++) { + long start = System.currentTimeMillis(); String url = AlphaFineConstants.DOCUMENT_SEARCH_URL + searchText[j]; try { String result = HttpToolbox.get(url); @@ -88,6 +90,15 @@ public class DocumentSearchManager implements AlphaFineSearchProvider { } catch (Exception e) { FineLoggerFactory.getLogger().error("document search error: " + e.getMessage()); } + // 计算等待时间 防止1s内搜索多次 + long spentTime = (System.currentTimeMillis() - start); + long waitTime = AlphaFineConstants.DOCUMENT_SEARCH_GAP - spentTime; + if (waitTime > 0 && j != searchText.length - 1) { + try { + TimeUnit.MILLISECONDS.sleep(waitTime); + } catch (Exception ignored) { + } + } } lessModelList.clear(); moreModelList.clear(); diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/app/FormApp.java b/designer-realize/src/main/java/com/fr/design/mainframe/app/FormApp.java index 01b3172bd..0ced38559 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/app/FormApp.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/app/FormApp.java @@ -3,6 +3,9 @@ package com.fr.design.mainframe.app; import com.fr.base.Parameter; import com.fr.base.chart.exception.ChartNotFoundException; import com.fr.base.io.XMLEncryptUtils; +import com.fr.base.theme.FineColorManager; +import com.fr.base.theme.FineColorSynchronizer; +import com.fr.base.theme.TemplateThemeCompatible; import com.fr.design.DesignerEnvManager; import com.fr.design.fit.NewJForm; import com.fr.design.i18n.Toolkit; @@ -124,6 +127,6 @@ class FormApp extends AbstractAppProvider { FineLoggerFactory.getLogger().error("Failed to generate frm from " + file, exp); return null; } - return tpl; + return (Form) FineColorSynchronizer.flush(tpl, tpl.getTemplateTheme()); } } diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/cell/settingpane/CellOtherSetPane.java b/designer-realize/src/main/java/com/fr/design/mainframe/cell/settingpane/CellOtherSetPane.java index eeaf8f920..4cddfea73 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/cell/settingpane/CellOtherSetPane.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/cell/settingpane/CellOtherSetPane.java @@ -79,6 +79,7 @@ public class CellOtherSetPane extends AbstractCellAttrPane { //文本超出时隐藏 private UICheckBox textOverflowCheckBox; + private int curSelectedIndex; private UIComboBox showPartComboBox; private CardLayout showPartLayout; private JPanel showPartPane; @@ -325,7 +326,16 @@ public class CellOtherSetPane extends AbstractCellAttrPane { showPartLayout.show(showPartPane, "none"); showPartPane.setPreferredSize(new Dimension(0, 0)); } + // 记录目前自动调整哪个被选中 + for (int i = 0; i < adjustRadioButtons.length; i++) { + if (adjustRadioButtons[i].isSelected()) { + curSelectedIndex = i; + break; + } + } noAutoRadioButton.setSelected(true); + } else { + adjustRadioButtons[curSelectedIndex].setSelected(true); } } }); @@ -421,25 +431,37 @@ public class CellOtherSetPane extends AbstractCellAttrPane { @Override public void itemStateChanged(ItemEvent e) { - Style elementStyle = cellElement.getStyle(); - FRFont frFont = elementStyle.getFRFont(); - if (showContent.getSelectedIndex() == 3) { - fileNamePane.setPreferredSize(new Dimension(100, 20)); - fileNameLayout.show(fileNamePane, "content"); - frFont = frFont.applyForeground(Color.blue); - frFont = frFont.applyUnderline(Constants.LINE_THIN); - } else { - fileNameLayout.show(fileNamePane, "none"); - fileNamePane.setPreferredSize(new Dimension(0, 0)); - frFont = frFont.applyForeground(Color.black); - frFont = frFont.applyUnderline(Constants.LINE_NONE); + if (e.getStateChange() == ItemEvent.SELECTED) { + if (showContent.getSelectedIndex() == 3) { + fileNamePane.setPreferredSize(new Dimension(100, 20)); + fileNameLayout.show(fileNamePane, "content"); + } else { + fileNameLayout.show(fileNamePane, "none"); + fileNamePane.setPreferredSize(new Dimension(0, 0)); + } + handleCellShowStyleChange(e); } - cellElement.setStyle(elementStyle.deriveFRFont(frFont)); } }); return fileNamePane; } + private void handleCellShowStyleChange(ItemEvent itemEvent) { + CellGUIAttr cellGUIAttr = getCellGUIAttr(); + int selectedIndex = showContent.getSelectedIndex(); + boolean showAsDownload = cellGUIAttr.isShowAsDownload(); + Style elementStyle = cellElement.getStyle(); + FRFont frFont = elementStyle.getFRFont(); + if (!showAsDownload && selectedIndex == 3) { + frFont = frFont.applyForeground(Color.blue); + frFont = frFont.applyUnderline(Constants.LINE_THIN); + } else if (showAsDownload && selectedIndex != 3) { + frFont = frFont.applyForeground(Color.black); + frFont = frFont.applyUnderline(Constants.LINE_NONE); + } + cellElement.setStyle(elementStyle.deriveFRFont(frFont)); + } + private void initAllNames() { defaultAutoRadioButton.setGlobalName(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_Follow_Paper_Settings")); @@ -475,15 +497,20 @@ public class CellOtherSetPane extends AbstractCellAttrPane { return com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_Other"); } + private CellGUIAttr getCellGUIAttr() { + CellGUIAttr cellGUIAttr = cellElement.getCellGUIAttr(); + if (cellGUIAttr == null) { + cellGUIAttr = CellGUIAttr.DEFAULT_CELLGUIATTR; + } + return cellGUIAttr; + } + @Override protected void populateBean() { this.currentPageFixedRowDataTipLabel.setText(" (" + Toolkit.i18nText("Fine-Design_Report_CellWrite_No_Page_Fixed_Row_Cell") + ")"); this.pageFixedRowDataCell = null; checkPageFixedRow(); - CellGUIAttr cellGUIAttr = cellElement.getCellGUIAttr(); - if (cellGUIAttr == null) { - cellGUIAttr = CellGUIAttr.DEFAULT_CELLGUIATTR; - } + CellGUIAttr cellGUIAttr = getCellGUIAttr(); // 支持 跟随页面设置 选项 = 不在编辑表单中的报表块 && 不在大屏模板cpt组件中 boolean supportFollowTplDefine = !EastRegionContainerPane.getInstance().getCurrentMode().equals(EastRegionContainerPane.PropertyMode.FORM_REPORT) 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/design/share/ui/generate/ShareMainPane.java b/designer-realize/src/main/java/com/fr/design/share/ui/generate/ShareMainPane.java index 9ac98286e..70ec2b6f9 100644 --- a/designer-realize/src/main/java/com/fr/design/share/ui/generate/ShareMainPane.java +++ b/designer-realize/src/main/java/com/fr/design/share/ui/generate/ShareMainPane.java @@ -35,6 +35,7 @@ import com.fr.design.mainframe.share.ui.base.ui.PlaceHolderUI; import com.fr.design.mainframe.share.util.ShareUIUtils; import com.fr.design.share.effect.EffectItemGroup; import com.fr.design.share.utils.ShareDialogUtils; +import com.fr.file.FileCommonUtils; import com.fr.form.share.DefaultSharableWidget; import com.fr.form.share.bean.StyleThemeBean; import com.fr.form.share.constants.ShareComponentConstants; @@ -659,6 +660,15 @@ public class ShareMainPane extends JPanel { Toolkit.i18nText("Fine-Design_Basic_Error"), ERROR_MESSAGE, UIManager.getIcon("OptionPane.errorIcon")); return false; } + if (!FileCommonUtils.isFileNameValid(name)) { + FineJOptionPane.showMessageDialog( + shareDialog, + Toolkit.i18nText("Fine-Design_Share_Generate_Failure_Illegal_Component_Name_Tip"), + Toolkit.i18nText("Fine-Design_Basic_Error"), + ERROR_MESSAGE, + UIManager.getIcon("OptionPane.errorIcon")); + return false; + } if (upload && StringUtils.isEmpty(content.getText())) { FineJOptionPane.showMessageDialog(shareDialog, Toolkit.i18nText("Fine-Design_Share_Lack_Content"), Toolkit.i18nText("Fine-Design_Basic_Error"), ERROR_MESSAGE, UIManager.getIcon("OptionPane.errorIcon")); diff --git a/designer-realize/src/main/java/com/fr/design/sort/common/SortColumnRowPane.java b/designer-realize/src/main/java/com/fr/design/sort/common/SortColumnRowPane.java index e9ebbd882..19b8ad50a 100644 --- a/designer-realize/src/main/java/com/fr/design/sort/common/SortColumnRowPane.java +++ b/designer-realize/src/main/java/com/fr/design/sort/common/SortColumnRowPane.java @@ -3,13 +3,14 @@ package com.fr.design.sort.common; import com.fr.base.Style; import com.fr.base.background.ColorBackground; import com.fr.base.svg.IconUtils; -import com.fr.design.designer.TargetComponent; import com.fr.design.event.UIObserver; import com.fr.design.event.UIObserverListener; import com.fr.design.file.HistoryTemplateListCache; import com.fr.design.gui.ibutton.UIButton; import com.fr.design.gui.ilable.UILabel; import com.fr.design.mainframe.ElementCasePane; +import com.fr.design.mainframe.JTemplate; +import com.fr.design.mainframe.JTemplateActionListener; import com.fr.design.selection.SelectionEvent; import com.fr.design.selection.SelectionListener; import com.fr.design.sort.header.HeaderAreaPane; @@ -18,10 +19,6 @@ import com.fr.grid.selection.Selection; import com.fr.log.FineLoggerFactory; import com.fr.report.cell.DefaultTemplateCellElement; import com.fr.report.cell.TemplateCellElement; -import com.fr.report.cell.cellattr.CellExpandAttr; -import com.fr.report.cell.cellattr.core.group.DSColumn; -import com.fr.report.core.sort.common.CellSortable; -import com.fr.report.core.sort.header.SortHeader; import com.fr.report.elementcase.TemplateElementCase; import com.fr.stable.ColumnRow; import com.fr.stable.EssentialUtils; @@ -157,6 +154,7 @@ public class SortColumnRowPane extends JPanel implements UIObserver { class SelectActionListener extends MouseAdapter { SortColumnRowPane columnRowPane; ColumnRow oldColumnRow; + JTemplateActionListener jTemplateActionListener; Map disableHeaderCellsStyleMap = new HashMap<>(); java.util.List tempHeaderCells = new ArrayList<>(); @@ -178,13 +176,44 @@ public class SortColumnRowPane extends JPanel implements UIObserver { @Override public void selectionChanged(SelectionEvent e) { completeSelectHeader(elementCasePane); + removeJTemplateActionListener(); } }; elementCasePane.addSelectionChangeListener(gridSelectionChangeListener); + this.addJTemplateActionListener(); isAlreadyAddListener = true; } } + + private void addJTemplateActionListener() { + JTemplate jTemplate = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + if (jTemplate != null) { + removeJTemplateActionListener(); + jTemplateActionListener = new JTemplateActionListener() { + @Override + public void templateSaveBefore(JTemplate jt) { + ElementCasePane elementCasePane = SortUtils.getCurrentElementCase(); + if (elementCasePane != null) { + recoverSelectHeader(elementCasePane); + } + removeJTemplateActionListener(); + } + }; + jTemplate.addJTemplateActionListener(jTemplateActionListener); + } + } + + private void removeJTemplateActionListener() { + JTemplate jTemplate = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + if (jTemplate != null) { + if (jTemplateActionListener != null) { + jTemplate.removeJTemplateActionListener(jTemplateActionListener); + } + } + } + + private void prepareSelectHeader(ElementCasePane elementCasePane) { ashDisableHeaderCellsStyle(elementCasePane.getEditingElementCase()); oldSelection = (CellSelection) elementCasePane.getSelection(); 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..ba0c5e03d 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_Locked_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); } } } diff --git a/designer-realize/src/main/java/com/fr/start/module/DesignerStartup.java b/designer-realize/src/main/java/com/fr/start/module/DesignerStartup.java index da8b559e8..4149753f7 100644 --- a/designer-realize/src/main/java/com/fr/start/module/DesignerStartup.java +++ b/designer-realize/src/main/java/com/fr/start/module/DesignerStartup.java @@ -124,6 +124,10 @@ public class DesignerStartup extends Activator { listenEvent(LazyStartupEvent.INSTANCE, new Listener(Integer.MIN_VALUE) { @Override public void on(Event event, Null param) { + // 有可能被插件之类的 强制启动了 判断下 + if (FineEmbedServer.isRunning()) { + return; + } startEmbeddedServer(); } }); diff --git a/designer-realize/src/main/java/com/fr/start/module/PreStartActivator.java b/designer-realize/src/main/java/com/fr/start/module/PreStartActivator.java index 65cdc1c30..fa795fb37 100644 --- a/designer-realize/src/main/java/com/fr/start/module/PreStartActivator.java +++ b/designer-realize/src/main/java/com/fr/start/module/PreStartActivator.java @@ -2,6 +2,7 @@ package com.fr.start.module; import com.fr.design.DesignerEnvManager; import com.fr.design.RestartHelper; +import com.fr.design.file.TemplateResourceManager; import com.fr.design.utils.DesignUtils; import com.fr.file.TmpFileUtils; import com.fr.general.CloudCenter; @@ -28,6 +29,9 @@ public class PreStartActivator extends Activator { // 创建监听服务 DesignUtils.createListeningServer(DesignUtils.getPort(), startFileSuffix()); + // 在插件引擎模块起来前 初始化下插件接口监听 + TemplateResourceManager.getResource(); + initLanguage(); } diff --git a/designer-realize/src/main/java/com/fr/start/module/optimized/TenantDBAdapter4Designer.java b/designer-realize/src/main/java/com/fr/start/module/optimized/TenantDBAdapter4Designer.java index d45913e1a..ad2485d69 100644 --- a/designer-realize/src/main/java/com/fr/start/module/optimized/TenantDBAdapter4Designer.java +++ b/designer-realize/src/main/java/com/fr/start/module/optimized/TenantDBAdapter4Designer.java @@ -9,6 +9,7 @@ import com.fr.event.Null; import com.fr.exit.ConfigToPropMigrator; import com.fr.stable.db.tenant.TenantDBAdapter; import com.fr.start.event.LazyStartupEvent; +import com.fr.start.server.EmbedServerEvent; /** * @author hades @@ -24,7 +25,15 @@ public class TenantDBAdapter4Designer extends TenantDBAdapter { @Override public void on(Event event, Null param) { TenantDBAdapter4Designer.super.start(); - afterStart(); + } + }); + + listenEvent(EmbedServerEvent.BeforeStart, new Listener() { + @Override + public void on(Event event, Null param) { + if (DaoSelectorFactory.getDaoSelector().useCacheDao()) { + beforeEmbedServerStart(); + } } }); @@ -33,7 +42,7 @@ public class TenantDBAdapter4Designer extends TenantDBAdapter { } } - private void afterStart() { + private void beforeEmbedServerStart() { DesignerEnvManager.getEnvManager().setPropertiesUsable(false); DaoSwitcher.executeSwitch(); ConfigToPropMigrator.getInstance().deletePropertiesCache(); diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/light_yellow_bulb.svg b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/light_yellow_bulb.svg new file mode 100644 index 000000000..16608753f --- /dev/null +++ b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/light_yellow_bulb.svg @@ -0,0 +1,4 @@ + + + + diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/yellow_bulb.svg b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/yellow_bulb.svg new file mode 100644 index 000000000..cabda1d9d --- /dev/null +++ b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/yellow_bulb.svg @@ -0,0 +1,4 @@ + + + +