@ -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<T extends Exception> { |
||||
|
||||
void run() throws T; |
||||
|
||||
static <T extends Exception> Runnable toRunnable(ThrowableRunnable<T> runnable) { |
||||
|
||||
return () -> { |
||||
try { |
||||
runnable.run(); |
||||
} catch (Exception e) { |
||||
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||
} |
||||
}; |
||||
} |
||||
} |
@ -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); |
||||
} |
@ -0,0 +1,348 @@
|
||||
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.Supplier; |
||||
import java.util.stream.Collectors; |
||||
|
||||
/** |
||||
* 右下角的提醒 <a href="https://kms.fineres.com/pages/viewpage.action?pageId=388333688">异常提醒</a> |
||||
* |
||||
* created by Harrison on 2022/05/24 |
||||
**/ |
||||
public class NotificationDialog extends JDialog { |
||||
|
||||
private Dimension contentSize = new Dimension(300, 100); |
||||
private Dimension buttonDimension = new Dimension(68, 20); |
||||
|
||||
private NotificationDialogProperties properties; |
||||
|
||||
/* 数据 model */ |
||||
|
||||
private List<NotificationModel> notificationModels; |
||||
private PageControlModel pageControlModel; |
||||
|
||||
private JPanel body; |
||||
private JPanel headerPanel; |
||||
private JPanel contentPanel; |
||||
private JPanel tailPanel; |
||||
|
||||
public NotificationDialog(NotificationDialogProperties properties, List<NotificationModel> 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, 20)); |
||||
|
||||
NotificationMessage[] messages = model.getMessages(); |
||||
List<? extends JComponent> 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())); |
||||
}) |
||||
.peek((component) -> { |
||||
Dimension preferredSize = component.getPreferredSize(); |
||||
double componentWidth = preferredSize.getWidth(); |
||||
double componentHeight = preferredSize.getHeight(); |
||||
double widthFactor = Math.ceil(componentWidth / 300); |
||||
double heightFactor = Math.ceil(componentHeight / 15); |
||||
int realHeight = (int) (heightFactor + widthFactor - 1) * 15; |
||||
component.setPreferredSize(new Dimension(300, 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(contentSize); |
||||
|
||||
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(buttonDimension); |
||||
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(buttonDimension); |
||||
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<JPanel> 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
|
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
||||
} |
@ -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<NotificationMessage> 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; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,16 @@
|
||||
package com.fr.design.components.notification; |
||||
|
||||
/** |
||||
* 提醒类型 |
||||
* 决定图标种类 |
||||
* |
||||
* created by Harrison on 2022/05/27 |
||||
**/ |
||||
public enum NotificationType { |
||||
|
||||
INFO, |
||||
|
||||
ERROR, |
||||
|
||||
WARNING |
||||
} |
@ -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; |
||||
} |
||||
} |
@ -0,0 +1,150 @@
|
||||
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<PageControlModel, PageControlModel> 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; |
||||
} |
||||
|
||||
} |
@ -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); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,24 @@
|
||||
package com.fr.design.constants; |
||||
|
||||
import java.awt.Color; |
||||
|
||||
/** |
||||
* 见 <a href="https://www.figma.com/file/G2f40rv7cY8zpDDYbt6pY2/%E8%AE%BE%E8%AE%A1%E5%99%A811.0%E8%A7%84%E8%8C%83%E6%95%B4%E7%90%86">设计器规范</a> |
||||
* 将相关的逻辑抽象过来 |
||||
* 如果后面更改的话, 可以统一修改 |
||||
* 如果换版本,可以换成 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); |
||||
} |
||||
} |
||||
} |
@ -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)); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -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"}); |
||||
} |
||||
} |
@ -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 页面的调试 |
||||
* <p> |
||||
* 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<Frame> 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<Frame>() { |
||||
@Override |
||||
public void accept(Frame frame) { |
||||
} |
||||
}); |
||||
} |
||||
} |
@ -0,0 +1,55 @@
|
||||
package com.fr.design.utils; |
||||
|
||||
import com.fr.design.gui.ilable.UILabel; |
||||
import com.fr.stable.StringUtils; |
||||
|
||||
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 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><body style=\"" + style + "\">" + html + "</body></html>"; |
||||
} |
||||
|
||||
public static String generateLinkTag(String link, String text) { |
||||
|
||||
return "<a href=\"" + link + "\">" + text + "</a>"; |
||||
} |
||||
|
||||
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()); |
||||
} |
||||
} |
@ -0,0 +1,182 @@
|
||||
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.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.start.server.EmbedServerEvent; |
||||
import com.fr.update.delay.DelayHelper; |
||||
import com.fr.workspace.Workspace; |
||||
import com.fr.workspace.WorkspaceEvent; |
||||
|
||||
import java.util.List; |
||||
import java.util.Objects; |
||||
import java.util.concurrent.TimeUnit; |
||||
import java.util.concurrent.atomic.AtomicReference; |
||||
import java.util.stream.Collectors; |
||||
import java.util.stream.Stream; |
||||
|
||||
/** |
||||
* 环境检测中心 |
||||
* 如果环境检测 -> |
||||
* |
||||
* created by Harrison on 2022/05/27 |
||||
**/ |
||||
public class EnvDetectorCenter { |
||||
|
||||
public static EnvDetectorCenter getInstance() { |
||||
return EnvDetectorCenterHolder.INSTANCE; |
||||
} |
||||
|
||||
private static class EnvDetectorCenterHolder { |
||||
private static final EnvDetectorCenter INSTANCE = new EnvDetectorCenter(); |
||||
} |
||||
|
||||
private final AtomicReference<DetectorProcess> PROCESS = new AtomicReference<>(); |
||||
|
||||
/** |
||||
* 初始化 |
||||
*/ |
||||
public void init() { |
||||
|
||||
// 默认是启动
|
||||
PROCESS.set(DetectorProcess.DESIGN_LAUNCH); |
||||
start(); |
||||
|
||||
// 添加启动完成监听
|
||||
EventDispatcher.listen(DesignerLaunchStatus.STARTUP_COMPLETE, new Listener<Null>() { |
||||
@Override |
||||
public void on(Event event, Null param) { |
||||
if (isSameProcess(DetectorProcess.DESIGN_LAUNCH)) { |
||||
stop(); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
// 切换完成后的监听
|
||||
EventDispatcher.listen(WorkspaceEvent.AfterSwitch, new Listener<Workspace>() { |
||||
@Override |
||||
public void on(Event event, Workspace param) { |
||||
if (isSameProcess(DetectorProcess.DESIGN_LAUNCH)) { |
||||
stop(); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
// 打开内置服务器
|
||||
EventDispatcher.listen(EmbedServerEvent.BeforeStart, new Listener<Null>() { |
||||
@Override |
||||
public void on(Event event, Null param) { |
||||
PROCESS.set(DetectorProcess.SERVER_LAUNCH); |
||||
start(); |
||||
} |
||||
}); |
||||
EventDispatcher.listen(EmbedServerEvent.AfterStart, new Listener<Null>() { |
||||
@Override |
||||
public void on(Event event, Null param) { |
||||
if (isSameProcess(DetectorProcess.SERVER_LAUNCH)) { |
||||
stop(); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
} |
||||
|
||||
/** |
||||
* 当前的流程符合预期 |
||||
* 什么情况下不符合? |
||||
* design.start -> server.start -> design.end -> server.end |
||||
* 这个时候 design.end 就不会触发,等待服务器启动后才触发 |
||||
* |
||||
* @param process 检测流程 |
||||
* @return 是 / 否 |
||||
*/ |
||||
private boolean isSameProcess(DetectorProcess process) { |
||||
|
||||
return PROCESS.compareAndSet(process, null); |
||||
} |
||||
|
||||
/** |
||||
* 启动 |
||||
*/ |
||||
public void start() { |
||||
|
||||
DetectorBridge.getInstance().start(); |
||||
} |
||||
|
||||
/** |
||||
* 关闭 |
||||
*/ |
||||
public void stop() { |
||||
|
||||
// 一分钟后执行
|
||||
DelayHelper.delayCall(EnvDetectorCenter.class.getName(), () -> { |
||||
|
||||
Stream<DetectorResult> resultStream = DetectorBridge.getInstance().detect(); |
||||
|
||||
// 结束
|
||||
DetectorBridge.getInstance().stop(); |
||||
|
||||
// 展示效果
|
||||
NotificationDialogProperties properties = new NotificationDialogProperties(DesignerContext.getDesignerFrame(), Toolkit.i18nText("Fine-Design_Basic_Detect_Notification_Title")); |
||||
List<NotificationModel> notificationModels = resultStream |
||||
.filter(Objects::nonNull) |
||||
.filter((e) -> e.getStatus() == DetectorStatus.EXCEPTION) |
||||
.map(DetectorUtil::convert2Notification) |
||||
.collect(Collectors.toList()); |
||||
if (Collections.isEmpty(notificationModels)) { |
||||
return; |
||||
} |
||||
|
||||
UIUtil.invokeLaterIfNeeded(() -> { |
||||
NotificationDialog dialog = new NotificationDialog(properties, notificationModels); |
||||
dialog.open(); |
||||
}); |
||||
}, 30, TimeUnit.SECONDS); |
||||
|
||||
} |
||||
|
||||
/** |
||||
* 使用预期外的错误进行展示 |
||||
* |
||||
* @param throwable 异常 |
||||
* @return |
||||
*/ |
||||
public List<DetectorResult> terminate(Throwable throwable) { |
||||
|
||||
Stream<DetectorResult> resultStream = DetectorBridge.getInstance().detect(throwable); |
||||
return resultStream |
||||
.filter((e) -> e.getStatus() == DetectorStatus.EXCEPTION) |
||||
.collect(Collectors.toList()); |
||||
} |
||||
|
||||
private enum DetectorProcess { |
||||
|
||||
/** |
||||
* 设计器启动 |
||||
*/ |
||||
DESIGN_LAUNCH, |
||||
|
||||
/** |
||||
* 服务器启动 |
||||
*/ |
||||
SERVER_LAUNCH, |
||||
|
||||
/** |
||||
* 环境切换 |
||||
*/ |
||||
ENV_SWITCH |
||||
} |
||||
} |
@ -0,0 +1,26 @@
|
||||
package com.fr.env.detect; |
||||
|
||||
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() { |
||||
|
||||
} |
||||
} |
@ -0,0 +1,11 @@
|
||||
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"; |
||||
} |
@ -0,0 +1,127 @@
|
||||
package com.fr.env.detect.base; |
||||
|
||||
import com.fr.base.function.ThrowableRunnable; |
||||
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.gui.ilable.UILabel; |
||||
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.stable.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.Optional; |
||||
import java.util.function.Function; |
||||
|
||||
/** |
||||
* 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<NotificationMessage> messages = new ArrayList<>(); |
||||
|
||||
Function<Message, Optional<NotificationMessage>> 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) { |
||||
|
||||
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 new UILabel(LinkStrUtils.generateHtmlTag(message.get())); |
||||
} |
||||
} |
@ -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(); |
||||
} |
||||
|
||||
} |
@ -1,21 +0,0 @@
|
||||
package com.fr.env.detect.base; |
||||
|
||||
/** |
||||
* created by Harrison on 2022/05/13 |
||||
**/ |
||||
public class ExceptionDetectorConfig { |
||||
|
||||
|
||||
public static ExceptionDetectorConfig getInstance() { |
||||
return ExceptionDetectorConfigHolder.INSTANCE; |
||||
} |
||||
|
||||
private static class ExceptionDetectorConfigHolder { |
||||
private static final ExceptionDetectorConfig INSTANCE = new ExceptionDetectorConfig(); |
||||
} |
||||
|
||||
public boolean isOpen() { |
||||
|
||||
return true; |
||||
} |
||||
} |
@ -1,22 +1,17 @@
|
||||
package com.fr.env.detect.bean; |
||||
|
||||
/** |
||||
* created by Harrison on 2022/05/25 |
||||
* created by Harrison on 2022/05/27 |
||||
**/ |
||||
public enum DetectorStatus { |
||||
|
||||
/** |
||||
* 成功 |
||||
* 正常 |
||||
*/ |
||||
SUCCESS, |
||||
|
||||
/** |
||||
* 失败 |
||||
*/ |
||||
FAILED, |
||||
NORMAL, |
||||
|
||||
/** |
||||
* 异常 |
||||
*/ |
||||
UNKNOWN |
||||
EXCEPTION, |
||||
} |
||||
|
@ -1,24 +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 errorCode; |
||||
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); |
||||
} |
||||
|
||||
private final String locale; |
||||
public void log() { |
||||
|
||||
public ExceptionLog(String errorCode, String locale) { |
||||
this.errorCode = errorCode; |
||||
this.locale = locale; |
||||
FineLoggerFactory.getLogger().error(template, args); |
||||
} |
||||
|
||||
public String getErrorCode() { |
||||
return errorCode; |
||||
public String getTemplate() { |
||||
return template; |
||||
} |
||||
|
||||
public String getLocale() { |
||||
return locale; |
||||
public Object[] getArgs() { |
||||
return args; |
||||
} |
||||
} |
||||
|
@ -1,26 +0,0 @@
|
||||
package com.fr.env.detect.exception; |
||||
|
||||
/** |
||||
* created by Harrison on 2022/05/25 |
||||
**/ |
||||
public class DetectorException extends RuntimeException { |
||||
|
||||
public DetectorException() { |
||||
} |
||||
|
||||
public DetectorException(String message) { |
||||
super(message); |
||||
} |
||||
|
||||
public DetectorException(String message, Throwable cause) { |
||||
super(message, cause); |
||||
} |
||||
|
||||
public DetectorException(Throwable cause) { |
||||
super(cause); |
||||
} |
||||
|
||||
public DetectorException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { |
||||
super(message, cause, enableSuppression, writableStackTrace); |
||||
} |
||||
} |
@ -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()); |
||||
} |
||||
|
||||
} |
@ -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()); |
||||
} |
||||
} |
@ -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()); |
||||
} |
||||
} |
@ -1,43 +1,150 @@
|
||||
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.BuildInfoOperator; |
||||
import com.fr.stable.StringUtils; |
||||
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.stream.Collectors; |
||||
|
||||
/** |
||||
* created by Harrison on 2022/05/24 |
||||
**/ |
||||
public class JarLackDetector extends AbstractExceptionDetector { |
||||
|
||||
public static final String SEPARATOR = "、"; |
||||
public static final String BR_TAG = "<br/>"; |
||||
public static final String WEB_LIB_PATH = "%FR_HOME%\\webapps\\webroot\\WEB-INF\\lib:"; |
||||
public static final String FR_HOME_LIB = "%FR_HOME%\\lib:"; |
||||
|
||||
public JarLackDetector() { |
||||
|
||||
super(DetectorType.LACK_OF_JAR); |
||||
} |
||||
|
||||
@Override |
||||
public DetectorResult detect() { |
||||
|
||||
DetectorResult.DetectorResultBuilder builder = DetectorResult.builder(); |
||||
// 不支持远程
|
||||
if (!WorkContext.getCurrent().isLocal()) { |
||||
return DetectorResult.normal(type()); |
||||
} |
||||
|
||||
// 检测有哪些 JAR 包, 当前是否缺少对应的 JAR 包
|
||||
BuildInfoOperator buildInfoOperator = WorkContext.getCurrent().get(BuildInfoOperator.class); |
||||
BuildInfoOperator buildInfoOperator = new BuildInfoOperatorImpl(); |
||||
List<BuildInfo> buildInfos = buildInfoOperator.getBuildInfos(); |
||||
for (BuildInfo buildInfo : buildInfos) { |
||||
String jar = buildInfo.getJar(); |
||||
String buildContent = buildInfo.getGroupBuild(); |
||||
boolean isLack = StringUtils.isEmpty(buildContent); |
||||
if (isLack) { |
||||
// 处理信息
|
||||
List<BuildInfo> 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<BuildInfo> lackInfos) { |
||||
|
||||
List<String> jarInfos = lackInfos.stream() |
||||
.map(BuildInfo::getJar) |
||||
.collect(Collectors.toList()); |
||||
String message = StringUtils.join(jarInfos, ","); |
||||
return ExceptionLog.create(Toolkit.i18nText(type().getLogLocale()) + message); |
||||
} |
||||
|
||||
private boolean isLackInfo(BuildInfo e) { |
||||
return StringUtils.isNotEmpty(e.getGroupBuild()); |
||||
} |
||||
|
||||
private Message tipsMessage(List<BuildInfo> infos) { |
||||
|
||||
String webLibPath = WEB_LIB_PATH; |
||||
String homeLibPath = FR_HOME_LIB; |
||||
|
||||
Map<String, List<String>> libMap = groupByPath(infos, homeLibPath, webLibPath); |
||||
|
||||
StringBuilder sb = new StringBuilder(); |
||||
|
||||
List<String> homeLibs = libMap.get(homeLibPath); |
||||
if (!Collections.isEmpty(homeLibs)) { |
||||
sb.append(homeLibPath); |
||||
sb.append(StringUtils.join(homeLibs, SEPARATOR)); |
||||
} |
||||
|
||||
List<String> 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 builder.build(); |
||||
String mainContent = sb.toString(); |
||||
|
||||
DetectorType type = this.type(); |
||||
String header = Toolkit.i18nText(type.getTipsLocale()); |
||||
return new Message.Simple(header + mainContent); |
||||
} |
||||
|
||||
@NotNull |
||||
private Map<String, List<String>> groupByPath(List<BuildInfo> infos, String homeLibPath, String webLibPath) { |
||||
|
||||
Map<String, List<String>> 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); |
||||
} |
||||
|
||||
} |
||||
|
@ -0,0 +1,191 @@
|
||||
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.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.resource.ResourceLoader; |
||||
import com.fr.third.org.apache.commons.lang3.StringUtils; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import javax.el.MethodNotFoundException; |
||||
import java.io.IOException; |
||||
import java.net.URL; |
||||
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 final Map<Class<?>, 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<String> classNames = Collections.emptyList(); |
||||
Throwable sign = throwable; |
||||
while (sign != null) { |
||||
if (throwableMap.containsKey(sign.getClass())) { |
||||
classNames = throwableMap.get(sign.getClass()) |
||||
.converter(throwable); |
||||
} |
||||
sign = sign.getCause(); |
||||
} |
||||
|
||||
Set<String> allPath = new HashSet<>(); |
||||
for (String className : classNames) { |
||||
String classFile = convertClass2Path(className); |
||||
try { |
||||
Enumeration<URL> urls = ResourceLoader.getResources(classFile, this.getClass()); |
||||
List<URL> urlList = new ArrayList<>(); |
||||
while (urls.hasMoreElements()) { |
||||
URL url = urls.nextElement(); |
||||
urlList.add(url); |
||||
} |
||||
for (URL url : urlList) { |
||||
String file = url.getFile(); |
||||
Matcher matcher = JAR_NAME_PATTERN.matcher(url.getFile()); |
||||
if (matcher.find()) { |
||||
String jar = matcher.group(); |
||||
allPath.add(jar); |
||||
} else { |
||||
boolean containsClasses = file.contains(CLASSES); |
||||
if (containsClasses) { |
||||
allPath.add(CLASSES); |
||||
} |
||||
} |
||||
} |
||||
} catch (IOException ignore) { |
||||
} |
||||
} |
||||
|
||||
String msg = StringUtils.join(allPath, SEPARATOR); |
||||
|
||||
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<String> 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<String> converter(Throwable throwable) { |
||||
|
||||
String message = throwable.getMessage(); |
||||
Matcher matcher = CLASS_PATTERN.matcher(message); |
||||
List<String> 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<String> converter(Throwable throwable) { |
||||
|
||||
String message = throwable.getMessage(); |
||||
Matcher matcher = METHOD_PATTERN.matcher(message); |
||||
List<String> names = new ArrayList<>(); |
||||
while (matcher.find()) { |
||||
String className = matcher.group(); |
||||
names.add(className); |
||||
} |
||||
return names; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,53 +0,0 @@
|
||||
package com.fr.env.detect.impl.converter; |
||||
|
||||
import com.fr.env.detect.bean.DetectorResult; |
||||
import com.fr.env.detect.thowable.ThrowableConverter; |
||||
|
||||
import javax.el.MethodNotFoundException; |
||||
import java.util.HashSet; |
||||
import java.util.Set; |
||||
|
||||
/** |
||||
* 类抛出异常的转换 |
||||
* created by Harrison on 2022/05/24 |
||||
**/ |
||||
public class ClassThrowableConvertor implements ThrowableConverter { |
||||
|
||||
private Set<Class<?>> throwableClassSet = new HashSet<>(); |
||||
|
||||
private Set<Class<?>> throwableMethodSet = new HashSet<>(); |
||||
|
||||
public ClassThrowableConvertor() { |
||||
|
||||
// 类异常
|
||||
this.throwableClassSet.add(ClassNotFoundException.class); |
||||
this.throwableClassSet.add(NoClassDefFoundError.class); |
||||
this.throwableClassSet.add(ClassCastException.class); |
||||
this.throwableClassSet.add(IncompatibleClassChangeError.class); |
||||
|
||||
// 方法异常
|
||||
this.throwableMethodSet.add(MethodNotFoundException.class); |
||||
this.throwableMethodSet.add(NoSuchMethodException.class); |
||||
this.throwableMethodSet.add(NoSuchMethodError.class); |
||||
} |
||||
|
||||
@Override |
||||
public boolean accept(Throwable throwable) { |
||||
|
||||
Throwable sign = throwable; |
||||
while (sign != null) { |
||||
if (throwableClassSet.contains(sign.getClass()) || throwableMethodSet.contains(sign.getClass())) { |
||||
return true; |
||||
} |
||||
sign = sign.getCause(); |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
@Override |
||||
public DetectorResult convert(Throwable throwable) { |
||||
|
||||
// todo
|
||||
return null; |
||||
} |
||||
} |
@ -0,0 +1,135 @@
|
||||
package com.fr.env.detect.ui; |
||||
|
||||
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.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 com.fr.general.FRFont; |
||||
|
||||
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 { |
||||
|
||||
private UIButton okButton; |
||||
private UIButton restartButton; |
||||
|
||||
public DetectorErrorDialog(Frame parent, List<DetectorResult> results) { |
||||
|
||||
super(parent, true); |
||||
|
||||
JPanel northPane = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||
JPanel messagePane = FRGUIPaneFactory.createVerticalFlowLayout_S_Pane(true); |
||||
|
||||
UILabel boldHeader = new UILabel(Toolkit.i18nText("Fine-Design_Error_Start_Apology_Message")); |
||||
Font font = FRFont.getInstance(boldHeader.getFont().getFontName(), Font.BOLD, 20); |
||||
boldHeader.setFont(font); |
||||
messagePane.add(boldHeader); |
||||
|
||||
UILabel description = new UILabel(Toolkit.i18nText("Fine-Design_Send_Report_To_Us")); |
||||
messagePane.add(description); |
||||
northPane.add(messagePane); |
||||
|
||||
JPanel centerPane = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||
|
||||
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)); |
||||
|
||||
for (DetectorResult result : results) { |
||||
ExceptionTips tips = result.getTips(); |
||||
if (tips != null) { |
||||
Message tipsMsg = tips.getMessage(); |
||||
detailPanel.add(DetectorUtil.convert2TextComponent(tipsMsg), BorderLayout.NORTH); |
||||
} |
||||
ExceptionSolution solution = result.getSolution(); |
||||
if (solution != null) { |
||||
Message solutionMsg = solution.getMessage(); |
||||
detailPanel.add(DetectorUtil.convert2TextComponent(solutionMsg), 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(600, 500)); |
||||
GUICoreUtils.centerWindow(this); |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public void actionPerformed(ActionEvent e) { |
||||
dispose(); |
||||
} |
||||
|
||||
protected void okEvent() { |
||||
|
||||
dispose(); |
||||
DesignerExiter.getInstance().execute(); |
||||
} |
||||
|
||||
protected void restartEvent() { |
||||
|
||||
dispose(); |
||||
RestartHelper.restart(); |
||||
} |
||||
|
||||
} |
@ -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); |
||||
} |
||||
} |
@ -0,0 +1,494 @@
|
||||
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 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(); |
||||
public static final int TIMEOUT = 1000; |
||||
|
||||
private JPanel body; |
||||
|
||||
private final JPanel headerPanel; |
||||
|
||||
private final TablePanel tablePanel; |
||||
|
||||
private final JPanel tailPanel; |
||||
|
||||
/* 数据 model */ |
||||
|
||||
private EnvDetectorModel model; |
||||
|
||||
/* 流程 model */ |
||||
|
||||
/** |
||||
* 默认是第一个要检测 |
||||
*/ |
||||
private int currentDetectIndex = 0; |
||||
|
||||
private EnvDetectorButtonStatus buttonStatus = EnvDetectorButtonStatus.START; |
||||
|
||||
private SwingWorker<Void, Void> 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)); |
||||
UIButton 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(detectButton); |
||||
} 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(UIButton detectButton) { |
||||
|
||||
// 执行前
|
||||
buttonStatus = buttonStatus.next(); |
||||
UIUtil.invokeLaterIfNeeded(() -> detectButton.setText(buttonStatus.getDesc())); |
||||
detectWorker = new SwingWorker<Void, Void>() { |
||||
|
||||
@Override |
||||
protected Void doInBackground() throws Exception { |
||||
List<EnvDetectorItem> items = model.getItems(); |
||||
// 执行刷新
|
||||
for (int i = currentDetectIndex; i < items.size(); i++) { |
||||
|
||||
// 看一下是否关闭了, 有可能已经关闭了。
|
||||
if (buttonStatus.isNotExecuting()) { |
||||
return null; |
||||
} |
||||
|
||||
// 刷新一下面板-开始执行啦
|
||||
UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refresh); |
||||
|
||||
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::refresh); |
||||
currentDetectIndex++; |
||||
} |
||||
|
||||
} |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
protected void done() { |
||||
|
||||
if (buttonStatus.isExecuting()) { |
||||
// 执行结束
|
||||
buttonStatus = EnvDetectorButtonStatus.A_NEW; |
||||
UIUtil.invokeLaterIfNeeded(() -> detectButton.setText(buttonStatus.getDesc())); |
||||
} |
||||
} |
||||
}; |
||||
// 开始执行
|
||||
detectWorker.execute(); |
||||
} |
||||
|
||||
private void stopDetecting(UIButton detectButton) { |
||||
|
||||
buttonStatus = buttonStatus.next(); |
||||
|
||||
// 先停止
|
||||
detectWorker.cancel(false); |
||||
// 更改-UI
|
||||
// 执行中
|
||||
UIUtil.invokeLaterIfNeeded(() -> { |
||||
// 刷新按钮
|
||||
detectButton.setText(buttonStatus.getDesc()); |
||||
// 刷新面板
|
||||
refresh(); |
||||
}); |
||||
} |
||||
|
||||
/* 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<DetectorType.Kind, List<EnvDetectorItem>> itemMap = model.getItemMap(); |
||||
|
||||
// 行号, 这边更新是通过 行/列 。 不是索引
|
||||
int row = 1; |
||||
for (Map.Entry<DetectorType.Kind, List<EnvDetectorItem>> entry : itemMap.entrySet()) { |
||||
|
||||
DetectorType.Kind kind = entry.getKey(); |
||||
List<EnvDetectorItem> 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); |
||||
|
||||
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(new Color(65, 155, 249)); |
||||
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<DetectorResult> results = model.getResults(); |
||||
List<NotificationModel> 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 refresh() { |
||||
|
||||
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(); |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
@ -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<DetectorType.Kind, List<EnvDetectorItem>> 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<EnvDetectorItem> items = itemMap.get(type.getKind()); |
||||
items.stream() |
||||
.filter((e) -> e.getType() == type) |
||||
.findFirst() |
||||
.ifPresent((e) -> { |
||||
e.setResult(result); |
||||
}); |
||||
} |
||||
|
||||
public Stream<DetectorResult> getResults() { |
||||
|
||||
return getItems().stream() |
||||
.map(EnvDetectorItem::getResult); |
||||
} |
||||
|
||||
public List<EnvDetectorItem> getItems() { |
||||
|
||||
return itemMap.values().stream() |
||||
.flatMap(Collection::stream) |
||||
.collect(Collectors.toList()); |
||||
} |
||||
|
||||
public Map<DetectorType.Kind, List<EnvDetectorItem>> getItemMap() { |
||||
|
||||
return this.itemMap; |
||||
} |
||||
|
||||
} |
||||
|
||||
|
After Width: | Height: | Size: 440 B |
After Width: | Height: | Size: 440 B |
After Width: | Height: | Size: 440 B |
After Width: | Height: | Size: 440 B |
After Width: | Height: | Size: 171 KiB |
After Width: | Height: | Size: 88 KiB |
After Width: | Height: | Size: 971 B |
After Width: | Height: | Size: 696 B |
After Width: | Height: | Size: 780 B |
After Width: | Height: | Size: 685 B |
After Width: | Height: | Size: 685 B |
@ -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<Frame>() { |
||||
@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 <br/> 3333", ""),new NotificationMessage.LinkMessage("display <a href='#'>model2</a> test", "abc")); |
||||
|
||||
NotificationDialogProperties properties = new NotificationDialogProperties(frame, "test"); |
||||
NotificationDialog dialog = new NotificationDialog(properties, Lists.newArrayList(model1, model2)); |
||||
dialog.setVisible(true); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
} |
@ -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<String> 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); |
||||
} |
||||
} |
@ -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<Frame>() { |
||||
@Override |
||||
public void accept(Frame frame) { |
||||
|
||||
DetectorErrorDialog dialog = new DetectorErrorDialog(frame, Lists.newArrayList(DetectorResult.exception(DetectorType.FINE_DB_PERMISSION, |
||||
ExceptionTips.create("test"), |
||||
ExceptionSolution.create("111<a href=\"\">test</a>222", "www.baidu.com", null), |
||||
ExceptionLog.create("log")))); |
||||
dialog.setVisible(true); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
} |
@ -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<Frame>() { |
||||
@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); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
} |