Browse Source
* commit 'e19fa1e976f5726f44de6d35c2171583152c7af1': (124 commits) REPORT-79889 远程设计下存储过程结果列表缓存刷新的问题 import REPORT-79881 多结果数据集树节点图标查找性能问题 REPORT-79845 同一个多结果数据集equals为false的问题 REPORT-79344【运营产品化二期】埋点暂不生效 修改pr REPORT-79344【运营产品化二期】埋点暂不生效 修改部分埋点 REPORT-79345 【冒烟】切换远程,日志刷报错write failed KERNEL-12469 js引擎j2v8剥离成独立插件 KERNEL-12469 js引擎j2v8剥离成独立插件 REPORT-79096【运营产品化二期】交互问题集合二 REPORT-79341【运营产品化二期】立即使用下载的模板名称目前是开头加了(1),是否可按浏览器规则 REPORT-79096【运营产品化二期】交互问题集合二 REPORT-79341【运营产品化二期】立即使用下载的模板名称目前是开头加了(1),是否可按浏览器规则 1、改ui 2、改文件重名规则 REPORT-79440 找不到驱动的话可以保存数据连接 改一下写法 REPORT-79492 复制多张模板,删除一部分,触发粘贴后没有定位到粘贴的位置 REPORT-79456 【运营产品化二期】远程设计下 立即使用下载不成功 远程设计获取的下载路径是个url,不可用 改为下载到桌面 REPORT-79341 【运营产品化二期】立即使用下载的模板名称目前是开头加了(1),是否可按浏览器规则 修改了重名规则 添加了异常处理 REPORT-79182 选中的文件下有锁定的模板,异常提示 REPORT-79343 【运营产品化二期】立即使用后打开的模板仍提示报存 1、打开前刷新目录 代码质量问题 REPORT-78748 搜索结果很多时,设计器界面处于卡死的状态 ...fix-lag
superman
2 years ago
191 changed files with 11555 additions and 1231 deletions
@ -0,0 +1,50 @@ |
|||||||
|
package com.fr.design.actions.file; |
||||||
|
|
||||||
|
import com.fr.design.actions.UpdateAction; |
||||||
|
import com.fr.design.dialog.FineJOptionPane; |
||||||
|
import com.fr.design.file.FileOperations; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.mainframe.DesignerContext; |
||||||
|
import com.fr.design.mainframe.DesignerFrameFileDealerPane; |
||||||
|
import com.fr.design.utils.TemplateUtils; |
||||||
|
|
||||||
|
import javax.swing.JOptionPane; |
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
|
||||||
|
import static javax.swing.JOptionPane.WARNING_MESSAGE; |
||||||
|
import static javax.swing.JOptionPane.YES_NO_OPTION; |
||||||
|
|
||||||
|
/* |
||||||
|
* 删除指定文件 |
||||||
|
*/ |
||||||
|
public class DelFileAction extends UpdateAction { |
||||||
|
|
||||||
|
public DelFileAction() { |
||||||
|
|
||||||
|
this.setName(Toolkit.i18nText("Fine-Design_Basic_Remove")); |
||||||
|
this.setSmallIcon("/com/fr/design/images/FileDealerPaneIcon/remove"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent evt) { |
||||||
|
FileOperations selectedOperation = DesignerFrameFileDealerPane.getInstance().getSelectedOperation(); |
||||||
|
if (!selectedOperation.access()) { |
||||||
|
FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Template_Permission_Denied"), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Alert"), |
||||||
|
WARNING_MESSAGE); |
||||||
|
return; |
||||||
|
} |
||||||
|
if (TemplateUtils.checkSelectedTemplateIsEditing()) { |
||||||
|
if (FineJOptionPane.showConfirmDialog(DesignerContext.getDesignerFrame(), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Template_Is_Editing"), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Alert"), |
||||||
|
YES_NO_OPTION) != JOptionPane.YES_OPTION) { |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
selectedOperation.deleteFile(); |
||||||
|
DesignerFrameFileDealerPane.getInstance().stateChange(); |
||||||
|
DesignerContext.getDesignerFrame().setTitle(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,163 @@ |
|||||||
|
package com.fr.design.actions.file; |
||||||
|
|
||||||
|
import com.fr.design.actions.UpdateAction; |
||||||
|
import com.fr.design.file.HistoryTemplateListCache; |
||||||
|
import com.fr.design.file.TemplateTreePane; |
||||||
|
import com.fr.design.gui.itree.filetree.TemplateFileTree; |
||||||
|
import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; |
||||||
|
import com.fr.design.gui.itree.refreshabletree.RefreshableJTree; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.mainframe.JTemplate; |
||||||
|
import com.fr.design.mainframe.manager.search.TemplateTreeSearchManager; |
||||||
|
import com.fr.file.filetree.FileNode; |
||||||
|
import com.fr.general.ComparatorUtils; |
||||||
|
import com.fr.stable.CoreConstants; |
||||||
|
import com.fr.stable.StableUtils; |
||||||
|
import com.fr.stable.project.ProjectConstants; |
||||||
|
|
||||||
|
import javax.swing.tree.DefaultTreeModel; |
||||||
|
import javax.swing.tree.TreeNode; |
||||||
|
import javax.swing.tree.TreePath; |
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
import java.io.File; |
||||||
|
|
||||||
|
/** |
||||||
|
* 模板定位功能 |
||||||
|
*/ |
||||||
|
public class LocateAction extends UpdateAction { |
||||||
|
|
||||||
|
public LocateAction() { |
||||||
|
this.setName(Toolkit.i18nText("Fine-Design_Basic_Locate")); |
||||||
|
this.setSmallIcon("/com/fr/design/images/FileDealerPaneIcon/locate.png"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
JTemplate<?, ?> current = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); |
||||||
|
gotoEditingTemplateLeaf(current.getEditingFILE().getPath(), false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 在左侧模板树定位到指定模板 |
||||||
|
* |
||||||
|
* @param locatedPath |
||||||
|
*/ |
||||||
|
public static void gotoEditingTemplateLeaf(String locatedPath) { |
||||||
|
gotoEditingTemplateLeaf(locatedPath, true); |
||||||
|
} |
||||||
|
|
||||||
|
private static void gotoEditingTemplateLeaf(String locatedPath, boolean needRefreshMode) { |
||||||
|
if (locatedPath == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
if (TemplateTreeSearchManager.getInstance().isInSearchMode()) { |
||||||
|
TemplateTreeSearchManager.getInstance().outOfSearchMode(); |
||||||
|
} |
||||||
|
|
||||||
|
DefaultTreeModel model = (DefaultTreeModel) getTemplateFileTree().getModel(); |
||||||
|
ExpandMutableTreeNode treeNode = (ExpandMutableTreeNode) model.getRoot(); |
||||||
|
if (needRefreshMode) { |
||||||
|
treeNode.removeAllChildren(); |
||||||
|
ExpandMutableTreeNode[] childTreeNodes = getTemplateFileTree().loadChildTreeNodes(treeNode); |
||||||
|
treeNode.addChildTreeNodes(childTreeNodes); |
||||||
|
model.reload(treeNode); |
||||||
|
} |
||||||
|
|
||||||
|
recursiveSelectPath(treeNode, locatedPath, model); |
||||||
|
TreePath selectedTreePath = getTemplateFileTree().getSelectionPath(); |
||||||
|
if (selectedTreePath != null) { |
||||||
|
getTemplateFileTree().scrollPathToVisible(selectedTreePath); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static TemplateFileTree getTemplateFileTree() { |
||||||
|
return TemplateTreePane.getInstance().getTemplateFileTree(); |
||||||
|
} |
||||||
|
|
||||||
|
private static void recursiveSelectPath(TreeNode treeNode, String currentPath, DefaultTreeModel m_model) { |
||||||
|
for (int i = 0, len = treeNode.getChildCount(); i < len; i++) { |
||||||
|
TreeNode node = treeNode.getChildAt(i); |
||||||
|
// 取出当前的childTreeNode,并append到searchingPath后面
|
||||||
|
ExpandMutableTreeNode childTreeNode = (ExpandMutableTreeNode) node; |
||||||
|
if (selectFilePath(childTreeNode, ProjectConstants.REPORTLETS_NAME, currentPath, m_model)) { |
||||||
|
break; |
||||||
|
} |
||||||
|
if (!node.isLeaf()) { |
||||||
|
for (int j = 0; j < node.getChildCount(); j++) { |
||||||
|
recursiveSelectPath(node.getChildAt(j), currentPath, m_model); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* 在currentTreeNode下找寻filePath |
||||||
|
* |
||||||
|
* prefix + currentTreeNode.getName() = currentTreeNode所对应的Path |
||||||
|
* |
||||||
|
* 返回currentTreeNode下是否找到了filePath |
||||||
|
*/ |
||||||
|
private static boolean selectFilePath(ExpandMutableTreeNode currentTreeNode, String prefix, String filePath, DefaultTreeModel model) { |
||||||
|
Object userObj = currentTreeNode.getUserObject(); |
||||||
|
if (!(userObj instanceof FileNode)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
FileNode fileNode = (FileNode) userObj; |
||||||
|
String nodePath = fileNode.getName(); |
||||||
|
String currentTreePath = StableUtils.pathJoin(prefix, nodePath); |
||||||
|
boolean result = false; |
||||||
|
|
||||||
|
// 如果equals,说明找到了,不必再找下去了
|
||||||
|
if (ComparatorUtils.equals(new File(currentTreePath), new File(filePath))) { |
||||||
|
getTemplateFileTree().setSelectionPath(new TreePath(model.getPathToRoot(currentTreeNode))); |
||||||
|
result = true; |
||||||
|
} |
||||||
|
// 如果当前路径是currentFilePath的ParentFile,则expandTreeNode,并继续往下找
|
||||||
|
else if (isParentFile(currentTreePath, filePath)) { |
||||||
|
loadPendingChildTreeNode(currentTreeNode); |
||||||
|
prefix = currentTreePath + CoreConstants.SEPARATOR; |
||||||
|
for (int i = 0, len = currentTreeNode.getChildCount(); i < len; i++) { |
||||||
|
ExpandMutableTreeNode childTreeNode = (ExpandMutableTreeNode) currentTreeNode.getChildAt(i); |
||||||
|
|
||||||
|
if (selectFilePath(childTreeNode, prefix, filePath, model)) { |
||||||
|
result = true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
protected static void loadPendingChildTreeNode(ExpandMutableTreeNode currentTreeNode) { |
||||||
|
if (currentTreeNode.isLeaf()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// 判断第一个孩子节点.UserObject是不是PENDING,如果是PENDING的话,需要重新加载这个TreeNode
|
||||||
|
ExpandMutableTreeNode flag = (ExpandMutableTreeNode) currentTreeNode.getFirstChild(); |
||||||
|
if (flag == null || !flag.getUserObject().toString().equals(RefreshableJTree.PENDING.toString())) { |
||||||
|
return; |
||||||
|
} |
||||||
|
// 删除所有的节点.
|
||||||
|
currentTreeNode.removeAllChildren(); |
||||||
|
|
||||||
|
ExpandMutableTreeNode[] children = getTemplateFileTree().loadChildTreeNodes(currentTreeNode); |
||||||
|
for (ExpandMutableTreeNode c : children) { |
||||||
|
currentTreeNode.add(c); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static boolean isParentFile(String var0, String var1) { |
||||||
|
File var2 = new File(var0); |
||||||
|
File var3 = new File(var1); |
||||||
|
|
||||||
|
while (!ComparatorUtils.equals(var2, var3)) { |
||||||
|
var3 = var3.getParentFile(); |
||||||
|
if (var3 == null) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,347 @@ |
|||||||
|
package com.fr.design.actions.file; |
||||||
|
|
||||||
|
import com.fr.base.BaseUtils; |
||||||
|
import com.fr.chartx.TwoTuple; |
||||||
|
import com.fr.design.DesignerEnvManager; |
||||||
|
import com.fr.design.actions.UpdateAction; |
||||||
|
import com.fr.design.dialog.FineJOptionPane; |
||||||
|
import com.fr.design.file.FileOperations; |
||||||
|
import com.fr.design.file.HistoryTemplateListCache; |
||||||
|
import com.fr.design.file.MutilTempalteTabPane; |
||||||
|
import com.fr.design.gui.ibutton.UIButton; |
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.design.gui.itextfield.UITextField; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.layout.TableLayout; |
||||||
|
import com.fr.design.layout.TableLayoutHelper; |
||||||
|
import com.fr.design.mainframe.DesignerContext; |
||||||
|
import com.fr.design.mainframe.DesignerFrameFileDealerPane; |
||||||
|
import com.fr.design.mainframe.manager.search.TemplateTreeSearchManager; |
||||||
|
import com.fr.design.mainframe.vcs.common.VcsHelper; |
||||||
|
import com.fr.design.utils.TemplateUtils; |
||||||
|
import com.fr.design.utils.gui.GUICoreUtils; |
||||||
|
import com.fr.event.EventDispatcher; |
||||||
|
import com.fr.file.FileNodeFILE; |
||||||
|
import com.fr.file.filetree.FileNode; |
||||||
|
import com.fr.general.ComparatorUtils; |
||||||
|
import com.fr.stable.CoreConstants; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
import com.fr.stable.project.ProjectConstants; |
||||||
|
import com.fr.third.org.apache.commons.io.FilenameUtils; |
||||||
|
|
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.JDialog; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.JOptionPane; |
||||||
|
import javax.swing.SwingConstants; |
||||||
|
import javax.swing.event.DocumentEvent; |
||||||
|
import javax.swing.event.DocumentListener; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Component; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.FlowLayout; |
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
import java.awt.event.ActionListener; |
||||||
|
import java.awt.event.KeyAdapter; |
||||||
|
import java.awt.event.KeyEvent; |
||||||
|
import java.awt.event.KeyListener; |
||||||
|
import java.util.regex.Pattern; |
||||||
|
|
||||||
|
import static javax.swing.JOptionPane.DEFAULT_OPTION; |
||||||
|
import static javax.swing.JOptionPane.ERROR_MESSAGE; |
||||||
|
import static javax.swing.JOptionPane.WARNING_MESSAGE; |
||||||
|
import static javax.swing.JOptionPane.YES_NO_OPTION; |
||||||
|
|
||||||
|
/** |
||||||
|
* 模板/目录重命名操作 |
||||||
|
*/ |
||||||
|
public class RenameAction extends UpdateAction { |
||||||
|
|
||||||
|
private FileOperations selectedOperation; |
||||||
|
|
||||||
|
public RenameAction() { |
||||||
|
|
||||||
|
this.setName(Toolkit.i18nText("Fine-Design_Basic_Rename")); |
||||||
|
this.setSmallIcon("/com/fr/design/images/FileDealerPaneIcon/rename"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent evt) { |
||||||
|
selectedOperation = DesignerFrameFileDealerPane.getInstance().getSelectedOperation(); |
||||||
|
if (!selectedOperation.access()) { |
||||||
|
FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Template_Permission_Denied"), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Alert"), |
||||||
|
WARNING_MESSAGE); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
FileNode node = selectedOperation.getFileNode(); |
||||||
|
String lock = node.getLock(); |
||||||
|
if (lock != null && !lock.equals(node.getUserID())) { |
||||||
|
// 提醒被锁定模板无法重命名
|
||||||
|
FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Unable_Rename_Locked_File"), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Alert"), |
||||||
|
WARNING_MESSAGE); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
new FileRenameDialog(node); |
||||||
|
MutilTempalteTabPane.getInstance().repaint(); |
||||||
|
DesignerFrameFileDealerPane.getInstance().stateChange(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 重命名对话框 |
||||||
|
* 支持快捷键Enter,ESC |
||||||
|
*/ |
||||||
|
private class FileRenameDialog extends JDialog { |
||||||
|
|
||||||
|
private UITextField nameField; |
||||||
|
|
||||||
|
private UILabel warnLabel; |
||||||
|
|
||||||
|
private UIButton confirmButton; |
||||||
|
|
||||||
|
/** |
||||||
|
* 操作的节点 |
||||||
|
*/ |
||||||
|
private FileNodeFILE fnf; |
||||||
|
|
||||||
|
private KeyListener keyListener = new KeyAdapter() { |
||||||
|
@Override |
||||||
|
public void keyPressed(KeyEvent e) { |
||||||
|
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { |
||||||
|
dispose(); |
||||||
|
} else if (e.getKeyCode() == KeyEvent.VK_ENTER) { |
||||||
|
if (confirmButton.isEnabled()) { |
||||||
|
confirmClose(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
private FileRenameDialog(FileNode node) { |
||||||
|
super(DesignerContext.getDesignerFrame(), true); |
||||||
|
if (node == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
fnf = new FileNodeFILE(node); |
||||||
|
String oldName = fnf.getName(); |
||||||
|
String suffix = fnf.isDirectory() ? StringUtils.EMPTY : oldName.substring(oldName.lastIndexOf(CoreConstants.DOT), oldName.length()); |
||||||
|
oldName = StringUtils.replaceLast(oldName, suffix, StringUtils.EMPTY); |
||||||
|
|
||||||
|
initPane(oldName); |
||||||
|
} |
||||||
|
|
||||||
|
private void initPane(String oldName) { |
||||||
|
this.setLayout(new BorderLayout()); |
||||||
|
|
||||||
|
// 输入框前提示
|
||||||
|
UILabel newNameLabel = new UILabel(Toolkit.i18nText( |
||||||
|
fnf.isDirectory() ? |
||||||
|
"Fine-Design_Basic_Enter_New_Folder_Name" : "Fine-Design_Basic_Enter_New_File_Name") |
||||||
|
); |
||||||
|
newNameLabel.setHorizontalAlignment(SwingConstants.RIGHT); |
||||||
|
newNameLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10)); |
||||||
|
//newNameLabel.setPreferredSize(new Dimension(118, 15));
|
||||||
|
|
||||||
|
// 重命名输入框
|
||||||
|
nameField = new UITextField(oldName); |
||||||
|
nameField.getDocument().addDocumentListener(new DocumentListener() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void changedUpdate(DocumentEvent e) { |
||||||
|
validInput(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void insertUpdate(DocumentEvent e) { |
||||||
|
validInput(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeUpdate(DocumentEvent e) { |
||||||
|
validInput(); |
||||||
|
} |
||||||
|
}); |
||||||
|
nameField.selectAll(); |
||||||
|
nameField.setPreferredSize(new Dimension(170, 20)); |
||||||
|
|
||||||
|
JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 5)); |
||||||
|
topPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 0, 15)); |
||||||
|
topPanel.add(newNameLabel); |
||||||
|
topPanel.add(nameField); |
||||||
|
|
||||||
|
// 增加enter以及esc快捷键的支持
|
||||||
|
nameField.addKeyListener(keyListener); |
||||||
|
// 重名提示
|
||||||
|
warnLabel = new UILabel(); |
||||||
|
warnLabel.setPreferredSize(new Dimension(300, 50)); |
||||||
|
warnLabel.setHorizontalAlignment(SwingConstants.LEFT); |
||||||
|
warnLabel.setVerticalAlignment(SwingConstants.TOP); |
||||||
|
warnLabel.setForeground(Color.RED); |
||||||
|
warnLabel.setVisible(false); |
||||||
|
|
||||||
|
JPanel midPanel = new JPanel(new BorderLayout()); |
||||||
|
midPanel.setBorder(BorderFactory.createEmptyBorder(0, 15, 0, 15)); |
||||||
|
midPanel.add(warnLabel, BorderLayout.WEST); |
||||||
|
|
||||||
|
// 确认按钮
|
||||||
|
confirmButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Confirm")); |
||||||
|
confirmButton.addActionListener(new ActionListener() { |
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
confirmClose(); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
// 取消按钮
|
||||||
|
UIButton cancelButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Cancel")); |
||||||
|
|
||||||
|
cancelButton.addActionListener(new ActionListener() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
dispose(); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
|
||||||
|
JPanel buttonsPane = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 0)); |
||||||
|
buttonsPane.setBorder(BorderFactory.createEmptyBorder(10, 15, 10, 10)); |
||||||
|
buttonsPane.add(confirmButton); |
||||||
|
buttonsPane.add(cancelButton); |
||||||
|
|
||||||
|
this.add( |
||||||
|
TableLayoutHelper.createTableLayoutPane( |
||||||
|
new Component[][]{ |
||||||
|
new Component[]{topPanel}, |
||||||
|
new Component[]{midPanel}, |
||||||
|
new Component[]{buttonsPane} |
||||||
|
}, |
||||||
|
new double[]{TableLayout.FILL, TableLayout.PREFERRED, TableLayout.PREFERRED}, |
||||||
|
new double[]{TableLayout.FILL} |
||||||
|
), |
||||||
|
BorderLayout.CENTER); |
||||||
|
|
||||||
|
this.setSize(340, 200); |
||||||
|
this.setTitle(Toolkit.i18nText("Fine-Design_Basic_Rename")); |
||||||
|
this.setResizable(false); |
||||||
|
this.setIconImage(BaseUtils.readImage("/com/fr/base/images/oem/logo.png")); |
||||||
|
this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); |
||||||
|
GUICoreUtils.centerWindow(this); |
||||||
|
this.setVisible(true); |
||||||
|
} |
||||||
|
|
||||||
|
private void confirmClose() { |
||||||
|
|
||||||
|
if (TemplateUtils.checkSelectedTemplateIsEditing()) { |
||||||
|
if (FineJOptionPane.showConfirmDialog(this, |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Template_Is_Editing"), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Alert"), |
||||||
|
YES_NO_OPTION) != JOptionPane.YES_OPTION) { |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
String userInput = nameField.getText().trim(); |
||||||
|
|
||||||
|
String path = FilenameUtils.standard(fnf.getPath()); |
||||||
|
|
||||||
|
String oldName = fnf.getName(); |
||||||
|
String suffix = fnf.isDirectory() ? StringUtils.EMPTY : oldName.substring(oldName.lastIndexOf(CoreConstants.DOT), oldName.length()); |
||||||
|
oldName = StringUtils.replaceLast(oldName, suffix, StringUtils.EMPTY); |
||||||
|
|
||||||
|
// 输入为空或者没有修改
|
||||||
|
if (ComparatorUtils.equals(userInput, oldName)) { |
||||||
|
this.dispose(); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
String parentPath = FilenameUtils.standard(fnf.getParent().getPath()); |
||||||
|
|
||||||
|
// 简单执行old new 替换是不可行的,例如 /abc/abc/abc/abc/
|
||||||
|
String newPath = parentPath + CoreConstants.SEPARATOR + userInput + suffix; |
||||||
|
this.dispose(); |
||||||
|
|
||||||
|
//模版重命名
|
||||||
|
boolean success = selectedOperation.rename(fnf, path, newPath); |
||||||
|
|
||||||
|
if (success) { |
||||||
|
afterRename(path, newPath); |
||||||
|
} else { |
||||||
|
FineJOptionPane.showConfirmDialog(DesignerContext.getDesignerFrame(), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Rename_Failure"), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Error"), |
||||||
|
DEFAULT_OPTION, |
||||||
|
ERROR_MESSAGE); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void afterRename(String path, String newPath) { |
||||||
|
EventDispatcher.fire(DesignerFrameFileDealerPane.TEMPLATE_RENAME, new TwoTuple<>(path, newPath)); |
||||||
|
HistoryTemplateListCache.getInstance().rename(fnf, path, newPath); |
||||||
|
DesignerEnvManager.getEnvManager().replaceRecentOpenedFilePath(fnf.isDirectory(), path, newPath); |
||||||
|
selectedOperation.refreshParent(); |
||||||
|
if (!fnf.isDirectory()) { |
||||||
|
//版本控制:未打开模板时重命名,是个纯文件操作
|
||||||
|
//不借助JTemplate的事件触发机制实现(需要新创建JTemplate,并添加监听,不确定会不会有问题)
|
||||||
|
path = path.replaceFirst(ProjectConstants.REPORTLETS_NAME, StringUtils.EMPTY); |
||||||
|
VcsHelper.getInstance().moveVcs(path, newPath.replaceFirst(ProjectConstants.REPORTLETS_NAME, StringUtils.EMPTY)); |
||||||
|
} |
||||||
|
DesignerContext.getDesignerFrame().setTitle(); |
||||||
|
LocateAction.gotoEditingTemplateLeaf(newPath); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private void validInput() { |
||||||
|
|
||||||
|
String userInput = nameField.getText().trim(); |
||||||
|
|
||||||
|
String oldName = fnf.getName(); |
||||||
|
String suffix = fnf.isDirectory() ? StringUtils.EMPTY : oldName.substring(oldName.lastIndexOf(CoreConstants.DOT), oldName.length()); |
||||||
|
oldName = oldName.replaceAll(suffix, StringUtils.EMPTY); |
||||||
|
|
||||||
|
if (StringUtils.isEmpty(userInput)) { |
||||||
|
confirmButton.setEnabled(false); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (ComparatorUtils.equals(userInput, oldName)) { |
||||||
|
warnLabel.setVisible(false); |
||||||
|
confirmButton.setEnabled(true); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
String errorMsg = doCheck(userInput, suffix, fnf.isDirectory()); |
||||||
|
if (StringUtils.isNotEmpty(errorMsg)) { |
||||||
|
nameField.selectAll(); |
||||||
|
// 如果文件名已存在,则灰掉确认按钮
|
||||||
|
warnLabel.setText(errorMsg); |
||||||
|
warnLabel.setVisible(true); |
||||||
|
confirmButton.setEnabled(false); |
||||||
|
} else { |
||||||
|
warnLabel.setVisible(false); |
||||||
|
confirmButton.setEnabled(true); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private String doCheck (String userInput, String suffix, boolean isDirectory) { |
||||||
|
String errorMsg = StringUtils.EMPTY; |
||||||
|
if (selectedOperation.duplicated(userInput, suffix, false)) { |
||||||
|
errorMsg = Toolkit.i18nText(isDirectory ? |
||||||
|
"Fine-Design_Basic_Folder_Name_Duplicate" : |
||||||
|
"Fine-Design_Basic_Template_File_Name_Duplicate", |
||||||
|
userInput); |
||||||
|
} |
||||||
|
if (!Pattern.compile(DesignerFrameFileDealerPane.FILE_NAME_LIMIT).matcher(userInput).matches()) { |
||||||
|
errorMsg = Toolkit.i18nText("Fine-Design_Basic_Template_Name_Illegal"); |
||||||
|
} |
||||||
|
return errorMsg; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
package com.fr.design.carton; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
|
||||||
|
public class CartonFiles { |
||||||
|
private File easyCheckerFile; |
||||||
|
private File timerCheckerFile; |
||||||
|
|
||||||
|
public CartonFiles() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public File getEasyCheckerFile() { |
||||||
|
return easyCheckerFile; |
||||||
|
} |
||||||
|
|
||||||
|
public void setEasyCheckerFile(File easyCheckerFile) { |
||||||
|
this.easyCheckerFile = easyCheckerFile; |
||||||
|
} |
||||||
|
|
||||||
|
public File getTimerCheckerFile() { |
||||||
|
return timerCheckerFile; |
||||||
|
} |
||||||
|
|
||||||
|
public void setTimerCheckerFile(File timerCheckerFile) { |
||||||
|
this.timerCheckerFile = timerCheckerFile; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,161 @@ |
|||||||
|
package com.fr.design.carton; |
||||||
|
|
||||||
|
import com.fr.base.SimpleDateFormatThreadSafe; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.json.JSONObject; |
||||||
|
|
||||||
|
import java.util.Map; |
||||||
|
import java.util.Timer; |
||||||
|
import java.util.TimerTask; |
||||||
|
import java.util.concurrent.*; |
||||||
|
import java.util.concurrent.atomic.AtomicLong; |
||||||
|
|
||||||
|
|
||||||
|
public class CartonThreadExecutorPool extends ThreadPoolExecutor { |
||||||
|
private static final int MAX_LIVE_TIME = 3000; |
||||||
|
private static final int MAX_WORKER_THREADS = 10; |
||||||
|
/** |
||||||
|
* 开启间隔检测后两次检测的相隔时间ms |
||||||
|
*/ |
||||||
|
private static final long CHECK_INTERVAL_MS = 100; |
||||||
|
private final ThreadLocal<StackTraceElement[]> startReportedStack = new ThreadLocal<>(); |
||||||
|
private volatile static CartonThreadExecutorPool cartonThreadExecutorPool; |
||||||
|
private static final ConcurrentHashMap<Long, ThreadInfo> concurrentHashMap = new ConcurrentHashMap<>(); |
||||||
|
private static final SimpleDateFormatThreadSafe simpleDateFormatThreadSafe = new SimpleDateFormatThreadSafe(); |
||||||
|
private final static AtomicLong hangCount = new AtomicLong(0); |
||||||
|
private Timer timer; |
||||||
|
/** |
||||||
|
* 一个变量,用于控制easy监测模式的开关 |
||||||
|
*/ |
||||||
|
private boolean easyWitch = false; |
||||||
|
|
||||||
|
public boolean isEasyWitch() { |
||||||
|
return easyWitch; |
||||||
|
} |
||||||
|
|
||||||
|
public void setEasyWitch(boolean easyWitch) { |
||||||
|
this.easyWitch = easyWitch; |
||||||
|
} |
||||||
|
|
||||||
|
private static class ThreadInfo { |
||||||
|
private ThreadInfo () { |
||||||
|
hangNumber = hangCount.getAndAdd(1); |
||||||
|
} |
||||||
|
private long hangNumber; |
||||||
|
private final Thread eventThread = Thread.currentThread(); |
||||||
|
private StackTraceElement[] lastReportedStack; |
||||||
|
private final long startTime = System.currentTimeMillis(); |
||||||
|
public void checkForHang() { |
||||||
|
if (timeSoFar() > MAX_LIVE_TIME) { |
||||||
|
examineHang(); |
||||||
|
} |
||||||
|
} |
||||||
|
private long timeSoFar() { |
||||||
|
return (System.currentTimeMillis() - startTime); |
||||||
|
} |
||||||
|
|
||||||
|
private void examineHang() { |
||||||
|
StackTraceElement[] currentStack = eventThread.getStackTrace(); |
||||||
|
|
||||||
|
if (lastReportedStack!=null && EventDispatchThreadHangMonitor.stacksEqual(currentStack, lastReportedStack)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
lastReportedStack = currentStack; |
||||||
|
String stackTrace = EventDispatchThreadHangMonitor.stackTraceToString(currentStack); |
||||||
|
JSONObject jsonObject = new JSONObject(); |
||||||
|
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"), simpleDateFormatThreadSafe.format(System.currentTimeMillis())); |
||||||
|
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"), "swingWorker_" + hangNumber); |
||||||
|
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Duration_Task_Execute"), timeSoFar() + "ms"); |
||||||
|
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Stack_Info"), stackTrace); |
||||||
|
EventDispatchThreadHangMonitor.outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.TIMER_CHECK_FLAG); |
||||||
|
EventDispatchThreadHangMonitor.checkForDeadlock(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 来自SwingWorker类 |
||||||
|
*/ |
||||||
|
public static ThreadFactory threadFactory = |
||||||
|
new ThreadFactory() { |
||||||
|
final ThreadFactory defaultFactory = |
||||||
|
Executors.defaultThreadFactory(); |
||||||
|
@Override |
||||||
|
public Thread newThread(final Runnable r) { |
||||||
|
Thread thread = |
||||||
|
defaultFactory.newThread(r); |
||||||
|
thread.setName("SwingWorker-" |
||||||
|
+ thread.getName()); |
||||||
|
thread.setDaemon(true); |
||||||
|
return thread; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
public static CartonThreadExecutorPool getTimerThreadExecutorPool () { |
||||||
|
if (cartonThreadExecutorPool == null) { |
||||||
|
synchronized (CartonThreadExecutorPool.class) { |
||||||
|
if (cartonThreadExecutorPool == null) { |
||||||
|
cartonThreadExecutorPool = |
||||||
|
new CartonThreadExecutorPool(MAX_WORKER_THREADS, MAX_WORKER_THREADS, |
||||||
|
10L, TimeUnit.MINUTES, |
||||||
|
new LinkedBlockingQueue<Runnable>(),threadFactory |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return cartonThreadExecutorPool; |
||||||
|
} |
||||||
|
|
||||||
|
private CartonThreadExecutorPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) { |
||||||
|
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory); |
||||||
|
simpleDateFormatThreadSafe.applyPattern("yyyy-MM-dd HH:mm:ss"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void beforeExecute(Thread t, Runnable r) { |
||||||
|
super.beforeExecute(t, r); |
||||||
|
startReportedStack.set(t.getStackTrace()); |
||||||
|
concurrentHashMap.put(t.getId(), new ThreadInfo()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void afterExecute(Runnable r, Throwable t) { |
||||||
|
super.afterExecute(r, t); |
||||||
|
long currentThreadId = Thread.currentThread().getId(); |
||||||
|
long runTime = (System.currentTimeMillis() - concurrentHashMap.get(currentThreadId).startTime); |
||||||
|
//加~是为了之后输出的时候换行。
|
||||||
|
if (isEasyWitch() && runTime > MAX_LIVE_TIME) { |
||||||
|
JSONObject jsonObject = new JSONObject(); |
||||||
|
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"), simpleDateFormatThreadSafe.format(System.currentTimeMillis())); |
||||||
|
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"), "swingWorker_" + concurrentHashMap.get(currentThreadId).hangNumber); |
||||||
|
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Start_Time"), simpleDateFormatThreadSafe.format(concurrentHashMap.get(currentThreadId).startTime)); |
||||||
|
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Total_Time"), runTime + "ms"); |
||||||
|
EventDispatchThreadHangMonitor.outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.EASY_CHECK_FLAG); |
||||||
|
|
||||||
|
} |
||||||
|
concurrentHashMap.remove(currentThreadId); |
||||||
|
} |
||||||
|
|
||||||
|
public class Checker extends TimerTask { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
if (cartonThreadExecutorPool == null || concurrentHashMap.isEmpty()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
for (Map.Entry<Long, ThreadInfo> map : concurrentHashMap.entrySet()) { |
||||||
|
map.getValue().checkForHang(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void initTimer() { |
||||||
|
timer = new Timer("CheckerSwingWorker",true); |
||||||
|
timer.schedule(new Checker(), 0, CHECK_INTERVAL_MS); |
||||||
|
} |
||||||
|
|
||||||
|
public void stopTimer() { |
||||||
|
if (timer != null) { |
||||||
|
timer.cancel(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,45 @@ |
|||||||
|
package com.fr.design.carton; |
||||||
|
|
||||||
|
|
||||||
|
public class CartonUploadMessage { |
||||||
|
private String hangCount; |
||||||
|
private String slowTime; |
||||||
|
private String threadTime; |
||||||
|
private String info; |
||||||
|
|
||||||
|
public CartonUploadMessage() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public String getHangCount() { |
||||||
|
return hangCount; |
||||||
|
} |
||||||
|
|
||||||
|
public void setHangCount(String hangCount) { |
||||||
|
this.hangCount = hangCount; |
||||||
|
} |
||||||
|
|
||||||
|
public String getSlowTime() { |
||||||
|
return slowTime; |
||||||
|
} |
||||||
|
|
||||||
|
public void setSlowTime(String slowTime) { |
||||||
|
this.slowTime = slowTime; |
||||||
|
} |
||||||
|
|
||||||
|
public String getThreadTime() { |
||||||
|
return threadTime; |
||||||
|
} |
||||||
|
|
||||||
|
public void setThreadTime(String threadTime) { |
||||||
|
this.threadTime = threadTime; |
||||||
|
} |
||||||
|
|
||||||
|
public String getInfo() { |
||||||
|
return info; |
||||||
|
} |
||||||
|
|
||||||
|
public void setInfo(String info) { |
||||||
|
this.info = info; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,407 @@ |
|||||||
|
package com.fr.design.carton; |
||||||
|
|
||||||
|
import com.fr.concurrent.FineExecutors; |
||||||
|
import com.fr.json.JSONObject; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.stable.ArrayUtils; |
||||||
|
import com.fr.stable.ProductConstantsBase; |
||||||
|
import com.fr.stable.StableUtils; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
import com.fr.third.ibm.icu.text.SimpleDateFormat; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import javax.swing.SwingUtilities; |
||||||
|
import java.awt.EventQueue; |
||||||
|
import java.awt.Toolkit; |
||||||
|
import java.awt.AWTEvent; |
||||||
|
import java.awt.event.WindowEvent; |
||||||
|
import java.io.BufferedWriter; |
||||||
|
import java.io.File; |
||||||
|
import java.io.FileWriter; |
||||||
|
import java.io.IOException; |
||||||
|
import java.lang.management.ManagementFactory; |
||||||
|
import java.lang.management.ThreadInfo; |
||||||
|
import java.lang.management.ThreadMXBean; |
||||||
|
import java.util.LinkedList; |
||||||
|
import java.util.Timer; |
||||||
|
import java.util.TimerTask; |
||||||
|
import java.util.concurrent.ScheduledExecutorService; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
|
||||||
|
/** |
||||||
|
* 参考自git swinghelper |
||||||
|
* 用于卡顿检测 |
||||||
|
* 主要是两块内容 |
||||||
|
* 1.获取eventQueue中每个事件的执行时间 |
||||||
|
* 2.用一个Timer定时去检测当前执行任务的线程 |
||||||
|
*/ |
||||||
|
|
||||||
|
public final class EventDispatchThreadHangMonitor extends EventQueue { |
||||||
|
/** |
||||||
|
* 日期事件格式 |
||||||
|
*/ |
||||||
|
private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
||||||
|
public static final EventDispatchThreadHangMonitor INSTANCE = new EventDispatchThreadHangMonitor(); |
||||||
|
/** |
||||||
|
* 一个timer |
||||||
|
*/ |
||||||
|
private Timer timer; |
||||||
|
/** |
||||||
|
* 开启间隔检测后两次检测的相隔时间ms |
||||||
|
*/ |
||||||
|
private static final long CHECK_INTERVAL_MS = 100; |
||||||
|
/** |
||||||
|
* 最大的事件允许执行时间,超过该时间则打印堆栈等相关信息 |
||||||
|
*/ |
||||||
|
private static final long UNREASONABLE_DISPATCH_DURATION_MS = 1500; |
||||||
|
/** |
||||||
|
* 事件唯一编码,用于方便日志的查看 |
||||||
|
*/ |
||||||
|
private static long hangCount = 0; |
||||||
|
/** |
||||||
|
* 输出日志所在地址 |
||||||
|
*/ |
||||||
|
private static final String JOURNAL_FILE_PATH = StableUtils.pathJoin(ProductConstantsBase.getEnvHome(), "journal_log"); |
||||||
|
/** |
||||||
|
* 类似于一个开关,当该值为默认的false启动时,定时任务在窗口开启前都不会对执行的事件进行检查 |
||||||
|
*/ |
||||||
|
private boolean haveShownSomeComponent = false; |
||||||
|
/** |
||||||
|
* 该链表为主要的实现定时任务的容器,在重写的dispatchEvent中由pre方法将DispatchInfo加入到链表,由post方法remove |
||||||
|
*/ |
||||||
|
private final LinkedList<DispatchInfo> dispatches = new LinkedList<DispatchInfo>(); |
||||||
|
/** |
||||||
|
* 一个变量,用于控制easy监测模式的开关 |
||||||
|
*/ |
||||||
|
private boolean easyWitch = false; |
||||||
|
private static ScheduledExecutorService scheduledExecutorService; |
||||||
|
|
||||||
|
public boolean isEasyWitch() { |
||||||
|
return easyWitch; |
||||||
|
} |
||||||
|
|
||||||
|
public void setEasyWitch(boolean easyWitch) { |
||||||
|
this.easyWitch = easyWitch; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 一个变量,用于记录Timer的开关。 |
||||||
|
*/ |
||||||
|
|
||||||
|
public boolean isTimerWitch() { |
||||||
|
return timerWitch; |
||||||
|
} |
||||||
|
|
||||||
|
public void setTimerWitch(boolean timerWitch) { |
||||||
|
this.timerWitch = timerWitch; |
||||||
|
} |
||||||
|
|
||||||
|
private boolean timerWitch = false; |
||||||
|
|
||||||
|
private synchronized static long getHangCount() { |
||||||
|
return hangCount++; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param a can not be null |
||||||
|
* @param b can not be null |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static boolean stacksEqual(@NotNull StackTraceElement[] a, @NotNull StackTraceElement[] b) { |
||||||
|
|
||||||
|
if (!ArrayUtils.isSameLength(a, b)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
for (int i = 0; i < a.length; ++i) { |
||||||
|
if (!a[i].equals(b[i])) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 用于判断是不是特定的堆栈 |
||||||
|
*/ |
||||||
|
public static boolean stackTraceElementIs(StackTraceElement e, String className, String methodName, boolean isNative) { |
||||||
|
return e.getClassName().equals(className) && e.getMethodName().equals(methodName) && e.isNativeMethod() == isNative; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 用于判断某个堆栈是否在等待另一个事件 |
||||||
|
* 取当前堆栈前三层判断是是不是匹配等待堆栈的格式 |
||||||
|
*/ |
||||||
|
public static boolean isWaitingForNextEvent(StackTraceElement[] currentStack) { |
||||||
|
|
||||||
|
return currentStack != null && currentStack.length >= 3 && |
||||||
|
stackTraceElementIs(currentStack[0], "java.lang.Object", "wait", true) |
||||||
|
&& stackTraceElementIs(currentStack[1], "java.lang.Object", "wait", false) |
||||||
|
&& stackTraceElementIs(currentStack[2], "java.awt.EventQueue", "getNextEvent", false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* event事件的包装类 |
||||||
|
*/ |
||||||
|
public static class DispatchInfo { |
||||||
|
// 上一次被打印的堆栈ou
|
||||||
|
private StackTraceElement[] lastReportedStack; |
||||||
|
//获取执行该事件的线程
|
||||||
|
private final Thread eventDispatchThread = Thread.currentThread(); |
||||||
|
//在队列中等待执行的事件最后未执行的时间,当有一个事件执行完后就遍历dispatches给该值赋当前时间
|
||||||
|
private long lastDispatchTimeMillis = System.currentTimeMillis(); |
||||||
|
//事件开始的时间
|
||||||
|
private final long startDispatchTimeMillis = System.currentTimeMillis(); |
||||||
|
//事件编号
|
||||||
|
private long hangNumber; |
||||||
|
//构造函数,给当前对象赋一个递增的唯一编号
|
||||||
|
public DispatchInfo() { |
||||||
|
hangNumber = getHangCount(); |
||||||
|
} |
||||||
|
//定时调度任务检测的入口,如果执行时间大于设定的值就进入examineHang()方法
|
||||||
|
public void checkForHang() { |
||||||
|
if (timeSoFar() > UNREASONABLE_DISPATCH_DURATION_MS) { |
||||||
|
examineHang(); |
||||||
|
} |
||||||
|
} |
||||||
|
//超时堆栈的具体处理
|
||||||
|
private void examineHang() { |
||||||
|
//获取执行线程的当前堆栈
|
||||||
|
StackTraceElement[] currentStack = eventDispatchThread.getStackTrace(); |
||||||
|
if (isWaitingForNextEvent(currentStack)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
//某个事件执行时间很长,定时处理时可能会连续打很多个堆栈,对同一个事件的相同堆栈只打一次
|
||||||
|
if (lastReportedStack !=null && stacksEqual(lastReportedStack, currentStack)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
String stackTrace = stackTraceToString(currentStack); |
||||||
|
lastReportedStack = currentStack; |
||||||
|
JSONObject jsonObject = new JSONObject(); |
||||||
|
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"), simpleDateFormat.format(System.currentTimeMillis())); |
||||||
|
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"), "eventQueue_" + hangNumber); |
||||||
|
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Duration_Task_Execute"), timeSoFar() + "ms"); |
||||||
|
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Stack_Info"), stackTrace); |
||||||
|
outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.TIMER_CHECK_FLAG); |
||||||
|
checkForDeadlock(); |
||||||
|
} |
||||||
|
//记录连续运行了多长时间
|
||||||
|
private long timeSoFar() { |
||||||
|
return (System.currentTimeMillis() - lastDispatchTimeMillis); |
||||||
|
} |
||||||
|
//记录一个事件从被分发到结束的总运行时间
|
||||||
|
private long totalTime() { |
||||||
|
return (System.currentTimeMillis() - startDispatchTimeMillis); |
||||||
|
} |
||||||
|
//事件处理完后的时间判断
|
||||||
|
public void dispose() { |
||||||
|
if (timeSoFar() > UNREASONABLE_DISPATCH_DURATION_MS) { |
||||||
|
exportCartonLog(true); |
||||||
|
} else if (lastReportedStack != null){ |
||||||
|
exportCartonLog(false); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* @param flag 判断一下输出日志时要输出哪个时间 |
||||||
|
*/ |
||||||
|
private void exportCartonLog(boolean flag) { |
||||||
|
JSONObject jsonObject = new JSONObject(); |
||||||
|
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"), simpleDateFormat.format(System.currentTimeMillis())); |
||||||
|
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"), "eventQueue_" + hangNumber); |
||||||
|
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Start_Time"), simpleDateFormat.format(startDispatchTimeMillis)); |
||||||
|
if (flag) { |
||||||
|
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Total_Time"), timeSoFar() + "ms"); |
||||||
|
} else { |
||||||
|
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Total_Time"), totalTime() + "ms"); |
||||||
|
} |
||||||
|
outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.EASY_CHECK_FLAG); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static void outPutJournalLog(String message, int flag) { |
||||||
|
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); |
||||||
|
String date = simpleDateFormat.format(System.currentTimeMillis()); |
||||||
|
String filename = flag == SwitchForSwingChecker.EASY_CHECK_FLAG ? SwitchForSwingChecker.EASY_CHECKER_FILE_NAME: SwitchForSwingChecker.TIMER_CHECKER_FILE_NAME; |
||||||
|
String[] split = date.split("-"); |
||||||
|
int month = StringUtils.isEmpty(split[1]) ? -1 : Integer.parseInt(split[1]); |
||||||
|
String dirPath = StableUtils.pathJoin(JOURNAL_FILE_PATH, split[0], "month-" + month, date); |
||||||
|
File dirFile = new File(dirPath); |
||||||
|
File file = new File( StableUtils.pathJoin(dirPath, filename)); |
||||||
|
try { |
||||||
|
if (!file.exists()) { |
||||||
|
if (!dirFile.exists()) { |
||||||
|
dirFile.mkdirs(); |
||||||
|
} |
||||||
|
file.createNewFile(); |
||||||
|
} |
||||||
|
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file, true)); |
||||||
|
String outputMessage = new StringBuilder(message.replaceAll("~", "\r\n")).append(",").append("\r\n").toString(); |
||||||
|
bufferedWriter.write(outputMessage); |
||||||
|
bufferedWriter.close(); |
||||||
|
} catch (IOException e) { |
||||||
|
FineLoggerFactory.getLogger().error("output fail", e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private EventDispatchThreadHangMonitor() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 参考SwingExplorer,在处理模态框时没有做特殊处理,也不会输出卡顿堆栈 |
||||||
|
* 原因是SwingExplorer窗口一直有一个监听事件,不断的add,remove。 |
||||||
|
* 由于卡顿日志输出的是事件连续执行的时间,所以一个长时间存在的模态框被不断重复的监听事件刷新时间,就不会输出了。 |
||||||
|
* 当检测开关打开后,在这里模拟一下监听事件,给个不耗时的任务就可以。 |
||||||
|
*/ |
||||||
|
public void startFilterModalWindow() { |
||||||
|
scheduledExecutorService = FineExecutors.newSingleThreadScheduledExecutor(); |
||||||
|
scheduledExecutorService.scheduleAtFixedRate(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
SwingUtilities.invokeLater(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
//不用干事,切个片就可以
|
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
}, 0, 500, TimeUnit.MILLISECONDS); |
||||||
|
} |
||||||
|
|
||||||
|
public void stopFilterModalWindow() { |
||||||
|
if (scheduledExecutorService != null) { |
||||||
|
scheduledExecutorService.shutdown(); |
||||||
|
} |
||||||
|
} |
||||||
|
/** |
||||||
|
* Sets up a timer to check for hangs frequently. |
||||||
|
* 初始化一个Timer |
||||||
|
*/ |
||||||
|
public void initTimer() { |
||||||
|
final long initialDelayMs = 0; |
||||||
|
final boolean daemon = true; |
||||||
|
timer = new Timer("EventDispatchThreadHangMonitor", daemon); |
||||||
|
timer.schedule(new HangChecker(), initialDelayMs, CHECK_INTERVAL_MS); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* /消除Timer |
||||||
|
*/ |
||||||
|
public void stopTimer() { |
||||||
|
if (timer != null) { |
||||||
|
timer.cancel(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* /定时执行的任务 |
||||||
|
*/ |
||||||
|
public class HangChecker extends TimerTask { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
synchronized (dispatches) { |
||||||
|
//如果链表为空或者窗口还没启开,定时检测就不进行
|
||||||
|
if (dispatches.isEmpty() || !haveShownSomeComponent) { |
||||||
|
return; |
||||||
|
} |
||||||
|
dispatches.getLast().checkForHang(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 将swing中默认的EventQueue换成自己的 |
||||||
|
*/ |
||||||
|
public static void initMonitoring() { |
||||||
|
Toolkit.getDefaultToolkit().getSystemEventQueue().push(INSTANCE); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Overrides EventQueue.dispatchEvent to call our pre and post hooks either |
||||||
|
* side of the system's event dispatch code. |
||||||
|
* 重写 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
protected void dispatchEvent(AWTEvent event) { |
||||||
|
//如果两个开关都没开,那就不走重写方法了
|
||||||
|
if (!isEasyWitch() && !isTimerWitch()) { |
||||||
|
super.dispatchEvent(event); |
||||||
|
} else { |
||||||
|
try { |
||||||
|
preDispatchEvent(); |
||||||
|
super.dispatchEvent(event); |
||||||
|
} finally { |
||||||
|
postDispatchEvent(); |
||||||
|
if (!haveShownSomeComponent && |
||||||
|
event instanceof WindowEvent && event.getID() == WindowEvent.WINDOW_OPENED) { |
||||||
|
haveShownSomeComponent = true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Starts tracking a dispatch. |
||||||
|
*/ |
||||||
|
private synchronized void preDispatchEvent() { |
||||||
|
synchronized (dispatches) { |
||||||
|
dispatches.addLast(new DispatchInfo()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Stops tracking a dispatch. |
||||||
|
*/ |
||||||
|
private synchronized void postDispatchEvent() { |
||||||
|
synchronized (dispatches) { |
||||||
|
DispatchInfo justFinishedDispatch = dispatches.removeLast(); |
||||||
|
if (isEasyWitch()) { |
||||||
|
justFinishedDispatch.dispose(); |
||||||
|
} |
||||||
|
//嵌套最深的事件执行完毕后刷新链表中其他事件的lastDispatchTimeMillis
|
||||||
|
Thread currentEventDispatchThread = Thread.currentThread(); |
||||||
|
for (DispatchInfo dispatchInfo : dispatches) { |
||||||
|
if (dispatchInfo.eventDispatchThread == currentEventDispatchThread) { |
||||||
|
dispatchInfo.lastDispatchTimeMillis = System.currentTimeMillis(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 检查死锁 |
||||||
|
*/ |
||||||
|
public static void checkForDeadlock() { |
||||||
|
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); |
||||||
|
long[] threadIds = threadBean.findDeadlockedThreads(); |
||||||
|
if (threadIds == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
FineLoggerFactory.getLogger().warn("deadlock detected involving the following threads:"); |
||||||
|
ThreadInfo[] threadInfos = threadBean.getThreadInfo(threadIds, Integer.MAX_VALUE); |
||||||
|
for (ThreadInfo info : threadInfos) { |
||||||
|
FineLoggerFactory.getLogger().warn("Thread # {} {} ( {} ) waiting on {} held by {} {}", info.getThreadId(), info.getThreadName(), |
||||||
|
info.getThreadState(), info.getLockName(), info.getLockOwnerName(), stackTraceToStringForConsole(info.getStackTrace())); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static String stackTraceToString(StackTraceElement[] stackTrace) { |
||||||
|
StringBuilder result = new StringBuilder(); |
||||||
|
for (StackTraceElement stackTraceElement : stackTrace) { |
||||||
|
String indentation = " "; |
||||||
|
result.append("~").append(indentation).append(stackTraceElement); |
||||||
|
} |
||||||
|
return result.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
public static String stackTraceToStringForConsole(StackTraceElement[] stackTrace) { |
||||||
|
StringBuilder result = new StringBuilder(); |
||||||
|
for (StackTraceElement stackTraceElement : stackTrace) { |
||||||
|
String indentation = " "; |
||||||
|
result.append("\r\n").append(indentation).append(stackTraceElement); |
||||||
|
} |
||||||
|
return result.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,407 @@ |
|||||||
|
package com.fr.design.carton; |
||||||
|
|
||||||
|
import com.fr.decision.webservice.v10.log.download.utils.LogZipUtils; |
||||||
|
import com.fr.design.DesignerEnvManager; |
||||||
|
import com.fr.design.constants.UIConstants; |
||||||
|
import com.fr.design.dialog.FineJOptionPane; |
||||||
|
import com.fr.design.env.DesignerWorkspaceInfo; |
||||||
|
import com.fr.design.gui.date.UIDatePicker; |
||||||
|
import com.fr.design.gui.ibutton.UIButton; |
||||||
|
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.mainframe.DesignerContext; |
||||||
|
import com.fr.design.utils.gui.GUICoreUtils; |
||||||
|
import com.fr.env.detect.ui.EnvDetectorDialog; |
||||||
|
import com.fr.file.FILE; |
||||||
|
import com.fr.file.FILEChooserPane; |
||||||
|
import com.fr.file.FILEFactory; |
||||||
|
import com.fr.file.filter.ChooseFileFilter; |
||||||
|
import com.fr.general.GeneralUtils; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.stable.StableUtils; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
import org.jetbrains.annotations.Nullable; |
||||||
|
import com.fr.workspace.WorkContext; |
||||||
|
|
||||||
|
|
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.JDialog; |
||||||
|
import javax.swing.JOptionPane; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.UIManager; |
||||||
|
import javax.swing.filechooser.FileSystemView; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Component; |
||||||
|
import java.awt.Cursor; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.FlowLayout; |
||||||
|
import java.awt.Frame; |
||||||
|
import java.awt.event.MouseAdapter; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
import java.awt.event.WindowEvent; |
||||||
|
import java.io.ByteArrayOutputStream; |
||||||
|
import java.io.File; |
||||||
|
import java.io.FileInputStream; |
||||||
|
import java.text.ParseException; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Objects; |
||||||
|
|
||||||
|
|
||||||
|
public class FeedbackToolboxDialog extends JDialog { |
||||||
|
private UIDatePicker uiDatePicker; |
||||||
|
private JPanel generalSettingPanel = null; |
||||||
|
private UICheckBox easyCheckerButton = null; |
||||||
|
private UICheckBox timerCheckerButton = null; |
||||||
|
private UIButton uploadButton = null; |
||||||
|
private UILabel exportLogLabel = null; |
||||||
|
private final Color backgroundColor = new Color(240, 240, 243, 1); |
||||||
|
private final Color lineColor = new Color(192, 192, 192, 120); |
||||||
|
private JPanel body = null; |
||||||
|
private static final String WORK_SPACE_PATH = "reportlets"; |
||||||
|
private static final int BUFFER_SIZE = 2 * 1024; |
||||||
|
|
||||||
|
public FeedbackToolboxDialog(Frame owner) { |
||||||
|
super(owner, Toolkit.i18nText("Fine-Design_Basic_Carton_Feedback_ToolBox")); |
||||||
|
setResizable(false); |
||||||
|
this.setLayout(FRGUIPaneFactory.createBorderLayout()); |
||||||
|
createBodyPanel(); |
||||||
|
add(body); |
||||||
|
setSize(body.getPreferredSize()); |
||||||
|
setSwitches(!StringUtils.isEmpty(GeneralUtils.objectToString(uiDatePicker.getSelectedItem()))); |
||||||
|
repaint(); |
||||||
|
GUICoreUtils.centerWindow(this); |
||||||
|
} |
||||||
|
|
||||||
|
public void createBodyPanel() { |
||||||
|
JPanel body = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||||
|
body.setBackground(backgroundColor); |
||||||
|
JPanel titlePane = createTitlePane(); |
||||||
|
JPanel tailPane = createTailPane(); |
||||||
|
JPanel midPane = createMidPane(); |
||||||
|
JPanel infoPane = createInfoPane(); |
||||||
|
body.add(titlePane, BorderLayout.NORTH); |
||||||
|
body.add(tailPane, BorderLayout.SOUTH); |
||||||
|
body.add(midPane, BorderLayout.CENTER); |
||||||
|
midPane.add(infoPane, BorderLayout.NORTH); |
||||||
|
Dimension dimension = new Dimension(662, 556); |
||||||
|
body.setPreferredSize(dimension); |
||||||
|
this.body = body; |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createInfoPane() { |
||||||
|
JPanel northPane = FRGUIPaneFactory.createNColumnGridInnerContainer_Pane(2, 10, 10); |
||||||
|
UILabel title = new UILabel(); |
||||||
|
//空格布局会好看一点
|
||||||
|
title.setText(" " + Toolkit.i18nText("Fine-Design_Basic_Carton_Record_Lag_Time") + ": "); |
||||||
|
//判断一下当天是否有卡顿日志记录,如果有将日期设置为当天,如果没有设置为空
|
||||||
|
boolean cartonExists = SwitchForSwingChecker.isCartonExists(); |
||||||
|
if (cartonExists) { |
||||||
|
this.uiDatePicker = new UIDatePicker(UIDatePicker.STYLE_CN_DATE1, this); |
||||||
|
} else { |
||||||
|
this.uiDatePicker = new UIDatePicker(UIDatePicker.STYLE_CN_DATE1, null, this); |
||||||
|
} |
||||||
|
Dimension dimension = new Dimension(160, 100); |
||||||
|
uiDatePicker.setPreferredSize(dimension); |
||||||
|
northPane.add(GUICoreUtils.createFlowPane(new Component[]{title, uiDatePicker}, FlowLayout.LEFT)); |
||||||
|
exportLogLabel = new UILabel(); |
||||||
|
exportLogLabel.setText(Toolkit.i18nText("Fine-Design_Basic_Carton_Export_Carton_Log")); |
||||||
|
exportLogLabel.setForeground(UIConstants.FLESH_BLUE); |
||||||
|
exportLogLabel.addMouseListener(new MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mouseClicked(MouseEvent e) { |
||||||
|
if (exportLogLabel.isEnabled()) { |
||||||
|
exportLogFile(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void mouseEntered(MouseEvent evt) { |
||||||
|
Object source = evt.getSource(); |
||||||
|
if (source instanceof UILabel) { |
||||||
|
((UILabel) source).setCursor(new Cursor(Cursor.HAND_CURSOR)); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
northPane.add(GUICoreUtils.createFlowPane(exportLogLabel, FlowLayout.RIGHT)); |
||||||
|
return northPane; |
||||||
|
} |
||||||
|
|
||||||
|
private void exportLogFile() { |
||||||
|
String selectDate = GeneralUtils.objectToString(uiDatePicker.getSelectedItem()); |
||||||
|
FILEChooserPane fileChooserPane = FILEChooserPane.getInstance(); |
||||||
|
StringBuilder fileName = new StringBuilder(); |
||||||
|
fileName.append(selectDate).append(Toolkit.i18nText("Fine-Design_Basic_Carton_Carton_Log")); |
||||||
|
fileChooserPane.setFileNameTextField(fileName.toString(), " "); |
||||||
|
fileChooserPane.removeAllFilter(); |
||||||
|
fileChooserPane.addChooseFILEFilter(new ChooseFileFilter("zip", Toolkit.i18nText("Fine-Design_Basic_Carton_Compile_File"))); |
||||||
|
//默认选择桌面
|
||||||
|
FILE desktop = FILEFactory.createFILE(FILEFactory.FILE_PREFIX + FileSystemView.getFileSystemView().getHomeDirectory().getPath()); |
||||||
|
fileChooserPane.setCurrentDirectory(desktop); |
||||||
|
int chooseResult = fileChooserPane.showSaveDialog(DesignerContext.getDesignerFrame()); |
||||||
|
if (chooseResult == 0) { |
||||||
|
FILE selectedFile = fileChooserPane.getSelectedFILE(); |
||||||
|
String path = selectedFile.getPath(); |
||||||
|
//selectDate 2002-03-09例子
|
||||||
|
String[] split = selectDate.split("-"); |
||||||
|
int month = Integer.parseInt(split[1]); |
||||||
|
String sourceFilePath = StableUtils.pathJoin(SwitchForSwingChecker.JOURNAL_FILE_PATH, split[0], "month-" + month, selectDate); |
||||||
|
File sourceFile = new File(sourceFilePath); |
||||||
|
if (sourceFile.exists()) { |
||||||
|
exportCartonLog(sourceFile, path, sourceFilePath); |
||||||
|
} |
||||||
|
fileChooserPane.removeAllFilter(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createTailPane() { |
||||||
|
JPanel tailPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
tailPanel.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, lineColor)); |
||||||
|
JPanel actionsPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
actionsPanel.setLayout(FRGUIPaneFactory.createM_BorderLayout()); |
||||||
|
{ |
||||||
|
uploadButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Carton_Upload_Carton_Log")); |
||||||
|
uploadButton.addActionListener((e) -> { |
||||||
|
try { |
||||||
|
List<CartonUploadMessage> list = SwitchForSwingChecker.uploadJournalLog(uiDatePicker.getSelectedDate()); |
||||||
|
if (list.isEmpty()) { |
||||||
|
FineJOptionPane.showMessageDialog(null, Toolkit.i18nText("Fine_Design_Basic_Upload_Fail"), UIManager.getString("OptionPane.messageDialogTitle"), JOptionPane.ERROR_MESSAGE); |
||||||
|
} else { |
||||||
|
FineJOptionPane.showMessageDialog(null, Toolkit.i18nText("Fine-Design_Basic_Upload_Success")); |
||||||
|
} |
||||||
|
} catch (ParseException parseException) { |
||||||
|
FineLoggerFactory.getLogger().error("parse error", parseException); |
||||||
|
} |
||||||
|
|
||||||
|
}); |
||||||
|
actionsPanel.add(uploadButton, BorderLayout.WEST); |
||||||
|
|
||||||
|
UIButton cancelButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Cancel")); |
||||||
|
cancelButton.addActionListener((e) -> { |
||||||
|
setVisible(false); |
||||||
|
EnvDetectorDialog envDetectorDialog = new EnvDetectorDialog(DesignerContext.getDesignerFrame()); |
||||||
|
envDetectorDialog.setVisible(true); |
||||||
|
dispose(); |
||||||
|
}); |
||||||
|
actionsPanel.add(cancelButton, BorderLayout.EAST); |
||||||
|
} |
||||||
|
UIButton currencySetButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Carton_General_Settings")); |
||||||
|
currencySetButton.addActionListener((e -> { |
||||||
|
createGeneralSettingPanel(); |
||||||
|
this.remove(body); |
||||||
|
this.add(generalSettingPanel); |
||||||
|
setSize(generalSettingPanel.getPreferredSize()); |
||||||
|
repaint(); |
||||||
|
setVisible(true); |
||||||
|
})); |
||||||
|
tailPanel.add(actionsPanel, BorderLayout.EAST); |
||||||
|
tailPanel.add(currencySetButton, BorderLayout.WEST); |
||||||
|
return tailPanel; |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createTitlePane() { |
||||||
|
JPanel titlePane = FRGUIPaneFactory.createBorderLayout_M_Pane(); |
||||||
|
titlePane.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, lineColor)); |
||||||
|
UILabel uiLabel = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Carton_Carton_Journal_Record")); |
||||||
|
uiLabel.setForeground(UIConstants.FLESH_BLUE); |
||||||
|
titlePane.add(uiLabel, BorderLayout.WEST); |
||||||
|
return titlePane; |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createMidPane() { |
||||||
|
JPanel midPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||||
|
return midPanel; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 下面是通用设置的面板 |
||||||
|
*/ |
||||||
|
private void createGeneralSettingPanel() { |
||||||
|
JPanel generalSettingPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||||
|
JPanel tailPaneInGeneralSettings = createTailPaneInGeneralSettings(); |
||||||
|
generalSettingPanel.add(tailPaneInGeneralSettings, BorderLayout.SOUTH); |
||||||
|
|
||||||
|
JPanel titlePaneInGeneralSettings = createTitlePaneInGeneralSettings(); |
||||||
|
generalSettingPanel.add(titlePaneInGeneralSettings, BorderLayout.NORTH); |
||||||
|
|
||||||
|
JPanel midPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||||
|
generalSettingPanel.add(midPanel, BorderLayout.CENTER); |
||||||
|
|
||||||
|
JPanel infoPane = createInfoPanelInGeneralSettings(); |
||||||
|
midPanel.add(infoPane, BorderLayout.NORTH); |
||||||
|
|
||||||
|
Dimension dimension = new Dimension(662, 556); |
||||||
|
generalSettingPanel.setPreferredSize(dimension); |
||||||
|
generalSettingPanel.setBackground(backgroundColor); |
||||||
|
this.generalSettingPanel = generalSettingPanel; |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createTitlePaneInGeneralSettings() { |
||||||
|
JPanel titlePane = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||||
|
titlePane.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, lineColor)); |
||||||
|
UILabel uiLabel = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Carton_Carton_Journal_Record") + "/"); |
||||||
|
uiLabel.addMouseListener(new MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mouseClicked(MouseEvent e) { |
||||||
|
createBodyPanel(); |
||||||
|
remove(generalSettingPanel); |
||||||
|
add(body); |
||||||
|
setPreferredSize(body.getPreferredSize()); |
||||||
|
setSwitches(!StringUtils.isEmpty(GeneralUtils.objectToString(uiDatePicker.getSelectedItem()))); |
||||||
|
repaint(); |
||||||
|
setVisible(true); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void mouseEntered(MouseEvent evt) { |
||||||
|
Object source = evt.getSource(); |
||||||
|
if (source instanceof UILabel) { |
||||||
|
((UILabel) source).setCursor(new Cursor(Cursor.HAND_CURSOR)); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
UILabel uiCurrentLabel = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Carton_General_Settings")); |
||||||
|
uiCurrentLabel.setForeground(UIConstants.FLESH_BLUE); |
||||||
|
titlePane.add(GUICoreUtils.createFlowPane(new Component[]{uiLabel, uiCurrentLabel}, FlowLayout.LEFT)); |
||||||
|
return titlePane; |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createTailPaneInGeneralSettings() { |
||||||
|
JPanel tailPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
tailPanel.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, lineColor)); |
||||||
|
JPanel actionsPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
actionsPanel.setLayout(FRGUIPaneFactory.createM_BorderLayout()); |
||||||
|
{ |
||||||
|
UIButton confirmButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Save")); |
||||||
|
confirmButton.addActionListener((e) -> { |
||||||
|
if (easyCheckerButton.isSelected()) { |
||||||
|
SwitchForSwingChecker.startEasyChecker(); |
||||||
|
} else { |
||||||
|
SwitchForSwingChecker.stopEasyChecker(); |
||||||
|
} |
||||||
|
if (timerCheckerButton.isSelected()) { |
||||||
|
SwitchForSwingChecker.startTimerChecker(); |
||||||
|
} else { |
||||||
|
SwitchForSwingChecker.stopTimerChecker(); |
||||||
|
} |
||||||
|
createBodyPanel(); |
||||||
|
remove(generalSettingPanel); |
||||||
|
add(body); |
||||||
|
setPreferredSize(body.getPreferredSize()); |
||||||
|
setSwitches(!StringUtils.isEmpty(GeneralUtils.objectToString(uiDatePicker.getSelectedItem()))); |
||||||
|
repaint(); |
||||||
|
setVisible(true); |
||||||
|
}); |
||||||
|
actionsPanel.add(confirmButton, BorderLayout.WEST); |
||||||
|
|
||||||
|
UIButton cancelButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Cancel")); |
||||||
|
cancelButton.addActionListener((e) -> { |
||||||
|
createBodyPanel(); |
||||||
|
remove(generalSettingPanel); |
||||||
|
add(body); |
||||||
|
setPreferredSize(body.getPreferredSize()); |
||||||
|
repaint(); |
||||||
|
setVisible(true); |
||||||
|
|
||||||
|
}); |
||||||
|
actionsPanel.add(cancelButton, BorderLayout.EAST); |
||||||
|
} |
||||||
|
tailPanel.add(actionsPanel, BorderLayout.EAST); |
||||||
|
return tailPanel; |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createInfoPanelInGeneralSettings() { |
||||||
|
JPanel infoPane = FRGUIPaneFactory.createNColumnGridInnerContainer_S_Pane(1); |
||||||
|
easyCheckerButton = new UICheckBox(Toolkit.i18nText("Fine-Design_Basic_Carton_Operation_Time_Consuming_Detection")); |
||||||
|
timerCheckerButton = new UICheckBox(Toolkit.i18nText("Fine-Design_Basic_Carton_Carton_Operation_Class_Capture")); |
||||||
|
easyCheckerButton.setSelected(SwitchForSwingChecker.isEasyChecker()); |
||||||
|
timerCheckerButton.setSelected(SwitchForSwingChecker.isCheckerTimerSwitch()); |
||||||
|
infoPane.add(GUICoreUtils.createFlowPane(easyCheckerButton, FlowLayout.LEFT)); |
||||||
|
infoPane.add(GUICoreUtils.createFlowPane(timerCheckerButton, FlowLayout.LEFT)); |
||||||
|
return infoPane; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void processWindowEvent(WindowEvent e) { |
||||||
|
super.processWindowEvent(e); |
||||||
|
if (e.getID() == WindowEvent.WINDOW_CLOSING) { |
||||||
|
EnvDetectorDialog envDetectorDialog = new EnvDetectorDialog(DesignerContext.getDesignerFrame()); |
||||||
|
envDetectorDialog.setVisible(true); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 上传和导出卡顿日志的可用化处理,如果没有选择日期就不可用 |
||||||
|
*/ |
||||||
|
public void setSwitches(boolean flag) { |
||||||
|
uploadButton.setEnabled(flag); |
||||||
|
exportLogLabel.setEnabled(flag); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 导出卡顿日志到本地或远程服务器WEB-INF下 |
||||||
|
* |
||||||
|
* @param sourceFile 导出的卡顿日志所在文件夹 |
||||||
|
* @param path 文件需要导出到的路径 |
||||||
|
* @param sourceFilePath 导出的卡顿日志所在文件夹的路径 |
||||||
|
*/ |
||||||
|
private void exportCartonLog(File sourceFile, String path, String sourceFilePath) { |
||||||
|
File[] files = sourceFile.listFiles(); |
||||||
|
if (!Objects.isNull(files)) { |
||||||
|
try { |
||||||
|
if (path.startsWith(WORK_SPACE_PATH)) { |
||||||
|
if (WorkContext.getCurrent().isLocal()) { |
||||||
|
String curEnvName = DesignerEnvManager.getEnvManager().getCurEnvName(); |
||||||
|
DesignerWorkspaceInfo workspaceInfo = DesignerEnvManager.getEnvManager().getWorkspaceInfo(curEnvName); |
||||||
|
String workspaceInfoPath = workspaceInfo.getPath(); |
||||||
|
path = StableUtils.pathJoin(workspaceInfoPath, path); |
||||||
|
LogZipUtils.compress(files, path, false); |
||||||
|
} else { |
||||||
|
String sourceFilePathZip = sourceFilePath + ".zip"; |
||||||
|
LogZipUtils.compress(files, sourceFilePathZip, false); |
||||||
|
byte[] bytesByFile = getBytesByFile(sourceFilePathZip); |
||||||
|
WorkContext.getWorkResource().write(path, bytesByFile); |
||||||
|
LogZipUtils.delDir(sourceFilePathZip); |
||||||
|
} |
||||||
|
} else { |
||||||
|
LogZipUtils.compress(files, path, false); |
||||||
|
} |
||||||
|
FineJOptionPane.showMessageDialog(this, Toolkit.i18nText("Fine-Design_Report_Exported_Successfully")); |
||||||
|
} catch (Exception exception) { |
||||||
|
FineJOptionPane.showMessageDialog(this, Toolkit.i18nText("Fine-Design_Report_Export_Failed"), UIManager.getString("OptionPane.messageDialogTitle"), JOptionPane.ERROR_MESSAGE); |
||||||
|
FineLoggerFactory.getLogger().error("export file fail", exception); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 根据文件地址将文件转换成byte[] |
||||||
|
* |
||||||
|
* @param pathStr 本地文件目录 |
||||||
|
* @return 本地文件转成的byte[] |
||||||
|
*/ |
||||||
|
@Nullable |
||||||
|
private static byte[] getBytesByFile(String pathStr) { |
||||||
|
File file = new File(pathStr); |
||||||
|
try { |
||||||
|
FileInputStream fis = new FileInputStream(file); |
||||||
|
ByteArrayOutputStream bos = new ByteArrayOutputStream(BUFFER_SIZE); |
||||||
|
byte[] b = new byte[BUFFER_SIZE]; |
||||||
|
int n; |
||||||
|
while ((n = fis.read(b)) != -1) { |
||||||
|
bos.write(b, 0, n); |
||||||
|
} |
||||||
|
fis.close(); |
||||||
|
byte[] data = bos.toByteArray(); |
||||||
|
bos.close(); |
||||||
|
return data; |
||||||
|
} catch (Exception e) { |
||||||
|
FineLoggerFactory.getLogger().error("reading local file fail", e); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
package com.fr.design.carton; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
|
||||||
|
public class MonthlyCartonFile { |
||||||
|
private File currentMonthFile; |
||||||
|
private File lastMonthFile; |
||||||
|
private File nextMonthFile; |
||||||
|
public MonthlyCartonFile() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public File getCurrentMonthFile() { |
||||||
|
return currentMonthFile; |
||||||
|
} |
||||||
|
|
||||||
|
public void setCurrentMonthFile(File currentMonthFile) { |
||||||
|
this.currentMonthFile = currentMonthFile; |
||||||
|
} |
||||||
|
|
||||||
|
public File getLastMonthFile() { |
||||||
|
return lastMonthFile; |
||||||
|
} |
||||||
|
|
||||||
|
public void setLastMonthFile(File lastMonthFile) { |
||||||
|
this.lastMonthFile = lastMonthFile; |
||||||
|
} |
||||||
|
|
||||||
|
public File getNextMonthFile() { |
||||||
|
return nextMonthFile; |
||||||
|
} |
||||||
|
|
||||||
|
public void setNextMonthFile(File nextMonthFile) { |
||||||
|
this.nextMonthFile = nextMonthFile; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,312 @@ |
|||||||
|
package com.fr.design.carton; |
||||||
|
|
||||||
|
|
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.general.GeneralUtils; |
||||||
|
import com.fr.json.JSON; |
||||||
|
import com.fr.json.JSONArray; |
||||||
|
import com.fr.json.JSONObject; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.stable.ProductConstantsBase; |
||||||
|
import com.fr.stable.StableUtils; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
import com.fr.stable.xml.XMLPrintWriter; |
||||||
|
import com.fr.stable.xml.XMLReadable; |
||||||
|
import com.fr.stable.xml.XMLWriter; |
||||||
|
import com.fr.stable.xml.XMLableReader; |
||||||
|
import sun.awt.AppContext; |
||||||
|
|
||||||
|
import javax.swing.SwingWorker; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.File; |
||||||
|
import java.io.BufferedReader; |
||||||
|
import java.io.FileReader; |
||||||
|
import java.text.SimpleDateFormat; |
||||||
|
import java.util.List; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Date; |
||||||
|
import java.util.Calendar; |
||||||
|
|
||||||
|
public class SwitchForSwingChecker implements XMLReadable, XMLWriter { |
||||||
|
/** |
||||||
|
* Designer4Debug类名 |
||||||
|
*/ |
||||||
|
private static final String DEBUG_MAIN_CLASS_NAME = "com.fr.start.Designer4Debug"; |
||||||
|
/** |
||||||
|
* XML标签 |
||||||
|
*/ |
||||||
|
public static final String XML_TAG = "SwitchForSwingChecker"; |
||||||
|
/** |
||||||
|
* 定时任务的开关 |
||||||
|
*/ |
||||||
|
private static boolean checkerTimerSwitch = false; |
||||||
|
/** |
||||||
|
* 简单记录事件执行时间的开关 |
||||||
|
*/ |
||||||
|
private static boolean easyChecker = false; |
||||||
|
/** |
||||||
|
* 一个标识位用于区分耗时任务时长检测(简单检测)和timer检测 |
||||||
|
*/ |
||||||
|
public static final int TIMER_CHECK_FLAG = 0; |
||||||
|
public static final int EASY_CHECK_FLAG = 1; |
||||||
|
|
||||||
|
/** |
||||||
|
* 日志存储地址 |
||||||
|
*/ |
||||||
|
public static final String JOURNAL_FILE_PATH = StableUtils.pathJoin(ProductConstantsBase.getEnvHome(), "journal_log"); |
||||||
|
public static final String EASY_CHECKER_FILE_NAME = "easy_check_log.csv"; |
||||||
|
public static final String TIMER_CHECKER_FILE_NAME = "timer_check_log.csv"; |
||||||
|
public static boolean isCheckerTimerSwitch() { |
||||||
|
return checkerTimerSwitch; |
||||||
|
} |
||||||
|
|
||||||
|
public static boolean isEasyChecker() { |
||||||
|
return easyChecker; |
||||||
|
} |
||||||
|
|
||||||
|
public static volatile SwitchForSwingChecker switchForSwingChecker = new SwitchForSwingChecker(); |
||||||
|
|
||||||
|
public static SwitchForSwingChecker getInstance() { |
||||||
|
return switchForSwingChecker; |
||||||
|
} |
||||||
|
|
||||||
|
public static void startTimerChecker() { |
||||||
|
if (!checkerTimerSwitch) { |
||||||
|
EventDispatchThreadHangMonitor.INSTANCE.initTimer(); |
||||||
|
CartonThreadExecutorPool.getTimerThreadExecutorPool().initTimer(); |
||||||
|
EventDispatchThreadHangMonitor.INSTANCE.setTimerWitch(true); |
||||||
|
checkerTimerSwitch = true; |
||||||
|
if (!easyChecker) { |
||||||
|
EventDispatchThreadHangMonitor.INSTANCE.startFilterModalWindow(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static void stopTimerChecker() { |
||||||
|
if (checkerTimerSwitch) { |
||||||
|
EventDispatchThreadHangMonitor.INSTANCE.stopTimer(); |
||||||
|
CartonThreadExecutorPool.getTimerThreadExecutorPool().stopTimer(); |
||||||
|
EventDispatchThreadHangMonitor.INSTANCE.setTimerWitch(false); |
||||||
|
checkerTimerSwitch = false; |
||||||
|
if (!easyChecker) { |
||||||
|
EventDispatchThreadHangMonitor.INSTANCE.stopFilterModalWindow(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static void startEasyChecker() { |
||||||
|
if (!easyChecker) { |
||||||
|
EventDispatchThreadHangMonitor.INSTANCE.setEasyWitch(true); |
||||||
|
CartonThreadExecutorPool.getTimerThreadExecutorPool().setEasyWitch(true); |
||||||
|
easyChecker = true; |
||||||
|
if (!checkerTimerSwitch) { |
||||||
|
EventDispatchThreadHangMonitor.INSTANCE.startFilterModalWindow(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static void stopEasyChecker() { |
||||||
|
if (easyChecker) { |
||||||
|
EventDispatchThreadHangMonitor.INSTANCE.setEasyWitch(false); |
||||||
|
CartonThreadExecutorPool.getTimerThreadExecutorPool().setEasyWitch(false); |
||||||
|
easyChecker = false; |
||||||
|
if (!checkerTimerSwitch) { |
||||||
|
EventDispatchThreadHangMonitor.INSTANCE.stopFilterModalWindow(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取文件名字以及判断文件是否存在 |
||||||
|
*/ |
||||||
|
private static CartonFiles getFiles(String date) { |
||||||
|
String[] split = date.split("-"); |
||||||
|
int month = StringUtils.isEmpty(split[1]) ? -1 : Integer.parseInt(split[1]); |
||||||
|
String dirPath = StableUtils.pathJoin(JOURNAL_FILE_PATH, split[0], "month-" + month, date); |
||||||
|
File file1 = new File(StableUtils.pathJoin(dirPath, EASY_CHECKER_FILE_NAME)); |
||||||
|
File file2 = new File(StableUtils.pathJoin(dirPath, TIMER_CHECKER_FILE_NAME)); |
||||||
|
File[] files = new File[2]; |
||||||
|
files[0] = file1; |
||||||
|
files[1] = file2; |
||||||
|
CartonFiles cartonFiles = new CartonFiles(); |
||||||
|
cartonFiles.setEasyCheckerFile(file1); |
||||||
|
cartonFiles.setTimerCheckerFile(file2); |
||||||
|
return cartonFiles; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
*处理文件 |
||||||
|
* 一共四种情况, |
||||||
|
* 两个文件都不存在 |
||||||
|
* 文件一存在,文件二不存在 |
||||||
|
* 文件二存在,文件一不存在 |
||||||
|
* 两个文件都存在 |
||||||
|
*/ |
||||||
|
private static List<CartonUploadMessage> getCartonLog(File easyFile, File timerFile) { |
||||||
|
List<CartonUploadMessage> res = new ArrayList<>(); |
||||||
|
List<CartonUploadMessage> easyFileCartonLog = getEasyFileCartonLog(easyFile); |
||||||
|
List<CartonUploadMessage> timerFileCartonLog = getTimerFileCartonLog(timerFile); |
||||||
|
Map<String, CartonUploadMessage> easyFileMap = new HashMap<>(); |
||||||
|
for (CartonUploadMessage cartonUploadMessage : easyFileCartonLog) { |
||||||
|
easyFileMap.put(cartonUploadMessage.getHangCount(), cartonUploadMessage); |
||||||
|
res.add(cartonUploadMessage); |
||||||
|
} |
||||||
|
for (CartonUploadMessage cartonUploadMessage : timerFileCartonLog) { |
||||||
|
String hangCount = cartonUploadMessage.getHangCount(); |
||||||
|
if (easyFileMap.containsKey(hangCount)) { |
||||||
|
cartonUploadMessage.setThreadTime(easyFileMap.get(hangCount).getThreadTime()); |
||||||
|
} |
||||||
|
res.add(cartonUploadMessage); |
||||||
|
} |
||||||
|
return res; |
||||||
|
} |
||||||
|
|
||||||
|
private static List<CartonUploadMessage> getTimerFileCartonLog(File file) { |
||||||
|
List<CartonUploadMessage> res = new ArrayList<>(); |
||||||
|
try { |
||||||
|
StringBuilder stringBuilder = new StringBuilder(); |
||||||
|
stringBuilder.append("["); |
||||||
|
BufferedReader bufferedReader1 = new BufferedReader(new FileReader(file)); |
||||||
|
String line1; |
||||||
|
while ((line1 = bufferedReader1.readLine()) != null) { |
||||||
|
stringBuilder.append(line1); |
||||||
|
} |
||||||
|
bufferedReader1.close(); |
||||||
|
stringBuilder.append("]"); |
||||||
|
JSONArray easyCheckerJSON = JSON.ARRAY.createJSON(GeneralUtils.objectToString(stringBuilder)); |
||||||
|
for (Object jsonObject : easyCheckerJSON) { |
||||||
|
CartonUploadMessage cartonUploadMessage = new CartonUploadMessage(); |
||||||
|
JSONObject x = (JSONObject) jsonObject; |
||||||
|
cartonUploadMessage.setHangCount(x.getString(Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"))); |
||||||
|
cartonUploadMessage.setSlowTime(x.getString(Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"))); |
||||||
|
cartonUploadMessage.setThreadTime("undefined"); |
||||||
|
//这个跟输出到文件中的格式匹配,参考EventDis里的stackTraceToString方法
|
||||||
|
String indentation = " "; |
||||||
|
String logMessage = x.getString(Toolkit.i18nText("Fine-Design_Basic_Carton_Stack_Info")).replaceAll(indentation, "\r\n "); |
||||||
|
cartonUploadMessage.setInfo(logMessage); |
||||||
|
res.add(cartonUploadMessage); |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
FineLoggerFactory.getLogger().error("upload fail", e); |
||||||
|
} |
||||||
|
return res; |
||||||
|
} |
||||||
|
|
||||||
|
private static List<CartonUploadMessage> getEasyFileCartonLog(File file) { |
||||||
|
List<CartonUploadMessage> res = new ArrayList<>(); |
||||||
|
try { |
||||||
|
StringBuilder stringBuilder = new StringBuilder(); |
||||||
|
stringBuilder.append("["); |
||||||
|
BufferedReader bufferedReader1 = new BufferedReader(new FileReader(file)); |
||||||
|
String line1; |
||||||
|
while ((line1 = bufferedReader1.readLine()) != null) { |
||||||
|
stringBuilder.append(line1); |
||||||
|
} |
||||||
|
bufferedReader1.close(); |
||||||
|
stringBuilder.append("]"); |
||||||
|
JSONArray timerCheckerJSON = JSON.ARRAY.createJSON(GeneralUtils.objectToString(stringBuilder)); |
||||||
|
for (Object jsonObject : timerCheckerJSON) { |
||||||
|
JSONObject x = (JSONObject) jsonObject; |
||||||
|
CartonUploadMessage cartonUploadMessage = new CartonUploadMessage(); |
||||||
|
cartonUploadMessage.setHangCount(x.getString(Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"))); |
||||||
|
cartonUploadMessage.setSlowTime(x.getString(Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"))); |
||||||
|
cartonUploadMessage.setThreadTime(x.getString(Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Total_Time"))); |
||||||
|
cartonUploadMessage.setInfo("undefined"); |
||||||
|
res.add(cartonUploadMessage); |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
FineLoggerFactory.getLogger().error("upload fail", e); |
||||||
|
} |
||||||
|
return res; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* /埋点方法上传卡顿信息入口 |
||||||
|
date为 2022-09-08的格式 |
||||||
|
*/ |
||||||
|
public static List<CartonUploadMessage> uploadJournalLog(Date dateTime) { |
||||||
|
List<CartonUploadMessage> res = new ArrayList<>(); |
||||||
|
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); |
||||||
|
CartonFiles files = getFiles(simpleDateFormat.format(dateTime)); |
||||||
|
File easyCheckerFile = files.getEasyCheckerFile(); |
||||||
|
File timerCheckerFile = files.getTimerCheckerFile(); |
||||||
|
if (easyCheckerFile.exists() && timerCheckerFile.exists()) { |
||||||
|
return getCartonLog(easyCheckerFile, timerCheckerFile); |
||||||
|
} else if (easyCheckerFile.exists()) { |
||||||
|
return getEasyFileCartonLog(easyCheckerFile); |
||||||
|
} else if (timerCheckerFile.exists()) { |
||||||
|
return getTimerFileCartonLog(timerCheckerFile); |
||||||
|
} else { |
||||||
|
return res; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 初始化监控任务,主要是替换EventQueue以及SwingWorker执行任务的线程池 |
||||||
|
* |
||||||
|
*/ |
||||||
|
public static void initThreadMonitoring () { |
||||||
|
String mainClass = System.getProperty("sun.java.command"); |
||||||
|
//判断一下,如果是以Designer4Debug启动,就不注册代码,不然会覆盖掉SwingExplorer,导致其无法使用
|
||||||
|
if (!StringUtils.equals(mainClass, DEBUG_MAIN_CLASS_NAME)) { |
||||||
|
EventDispatchThreadHangMonitor.initMonitoring(); |
||||||
|
AppContext.getAppContext().put(SwingWorker.class, CartonThreadExecutorPool.getTimerThreadExecutorPool()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 判断是否有指定日期的卡顿日志,没有就返回false |
||||||
|
*/ |
||||||
|
public static boolean isCartonExists(Date date) { |
||||||
|
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); |
||||||
|
String format = simpleDateFormat.format(date); |
||||||
|
Calendar calendar = Calendar.getInstance(); |
||||||
|
int month = calendar.get(Calendar.MONTH) + 1; |
||||||
|
int year = calendar.get(Calendar.YEAR); |
||||||
|
File file = new File(StableUtils.pathJoin(JOURNAL_FILE_PATH, String.valueOf(year), "month-" + month, format)); |
||||||
|
return file.exists(); |
||||||
|
} |
||||||
|
|
||||||
|
public static boolean isCartonExists() { |
||||||
|
return isCartonExists(new Date()); |
||||||
|
} |
||||||
|
|
||||||
|
private void initSwitchChecker() { |
||||||
|
if (easyChecker) { |
||||||
|
EventDispatchThreadHangMonitor.INSTANCE.setEasyWitch(true); |
||||||
|
CartonThreadExecutorPool.getTimerThreadExecutorPool().setEasyWitch(true); |
||||||
|
} |
||||||
|
if (checkerTimerSwitch) { |
||||||
|
EventDispatchThreadHangMonitor.INSTANCE.initTimer(); |
||||||
|
CartonThreadExecutorPool.getTimerThreadExecutorPool().initTimer(); |
||||||
|
EventDispatchThreadHangMonitor.INSTANCE.setTimerWitch(true); |
||||||
|
} |
||||||
|
if (easyChecker || checkerTimerSwitch) { |
||||||
|
EventDispatchThreadHangMonitor.INSTANCE.startFilterModalWindow(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void readXML(XMLableReader reader) { |
||||||
|
if (reader.isAttr()) { |
||||||
|
checkerTimerSwitch = reader.getAttrAsBoolean("checkerTimerSwitch", false); |
||||||
|
easyChecker = reader.getAttrAsBoolean("easyChecker", false); |
||||||
|
} |
||||||
|
try { |
||||||
|
initSwitchChecker(); |
||||||
|
} catch (Throwable t) { |
||||||
|
FineLoggerFactory.getLogger().error("read checker attr fail", t); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void writeXML(XMLPrintWriter writer) { |
||||||
|
writer.startTAG(XML_TAG); |
||||||
|
writer.attr("checkerTimerSwitch", checkerTimerSwitch); |
||||||
|
writer.attr("easyChecker", easyChecker); |
||||||
|
writer.end(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,143 @@ |
|||||||
|
package com.fr.design.data.tabledata.wrapper; |
||||||
|
|
||||||
|
import com.fr.base.TableData; |
||||||
|
import com.fr.data.MultiResultTableData; |
||||||
|
import com.fr.data.impl.NameDataModel; |
||||||
|
import com.fr.design.data.DesignTableDataManager; |
||||||
|
import com.fr.design.data.datapane.preview.PreviewTablePane; |
||||||
|
import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.workspace.WorkContext; |
||||||
|
|
||||||
|
import javax.swing.Icon; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 多结果数据集-结果包装 |
||||||
|
* |
||||||
|
* @author rinoux |
||||||
|
* @version 11.0 |
||||||
|
* Created by rinoux on 2022/8/12 |
||||||
|
*/ |
||||||
|
public final class MultiResultTableDataNameWrapper implements TableDataWrapper { |
||||||
|
private NameDataModel dataModel; |
||||||
|
private final String name; |
||||||
|
private final MultiResultTableData<?> tableData; |
||||||
|
private List<String> childrenList; |
||||||
|
|
||||||
|
/** |
||||||
|
* @param name 数据集名字 |
||||||
|
* @param tableData 数据集 |
||||||
|
*/ |
||||||
|
public MultiResultTableDataNameWrapper(String name, MultiResultTableData<?> tableData) { |
||||||
|
this.name = name; |
||||||
|
this.tableData = tableData; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 生成子节点 |
||||||
|
* |
||||||
|
* @return 子节点 |
||||||
|
*/ |
||||||
|
public ExpandMutableTreeNode[] load() { |
||||||
|
// 生成多数据集结果子节点
|
||||||
|
List<String> namelist = calculateColumnNameList(); |
||||||
|
ExpandMutableTreeNode[] res = new ExpandMutableTreeNode[namelist.size()]; |
||||||
|
for (int i = 0; i < res.length; i++) { |
||||||
|
res[i] = new ExpandMutableTreeNode(namelist.get(i)); |
||||||
|
} |
||||||
|
|
||||||
|
return res; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getTableDataName() { |
||||||
|
return name; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public TableData getTableData() { |
||||||
|
return tableData; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Icon getIcon() { |
||||||
|
return MultiResultTableDataWrapperHelper.getIcon(this.tableData.getClass()); |
||||||
|
} |
||||||
|
|
||||||
|
private void createResult(boolean needLoadingBar) { |
||||||
|
try { |
||||||
|
// todo 啥意思?
|
||||||
|
dataModel = DesignTableDataManager.createLazyDataModel(tableData, needLoadingBar)[0]; |
||||||
|
} catch (Exception e) { |
||||||
|
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 数据集执行结果返回的所有字段 |
||||||
|
* <p/> |
||||||
|
* TODO:要不要加上Exception呢?个人感觉很有必要 |
||||||
|
* |
||||||
|
* @return 字段 |
||||||
|
*/ |
||||||
|
public List<String> calculateColumnNameList() { |
||||||
|
if (childrenList != null) { |
||||||
|
return childrenList; |
||||||
|
} |
||||||
|
childrenList = new ArrayList<>(); |
||||||
|
if (!WorkContext.getCurrent().isLocal()) { |
||||||
|
try { |
||||||
|
createResult(false); |
||||||
|
childrenList = Arrays.asList(dataModel.getColumnNames()); |
||||||
|
} catch (Exception e) { |
||||||
|
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||||
|
} |
||||||
|
|
||||||
|
} else { |
||||||
|
if (dataModel == null) { |
||||||
|
createResult(false); |
||||||
|
} |
||||||
|
if (dataModel != null) { |
||||||
|
childrenList = Arrays.asList(dataModel.getColumnNames()); |
||||||
|
} |
||||||
|
} |
||||||
|
return childrenList; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 预览数据集 |
||||||
|
*/ |
||||||
|
public void previewData() { |
||||||
|
if (dataModel == null) { |
||||||
|
createResult(true); |
||||||
|
} |
||||||
|
PreviewTablePane.previewDataModel(dataModel); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 预览数据集,带有显示值和实际值的标记结果 |
||||||
|
* |
||||||
|
* @param keyIndex 显示值Index |
||||||
|
* @param valueIndex 实际值index |
||||||
|
*/ |
||||||
|
public void previewData(int keyIndex, int valueIndex) { |
||||||
|
if (dataModel == null) { |
||||||
|
createResult(true); |
||||||
|
} |
||||||
|
PreviewTablePane.previewDataModel(dataModel, keyIndex, valueIndex); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 是否异常 |
||||||
|
* |
||||||
|
* @return 异常返回true |
||||||
|
*/ |
||||||
|
public boolean isUnusual() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,325 @@ |
|||||||
|
package com.fr.design.data.tabledata.wrapper; |
||||||
|
|
||||||
|
import com.fr.base.TableData; |
||||||
|
import com.fr.data.MultiResultTableData; |
||||||
|
import com.fr.data.impl.NameDataModel; |
||||||
|
import com.fr.data.impl.storeproc.StoreProcedure; |
||||||
|
import com.fr.data.operator.DataOperator; |
||||||
|
import com.fr.design.data.DesignTableDataManager; |
||||||
|
import com.fr.design.data.datapane.preview.PreviewTablePane; |
||||||
|
import com.fr.design.dialog.FineJOptionPane; |
||||||
|
import com.fr.design.gui.iprogressbar.AutoProgressBar; |
||||||
|
import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.mainframe.DesignerContext; |
||||||
|
import com.fr.general.ComparatorUtils; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
|
||||||
|
import javax.swing.Icon; |
||||||
|
import javax.swing.JFrame; |
||||||
|
import javax.swing.SwingWorker; |
||||||
|
import java.awt.Component; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.List; |
||||||
|
import java.util.concurrent.CancellationException; |
||||||
|
|
||||||
|
/** |
||||||
|
* 多结果数据集包装 |
||||||
|
* |
||||||
|
* @author rinoux |
||||||
|
* @version 11.0 |
||||||
|
* Created by rinoux on 2022/8/12 |
||||||
|
*/ |
||||||
|
public final class MultiResultTableDataWrapper implements TableDataWrapper { |
||||||
|
public static final int PREVIEW_ALL = 0; |
||||||
|
public static final int PREVIEW_ONE = 1; |
||||||
|
public static AutoProgressBar loadingBar; |
||||||
|
|
||||||
|
private NameDataModel dataModel; |
||||||
|
private final String dataModelName; |
||||||
|
private final String tableDataName; |
||||||
|
private final MultiResultTableData<?> tableData; |
||||||
|
private List<String> columnNameList; |
||||||
|
private AutoProgressBar connectionBar; |
||||||
|
private NameDataModel[] dataModels; |
||||||
|
private SwingWorker<?, ?> worker; |
||||||
|
private int previewModel; |
||||||
|
|
||||||
|
/** |
||||||
|
* @param tableData 数据集 |
||||||
|
* @param tableDataName 数据集名称 |
||||||
|
* @param dataModelName 数据集的一个结果的名称(全限定名称) |
||||||
|
*/ |
||||||
|
public MultiResultTableDataWrapper(MultiResultTableData<?> tableData, String tableDataName, String dataModelName) { |
||||||
|
this(null, tableData, tableDataName, dataModelName, true); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param tableData 数据集 |
||||||
|
* @param tableDataName 数据集名称 |
||||||
|
* @param dataModelName 数据集的一个结果的名称(全限定名称) |
||||||
|
* @param needLoad 是否需要加载 |
||||||
|
*/ |
||||||
|
public MultiResultTableDataWrapper(MultiResultTableData<?> tableData, String tableDataName, String dataModelName, boolean needLoad) { |
||||||
|
this(null, tableData, tableDataName, dataModelName, needLoad); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param component 父容器 |
||||||
|
* @param tableData 数据集 |
||||||
|
* @param tableDataName 数据集名称 |
||||||
|
* @param dataModelName 数据集的一个结果的名称(全限定名称) |
||||||
|
*/ |
||||||
|
public MultiResultTableDataWrapper(Component component, MultiResultTableData<?> tableData, String tableDataName, String dataModelName) { |
||||||
|
this(component, tableData, tableDataName, dataModelName, true); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param component loadingBar的父弹框(如果不设置父弹框的话,可能出现loadingBar隐藏在一个弹框后的情况) |
||||||
|
* @param tableData 多结果数据集 |
||||||
|
* @param tableDataName 多结果数据集的名字(某些情况下可以为空) |
||||||
|
* @param dataModelName 多结果数据集一个返回数据集的名字 |
||||||
|
* @param needLoad 是否要加载 |
||||||
|
**/ |
||||||
|
public MultiResultTableDataWrapper(Component component, MultiResultTableData<?> tableData, String tableDataName, String dataModelName, boolean needLoad) { |
||||||
|
this.dataModelName = dataModelName; |
||||||
|
this.tableData = tableData; |
||||||
|
this.tableDataName = tableDataName; |
||||||
|
if (component == null) { |
||||||
|
component = new JFrame(); |
||||||
|
} |
||||||
|
if (needLoad) { |
||||||
|
setWorker(component); |
||||||
|
} |
||||||
|
loadingBar = new AutoProgressBar(component, Toolkit.i18nText("Fine-Design_Basic_Loading_Data"), "", 0, 100) { |
||||||
|
@Override |
||||||
|
public void doMonitorCanceled() { |
||||||
|
getWorker().cancel(true); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 数据集执行结果返回的所有字段 |
||||||
|
* |
||||||
|
* @return 数据集执行结果返回的所有字段 |
||||||
|
* @date 2014-12-3-下午7:43:17 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public List<String> calculateColumnNameList() { |
||||||
|
if (columnNameList != null) { |
||||||
|
return columnNameList; |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
createResults(false); |
||||||
|
} catch (Exception e) { |
||||||
|
FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), Toolkit.i18nText("Fine-Design_Basic_Engine_No_TableData")); |
||||||
|
return new ArrayList<String>(); |
||||||
|
} |
||||||
|
columnNameList = Arrays.asList(dataModel.getColumnNames()); |
||||||
|
return columnNameList; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 生成子节点 |
||||||
|
* |
||||||
|
* @return 节点数组 |
||||||
|
* @date 2014-12-3-下午7:06:47 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public ExpandMutableTreeNode[] load() { |
||||||
|
List<String> namelist; |
||||||
|
if (tableData.isCalculating()) { |
||||||
|
namelist = Arrays.asList(new String[0]); |
||||||
|
} else { |
||||||
|
namelist = calculateColumnNameList(); |
||||||
|
} |
||||||
|
ExpandMutableTreeNode[] res = new ExpandMutableTreeNode[namelist.size()]; |
||||||
|
for (int i = 0; i < res.length; i++) { |
||||||
|
res[i] = new ExpandMutableTreeNode(namelist.get(i)); |
||||||
|
} |
||||||
|
|
||||||
|
return res; |
||||||
|
} |
||||||
|
|
||||||
|
private void createResults(boolean needLoadingBar) throws Exception { |
||||||
|
|
||||||
|
this.dataModel = null; |
||||||
|
dataModels = DesignTableDataManager.createLazyDataModel(tableData, needLoadingBar); |
||||||
|
if (dataModels != null && dataModels.length != 0) { |
||||||
|
for (NameDataModel dataModel : dataModels) { |
||||||
|
if (ComparatorUtils.equals(this.dataModelName, tableDataName + MultiResultTableData.GROUP_MARKER + dataModel.getName())) { |
||||||
|
this.dataModel = dataModel; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Icon getIcon() { |
||||||
|
return MultiResultTableDataWrapperHelper.getIcon(this.tableData.getClass()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 预览数据 |
||||||
|
* |
||||||
|
* @param previewModel 预览模式, 全部还是一个 |
||||||
|
* @date 2014-12-3-下午7:05:50 |
||||||
|
*/ |
||||||
|
public void previewData(final int previewModel) { |
||||||
|
this.previewModel = previewModel; |
||||||
|
connectionBar = new AutoProgressBar(new JFrame(), Toolkit.i18nText("Fine-Design_Basic_Utils_Now_Create_Connection"), "", 0, 100) { |
||||||
|
@Override |
||||||
|
public void doMonitorCanceled() { |
||||||
|
connectionBar.close(); |
||||||
|
worker.cancel(true); |
||||||
|
} |
||||||
|
}; |
||||||
|
worker.execute(); |
||||||
|
} |
||||||
|
|
||||||
|
private void setWorker(final Component parent) { |
||||||
|
worker = new SwingWorker<Void, Void>() { |
||||||
|
@Override |
||||||
|
protected Void doInBackground() throws Exception { |
||||||
|
loadingBar.close(); |
||||||
|
PreviewTablePane.resetPreviewTable(); |
||||||
|
connectionBar.start(); |
||||||
|
|
||||||
|
// 存储过程需要先测试一下连接
|
||||||
|
if (tableData instanceof StoreProcedure) { |
||||||
|
boolean status = DataOperator.getInstance().testConnection(((StoreProcedure) getTableData()).getDatabaseConnection()); |
||||||
|
if (!status) { |
||||||
|
connectionBar.close(); |
||||||
|
throw new Exception(Toolkit.i18nText("Fine-Design_Basic_Database_Connection_Failed")); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
connectionBar.close(); |
||||||
|
tableData.resetDataModelList(); |
||||||
|
|
||||||
|
// 获取结果
|
||||||
|
createResults(true); |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void done() { |
||||||
|
try { |
||||||
|
get(); |
||||||
|
loadingBar.close(); |
||||||
|
switch (previewModel) { |
||||||
|
case MultiResultTableDataWrapper.PREVIEW_ALL: |
||||||
|
PreviewTablePane.previewMultiDataModels(dataModels); |
||||||
|
break; |
||||||
|
case MultiResultTableDataWrapper.PREVIEW_ONE: |
||||||
|
previewData(); |
||||||
|
break; |
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
} catch (Exception e) { |
||||||
|
loadingBar.close(); |
||||||
|
if (!(e instanceof CancellationException)) { |
||||||
|
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||||
|
FineJOptionPane.showMessageDialog(parent, e.getMessage()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
private SwingWorker getWorker() { |
||||||
|
return this.worker; |
||||||
|
} |
||||||
|
|
||||||
|
// august:这个只是预览返回的一个数据集
|
||||||
|
|
||||||
|
/** |
||||||
|
* 预览返回的一个数据集 |
||||||
|
* |
||||||
|
* @date 2014-12-3-下午7:42:53 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void previewData() { |
||||||
|
previewData(-1, -1); |
||||||
|
} |
||||||
|
|
||||||
|
// august:这个只是预览返回的一个数据集
|
||||||
|
|
||||||
|
/** |
||||||
|
* 预览返回的一个数据集,带有显示值和实际值的标记结果 |
||||||
|
* |
||||||
|
* @param keyIndex 实际值 |
||||||
|
* @param valueIndex 显示值 |
||||||
|
* @date 2014-12-3-下午7:42:27 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void previewData(final int keyIndex, final int valueIndex) { |
||||||
|
PreviewTablePane.previewDataModel(dataModel, keyIndex, valueIndex); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 预览返回的所有数据集,只有在编辑多结果数据集时才用到 |
||||||
|
*/ |
||||||
|
public void previewAllTable() { |
||||||
|
if (dataModel == null) { |
||||||
|
try { |
||||||
|
createResults(true); |
||||||
|
} catch (Exception e) { |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
PreviewTablePane.previewMultiDataModels(dataModels); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getTableDataName() { |
||||||
|
// todo 这里返回数据集结果名称,带_的
|
||||||
|
return dataModelName; |
||||||
|
} |
||||||
|
|
||||||
|
public String getDataModelName() { |
||||||
|
return dataModelName; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public TableData getTableData() { |
||||||
|
return tableData; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 获取当前节点所属可编辑的数据集名称 |
||||||
|
* |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public String getMultiResultTableDataName() { |
||||||
|
return tableDataName; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 是否异常 |
||||||
|
* |
||||||
|
* @return 是否异常 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public boolean isUnusual() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean equals(Object obj) { |
||||||
|
return obj instanceof MultiResultTableDataWrapper |
||||||
|
&& ComparatorUtils.equals(this.getDataModelName(), ((MultiResultTableDataWrapper) obj).getDataModelName()) |
||||||
|
&& ComparatorUtils.equals(this.getTableData(), ((MultiResultTableDataWrapper) obj).getTableData()) |
||||||
|
&& ComparatorUtils.equals(this.getMultiResultTableDataName(), ((MultiResultTableDataWrapper) obj).getMultiResultTableDataName()) |
||||||
|
&& ComparatorUtils.equals(this.getTableDataName(), ((MultiResultTableDataWrapper) obj).getTableDataName()); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,54 @@ |
|||||||
|
package com.fr.design.data.tabledata.wrapper; |
||||||
|
|
||||||
|
import com.fr.base.TableData; |
||||||
|
import com.fr.design.data.datapane.TableDataCreatorProducer; |
||||||
|
import com.fr.design.data.datapane.TableDataNameObjectCreator; |
||||||
|
import com.fr.design.fun.ServerTableDataDefineProvider; |
||||||
|
import com.fr.design.fun.TableDataDefineProvider; |
||||||
|
import com.fr.event.Event; |
||||||
|
import com.fr.event.EventDispatcher; |
||||||
|
import com.fr.event.Listener; |
||||||
|
import com.fr.general.IOUtils; |
||||||
|
import com.fr.plugin.context.PluginContext; |
||||||
|
import com.fr.plugin.observer.PluginEventType; |
||||||
|
import com.fr.stable.Filter; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import javax.swing.Icon; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author rinoux |
||||||
|
* @version 11.0 |
||||||
|
* Created by rinoux on 2022/9/7 |
||||||
|
*/ |
||||||
|
class MultiResultTableDataWrapperHelper { |
||||||
|
private static final Map<Class<? extends TableData>, Icon> TABLE_DATA_ICON_PATHS = new HashMap<>(); |
||||||
|
private static final String DEFAULT_MULTI_RESULT_TD_ICON = "/com/fr/design/images/data/multi.png"; |
||||||
|
|
||||||
|
|
||||||
|
static { |
||||||
|
Listener<PluginContext> listener = new Listener<PluginContext>() { |
||||||
|
@Override |
||||||
|
public void on(Event event, PluginContext param) { |
||||||
|
TABLE_DATA_ICON_PATHS.clear(); |
||||||
|
} |
||||||
|
}; |
||||||
|
Filter<PluginContext> filter = pluginContext -> pluginContext.contain(TableDataDefineProvider.XML_TAG) || pluginContext.contain(ServerTableDataDefineProvider.XML_TAG); |
||||||
|
EventDispatcher.listen(PluginEventType.AfterRun, listener, filter); |
||||||
|
EventDispatcher.listen(PluginEventType.BeforeStop, listener, filter); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public static @NotNull Icon getIcon(@NotNull Class<? extends TableData> tableDataClass) { |
||||||
|
return TABLE_DATA_ICON_PATHS.computeIfAbsent(tableDataClass, cls -> { |
||||||
|
for (TableDataNameObjectCreator creator : TableDataCreatorProducer.getInstance().createReportTableDataCreator()) { |
||||||
|
if (creator.createObject().getClass().isAssignableFrom(tableDataClass)) { |
||||||
|
return IOUtils.readIcon(creator.getIconPath()); |
||||||
|
} |
||||||
|
} |
||||||
|
return IOUtils.readIcon(DEFAULT_MULTI_RESULT_TD_ICON); |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
package com.fr.design.deeplink; |
||||||
|
|
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/08/23 |
||||||
|
**/ |
||||||
|
public interface DeepLinkPrepare { |
||||||
|
|
||||||
|
/** |
||||||
|
* 准备 |
||||||
|
* |
||||||
|
* @param url 链接 |
||||||
|
* @return 成功/失败 |
||||||
|
*/ |
||||||
|
boolean prepare(String url, String host, String path, Map<String, Object> params); |
||||||
|
} |
@ -0,0 +1,487 @@ |
|||||||
|
package com.fr.design.file; |
||||||
|
|
||||||
|
import com.fr.design.actions.UpdateAction; |
||||||
|
import com.fr.design.actions.file.DelFileAction; |
||||||
|
import com.fr.design.actions.file.LocateAction; |
||||||
|
import com.fr.design.actions.file.RenameAction; |
||||||
|
import com.fr.design.dialog.FineJOptionPane; |
||||||
|
import com.fr.design.fun.impl.AbstractTemplateTreeDefineProcessor; |
||||||
|
import com.fr.design.gui.ibutton.UIButton; |
||||||
|
import com.fr.design.gui.imenu.UIPopupMenu; |
||||||
|
import com.fr.design.gui.itree.filetree.TemplateDirTree; |
||||||
|
import com.fr.design.gui.itree.filetree.TemplateFileTree; |
||||||
|
import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.mainframe.DesignerContext; |
||||||
|
import com.fr.design.mainframe.DesignerFrameFileDealerPane; |
||||||
|
import com.fr.design.mainframe.manager.clip.TemplateTreeClipboard; |
||||||
|
import com.fr.design.mainframe.manager.search.TemplateDirTreeSearchManager; |
||||||
|
import com.fr.design.mainframe.manager.search.TemplateTreeSearchManager; |
||||||
|
import com.fr.design.mainframe.manager.search.searcher.control.pane.TemplateDirTreeSearchPane; |
||||||
|
import com.fr.design.mainframe.toast.DesignerToastMsgUtil; |
||||||
|
import com.fr.design.mainframe.toast.ToastMsgDialog; |
||||||
|
import com.fr.design.utils.TemplateUtils; |
||||||
|
import com.fr.design.utils.gui.GUICoreUtils; |
||||||
|
import com.fr.file.filetree.FileNode; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.stable.StableUtils; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
import com.fr.stable.collections.CollectionUtils; |
||||||
|
import com.fr.stable.project.ProjectConstants; |
||||||
|
|
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.JDialog; |
||||||
|
import javax.swing.JOptionPane; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.SwingUtilities; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.CardLayout; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.FlowLayout; |
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
import java.awt.event.ActionListener; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
import static javax.swing.JOptionPane.WARNING_MESSAGE; |
||||||
|
import static javax.swing.JOptionPane.YES_NO_OPTION; |
||||||
|
|
||||||
|
/** |
||||||
|
* 默认的模板右键处理方式:包含重命名、复制、黏贴、删除、移动等功能 |
||||||
|
*/ |
||||||
|
public class DefaultTemplateTreeDefineProcessor extends AbstractTemplateTreeDefineProcessor { |
||||||
|
|
||||||
|
private UIPopupMenu popupMenu; |
||||||
|
private RenameAction renameAction; |
||||||
|
private CopyAction copyAction; |
||||||
|
private PasteAction pasteAction; |
||||||
|
private DelFileAction delFileAction; |
||||||
|
private MoveAction moveAction; |
||||||
|
private LocateAction locateAction; |
||||||
|
|
||||||
|
public static DefaultTemplateTreeDefineProcessor getInstance() { |
||||||
|
return DefaultTemplateTreeDefineProcessor.HOLDER.singleton; |
||||||
|
} |
||||||
|
|
||||||
|
private static class HOLDER { |
||||||
|
private static DefaultTemplateTreeDefineProcessor singleton = new DefaultTemplateTreeDefineProcessor(); |
||||||
|
} |
||||||
|
|
||||||
|
private DefaultTemplateTreeDefineProcessor() { |
||||||
|
initPane(); |
||||||
|
} |
||||||
|
|
||||||
|
private void initPane() { |
||||||
|
renameAction = new RenameAction(); |
||||||
|
copyAction = new CopyAction(); |
||||||
|
pasteAction = new PasteAction(); |
||||||
|
delFileAction = new DelFileAction(); |
||||||
|
moveAction = new MoveAction(); |
||||||
|
locateAction = new OpenInTemplateTreeAction(); |
||||||
|
//右键菜单
|
||||||
|
popupMenu = new UIPopupMenu(); |
||||||
|
popupMenu.add(renameAction.createMenuItem()); |
||||||
|
popupMenu.addSeparator(); |
||||||
|
popupMenu.add(copyAction.createMenuItem()); |
||||||
|
popupMenu.add(pasteAction.createMenuItem()); |
||||||
|
popupMenu.add(delFileAction.createMenuItem()); |
||||||
|
popupMenu.addSeparator(); |
||||||
|
popupMenu.add(moveAction.createMenuItem()); |
||||||
|
popupMenu.add(locateAction.createMenuItem()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void rightClickAction(MouseEvent e) { |
||||||
|
if (SwingUtilities.isRightMouseButton(e)) { |
||||||
|
GUICoreUtils.showPopupMenu(popupMenu, e.getComponent(), e.getX(), e.getY()); |
||||||
|
checkButtonEnabled(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void checkButtonEnabled() { |
||||||
|
renameAction.setEnabled(false); |
||||||
|
copyAction.setEnabled(false); |
||||||
|
pasteAction.setEnabled(false); |
||||||
|
delFileAction.setEnabled(false); |
||||||
|
moveAction.setEnabled(false); |
||||||
|
locateAction.setEnabled(false); |
||||||
|
int length = getFileTree().getSelectionCount(); |
||||||
|
if (length == 0) { |
||||||
|
//没有选中文件时,只能黏贴
|
||||||
|
if (!CollectionUtils.isEmpty(TemplateTreeClipboard.getInstance().takeFromClip())) { |
||||||
|
pasteAction.setEnabled(true); |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
if (length == 1) { |
||||||
|
//选中一个时可以,可以重命名、黏贴
|
||||||
|
renameAction.setEnabled(true); |
||||||
|
if (TemplateTreeSearchManager.getInstance().isInSearchMode()) { |
||||||
|
//搜索模式开启定位当前模板
|
||||||
|
locateAction.setEnabled(true); |
||||||
|
} |
||||||
|
if (!CollectionUtils.isEmpty(TemplateTreeClipboard.getInstance().takeFromClip())) { |
||||||
|
pasteAction.setEnabled(true); |
||||||
|
} |
||||||
|
} |
||||||
|
moveAction.setEnabled(true); |
||||||
|
delFileAction.setEnabled(true); |
||||||
|
copyAction.setEnabled(true); |
||||||
|
} |
||||||
|
|
||||||
|
private TemplateFileTree getFileTree() { |
||||||
|
return TemplateTreePane.getInstance().getTemplateFileTree(); |
||||||
|
} |
||||||
|
|
||||||
|
private TemplateDirTree getDirTree() { |
||||||
|
return TemplateDirTreePane.getInstance().getTemplateDirTree(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 复制功能 |
||||||
|
*/ |
||||||
|
private class CopyAction extends UpdateAction { |
||||||
|
|
||||||
|
public CopyAction() { |
||||||
|
this.setName(Toolkit.i18nText("Fine-Design_Basic_Copy")); |
||||||
|
this.setMnemonic('C'); |
||||||
|
this.setSmallIcon("/com/fr/design/images/m_edit/copy"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
FileOperations selectedOperation = DesignerFrameFileDealerPane.getInstance().getSelectedOperation(); |
||||||
|
if (!selectedOperation.access()) { |
||||||
|
FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Template_Permission_Denied"), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Alert"), |
||||||
|
WARNING_MESSAGE); |
||||||
|
return; |
||||||
|
} |
||||||
|
//先记录所有的要复制的模板
|
||||||
|
List<ExpandMutableTreeNode> treeNodeList = TemplateTreeClipboard.getInstance().transferNameObjectArray2Map(getFileTree().getSelectedTreeNodes()); |
||||||
|
TemplateTreeClipboard.getInstance().addToClip(treeNodeList); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private class OpenInTemplateTreeAction extends LocateAction { |
||||||
|
|
||||||
|
public OpenInTemplateTreeAction() { |
||||||
|
this.setName(Toolkit.i18nText("Fine-Design_Basic_Locate_Node")); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
FileNode fileNode = getFileTree().getSelectedFileNode(); |
||||||
|
if (fileNode == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
gotoEditingTemplateLeaf(fileNode.getEnvPath()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 黏贴功能 |
||||||
|
*/ |
||||||
|
private class PasteAction extends UpdateAction { |
||||||
|
|
||||||
|
public PasteAction() { |
||||||
|
this.setName(Toolkit.i18nText("Fine-Design_Basic_Action_Paste_Name")); |
||||||
|
this.setMnemonic('P'); |
||||||
|
this.setSmallIcon("/com/fr/design/images/m_edit/paste"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
List<ExpandMutableTreeNode> treeNodeList = TemplateTreeClipboard.getInstance().takeFromClip(); |
||||||
|
if (!canPaste(treeNodeList)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
String targetDir = getTargetDir(); |
||||||
|
|
||||||
|
// 筛选可以黏贴的文件
|
||||||
|
ArrayList<ExpandMutableTreeNode> pasteNodes = new ArrayList<>(); |
||||||
|
ArrayList<ExpandMutableTreeNode> lockedNodes = new ArrayList<>(); |
||||||
|
for (ExpandMutableTreeNode treeNode : treeNodeList) { |
||||||
|
FileOperationHelper.getInstance().checkFreeOrLock(treeNode, pasteNodes, lockedNodes); |
||||||
|
} |
||||||
|
if (pasteNodes.isEmpty() || !lockedNodes.isEmpty()) { |
||||||
|
//提示:复制的文件都不能黏贴
|
||||||
|
FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Confirm_Paste_Unlock_File"), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Alert"), |
||||||
|
WARNING_MESSAGE); |
||||||
|
return; |
||||||
|
} |
||||||
|
doPaste(targetDir, treeNodeList); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 检测是否能够黏贴 |
||||||
|
* @param treeNodeList |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private boolean canPaste(List<ExpandMutableTreeNode> treeNodeList) { |
||||||
|
if (CollectionUtils.isEmpty(treeNodeList)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
//确定目标目录并检查权限
|
||||||
|
FileOperations selectedOperation = DesignerFrameFileDealerPane.getInstance().getSelectedOperation(); |
||||||
|
if (!selectedOperation.access()) { |
||||||
|
FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Template_Permission_Denied"), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Alert"), |
||||||
|
WARNING_MESSAGE); |
||||||
|
return false; |
||||||
|
} |
||||||
|
//检测之前复制的文件是否被打开
|
||||||
|
if (TemplateUtils.checkTemplateIsEditing(treeNodeList.toArray(new ExpandMutableTreeNode[0]))) { |
||||||
|
return FineJOptionPane.showConfirmDialog(DesignerContext.getDesignerFrame(), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Template_Is_Editing"), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Alert"), |
||||||
|
YES_NO_OPTION) == JOptionPane.YES_OPTION; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private void doPaste(String targetDir, List<ExpandMutableTreeNode> pasteNodes) { |
||||||
|
String targetFile = targetDir; |
||||||
|
try { |
||||||
|
if (StringUtils.isEmpty(targetDir) || !confirmTargetDir(targetDir, pasteNodes)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
for (ExpandMutableTreeNode node : pasteNodes) { |
||||||
|
if (node.getUserObject() instanceof FileNode) { |
||||||
|
FileNode fileNode = (FileNode) node.getUserObject(); |
||||||
|
String newTargetFile = FileOperationHelper.getInstance().copyFile(fileNode, targetDir); |
||||||
|
if (StringUtils.isNotEmpty(newTargetFile)) { |
||||||
|
targetFile = newTargetFile; |
||||||
|
} |
||||||
|
FineLoggerFactory.getLogger().debug("Template: {} paste to {} success.", fileNode.getEnvPath(), targetDir); |
||||||
|
} |
||||||
|
} |
||||||
|
} catch (Exception e) { |
||||||
|
FineLoggerFactory.getLogger().error(e,"Template paste failed.", e.getMessage()); |
||||||
|
FineJOptionPane.showConfirmDialog(null, |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Paste_Failure"), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Error"), |
||||||
|
JOptionPane.DEFAULT_OPTION, |
||||||
|
JOptionPane.ERROR_MESSAGE); |
||||||
|
} |
||||||
|
|
||||||
|
DesignerFrameFileDealerPane.getInstance().getSelectedOperation().refreshParent(); |
||||||
|
LocateAction.gotoEditingTemplateLeaf(targetFile); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 确认粘贴的目标目录是否是复制文件的子目录,并确认是否继续执行粘贴任务 |
||||||
|
* |
||||||
|
* @param targetDir 目标文件夹 |
||||||
|
* @param pasteNodes 待粘贴的文件 |
||||||
|
* @return 是否继续 |
||||||
|
*/ |
||||||
|
private boolean confirmTargetDir(String targetDir, List<ExpandMutableTreeNode> pasteNodes) { |
||||||
|
List<ExpandMutableTreeNode> filterNodes = pasteNodes.stream().filter( |
||||||
|
treeNode -> targetDir.startsWith(((FileNode) (treeNode.getUserObject())).getEnvPath())).collect(Collectors.toList()); |
||||||
|
if (!CollectionUtils.isEmpty(filterNodes)) { |
||||||
|
if (FineJOptionPane.showConfirmDialog(DesignerContext.getDesignerFrame(), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Confirm_Paste_Target_Dir"), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Alert"), |
||||||
|
YES_NO_OPTION) != JOptionPane.YES_OPTION) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
//移除待粘贴文件中目标目录的父目录
|
||||||
|
pasteNodes.removeAll(filterNodes); |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 移动功能 |
||||||
|
*/ |
||||||
|
private class MoveAction extends UpdateAction { |
||||||
|
|
||||||
|
public MoveAction() { |
||||||
|
this.setName(Toolkit.i18nText("Fine-Design_Basic_Move")); |
||||||
|
this.setSmallIcon("/com/fr/design/images/m_edit/move"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
FileOperations selectedOperation = DesignerFrameFileDealerPane.getInstance().getSelectedOperation(); |
||||||
|
if (!selectedOperation.access()) { |
||||||
|
FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Template_Permission_Denied"), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Alert"), |
||||||
|
WARNING_MESSAGE); |
||||||
|
return; |
||||||
|
} |
||||||
|
// 筛选可以移动的文件
|
||||||
|
ArrayList<ExpandMutableTreeNode> moveNodes = new ArrayList<>(); |
||||||
|
ArrayList<ExpandMutableTreeNode> lockedNodes = new ArrayList<>(); |
||||||
|
for (ExpandMutableTreeNode treeNode : getFileTree().getSelectedTreeNodes()) { |
||||||
|
FileOperationHelper.getInstance().checkFreeOrLock(treeNode, moveNodes, lockedNodes); |
||||||
|
} |
||||||
|
|
||||||
|
if (!lockedNodes.isEmpty()) { |
||||||
|
// 提醒被锁定模板无法移动
|
||||||
|
FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Unable_Move_Locked_File"), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Alert"), |
||||||
|
WARNING_MESSAGE); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
new TemplateMoveDialog(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private class TemplateMoveDialog extends JDialog { |
||||||
|
|
||||||
|
private static final String DIR = "dir"; |
||||||
|
private TemplateDirTreeSearchPane searchPane; |
||||||
|
private TemplateDirTreePane dirTreePane; |
||||||
|
private UIButton confirmButton; |
||||||
|
private String targetFile; |
||||||
|
|
||||||
|
public TemplateMoveDialog() { |
||||||
|
super(DesignerContext.getDesignerFrame(), true); |
||||||
|
this.setLayout(new BorderLayout()); |
||||||
|
|
||||||
|
searchPane = new TemplateDirTreeSearchPane(); |
||||||
|
add(searchPane, BorderLayout.NORTH); |
||||||
|
|
||||||
|
CardLayout card; |
||||||
|
JPanel cardPane = new JPanel(card = new CardLayout()); |
||||||
|
cardPane.add(TemplateDirTreePane.getInstance(), DIR); |
||||||
|
dirTreePane = TemplateDirTreePane.getInstance(); |
||||||
|
card.show(cardPane, DIR); |
||||||
|
add(cardPane, BorderLayout.CENTER); |
||||||
|
cardPane.setBorder(BorderFactory.createEmptyBorder(10, 15, 0, 10)); |
||||||
|
dirTreePane.refresh(); |
||||||
|
|
||||||
|
// 确认按钮,默认就可用
|
||||||
|
confirmButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Confirm")); |
||||||
|
confirmButton.setPreferredSize(new Dimension(60, 25)); |
||||||
|
confirmButton.addActionListener(new ActionListener() { |
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
confirmClose(); |
||||||
|
} |
||||||
|
}); |
||||||
|
confirmButton.setEnabled(true); |
||||||
|
|
||||||
|
// 取消按钮
|
||||||
|
UIButton cancelButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Cancel")); |
||||||
|
cancelButton.setPreferredSize(new Dimension(60, 25)); |
||||||
|
|
||||||
|
cancelButton.addActionListener(new ActionListener() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
dispose(); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
JPanel bottomPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 0)); |
||||||
|
bottomPanel.setBorder(BorderFactory.createEmptyBorder(10, 15, 10, 10)); |
||||||
|
bottomPanel.add(confirmButton); |
||||||
|
bottomPanel.add(cancelButton); |
||||||
|
this.add(bottomPanel, BorderLayout.SOUTH); |
||||||
|
|
||||||
|
this.setSize(new Dimension(600, 400)); |
||||||
|
this.setTitle(Toolkit.i18nText("Fine-Design_Basic_Move")); |
||||||
|
this.setResizable(false); |
||||||
|
this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); |
||||||
|
GUICoreUtils.centerWindow(this); |
||||||
|
this.setVisible(true); |
||||||
|
} |
||||||
|
|
||||||
|
private void confirmClose() { |
||||||
|
//获取目录树中所选中的文件,并判断是否有权限
|
||||||
|
if (!checkBeforeMove()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
boolean moveSuccess = doMove(); |
||||||
|
dispose(); |
||||||
|
if (moveSuccess) { |
||||||
|
ToastMsgDialog dialog = DesignerToastMsgUtil.createPromptDialog(Toolkit.i18nText("Fine-Design_Basic_Template_Moved_Success")); |
||||||
|
dialog.setVisible(true); |
||||||
|
|
||||||
|
DesignerFrameFileDealerPane.getInstance().getSelectedOperation().refresh(); |
||||||
|
LocateAction.gotoEditingTemplateLeaf(targetFile); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private boolean checkBeforeMove() { |
||||||
|
if (getDirTree().getSelectionCount() != 0 && !TemplateDirTreePane.getInstance().selectedAccess()) { |
||||||
|
FineJOptionPane.showMessageDialog(this, |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Template_Permission_Denied"), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Alert"), |
||||||
|
WARNING_MESSAGE); |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (TemplateUtils.checkSelectedTemplateIsEditing()) { |
||||||
|
return FineJOptionPane.showConfirmDialog(this, |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Template_Is_Editing"), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Alert"), |
||||||
|
YES_NO_OPTION) == JOptionPane.YES_OPTION; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
private boolean doMove() { |
||||||
|
FileNode fileNode = getDirTree().getSelectedFileNode(); |
||||||
|
ExpandMutableTreeNode rootTreeNode = (ExpandMutableTreeNode) getDirTree().getModel().getRoot(); |
||||||
|
fileNode = fileNode == null ? (FileNode) rootTreeNode.getUserObject() : fileNode; |
||||||
|
boolean moveSuccess = true; |
||||||
|
try { |
||||||
|
//待移动的文件可以有多个
|
||||||
|
ExpandMutableTreeNode[] sourceSelected = getFileTree().getSelectedTreeNodes(); |
||||||
|
for (ExpandMutableTreeNode treeNode : sourceSelected) { |
||||||
|
FileNode sourceFileNode = (FileNode) treeNode.getUserObject(); |
||||||
|
targetFile = FileOperationHelper.getInstance().moveFile(sourceFileNode, fileNode.getEnvPath()); |
||||||
|
} |
||||||
|
} catch (Exception e) { |
||||||
|
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||||
|
FineJOptionPane.showMessageDialog(this, |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Template_Moved_Fail"), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Alert"), |
||||||
|
WARNING_MESSAGE); |
||||||
|
moveSuccess = false; |
||||||
|
} |
||||||
|
return moveSuccess; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void dispose() { |
||||||
|
TemplateDirTreeSearchManager.getInstance().outOfSearchMode(); |
||||||
|
super.dispose(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 黏贴时确定黏贴的目录 |
||||||
|
* |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private String getTargetDir() { |
||||||
|
int count = getFileTree().getSelectionCount(); |
||||||
|
String targetDir; |
||||||
|
if (count == 0) { |
||||||
|
targetDir = ProjectConstants.REPORTLETS_NAME; |
||||||
|
} else { |
||||||
|
FileNode fileNode = getFileTree().getSelectedFileNode(); |
||||||
|
targetDir = fileNode.getParent(); |
||||||
|
if (fileNode.isDirectory()) { |
||||||
|
targetDir = StableUtils.pathJoin(targetDir, fileNode.getName()); |
||||||
|
} |
||||||
|
} |
||||||
|
return targetDir; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,208 @@ |
|||||||
|
package com.fr.design.file; |
||||||
|
|
||||||
|
import com.fr.design.dialog.FineJOptionPane; |
||||||
|
import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.mainframe.DesignerContext; |
||||||
|
import com.fr.design.mainframe.DesignerFrameFileDealerPane; |
||||||
|
import com.fr.design.mainframe.vcs.common.VcsHelper; |
||||||
|
import com.fr.file.FileNodeFILE; |
||||||
|
import com.fr.file.filetree.FileNode; |
||||||
|
import com.fr.general.ComparatorUtils; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.stable.ArrayUtils; |
||||||
|
import com.fr.stable.StableUtils; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
import com.fr.stable.project.ProjectConstants; |
||||||
|
import com.fr.workspace.WorkContext; |
||||||
|
import com.fr.workspace.resource.ResourceIOException; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
|
||||||
|
import static javax.swing.JOptionPane.WARNING_MESSAGE; |
||||||
|
|
||||||
|
/** |
||||||
|
* 文件操作的辅助类 |
||||||
|
*/ |
||||||
|
public class FileOperationHelper { |
||||||
|
|
||||||
|
private static final FileOperationHelper INSTANCE = new FileOperationHelper(); |
||||||
|
|
||||||
|
public static FileOperationHelper getInstance() { |
||||||
|
return INSTANCE; |
||||||
|
} |
||||||
|
|
||||||
|
public String moveFile(FileNode sourceFileNode, String targetDir) { |
||||||
|
String targetPath = copyFileAndVcs(sourceFileNode, targetDir); |
||||||
|
FileNodeFILE nodeFILE = new FileNodeFILE(sourceFileNode); |
||||||
|
if (nodeFILE.exists()) { |
||||||
|
if (TemplateResourceManager.getResource().delete(nodeFILE)) { |
||||||
|
HistoryTemplateListCache.getInstance().deleteFile(nodeFILE); |
||||||
|
FineLoggerFactory.getLogger().info("template {} move to {} success.", sourceFileNode.getEnvPath(), targetDir); |
||||||
|
} else { |
||||||
|
//删除源文件失败,将复制过去的文件删掉
|
||||||
|
TemplateResourceManager.getResource().delete(new FileNodeFILE(new FileNode(targetPath, sourceFileNode.isDirectory()))); |
||||||
|
FineLoggerFactory.getLogger().error("template {} move to {} failed.", sourceFileNode.getEnvPath(), targetDir); |
||||||
|
throw new ResourceIOException(String.format("template %s move to %s failed", sourceFileNode.getEnvPath(), targetDir)); |
||||||
|
} |
||||||
|
} |
||||||
|
return targetPath; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 拷贝文件的同时拷贝对应的版本控制文件 |
||||||
|
* @param sourceFile 源文件或目录 |
||||||
|
* @param targetDir 目标目录 |
||||||
|
* @return 复制后的目标文件的路径 |
||||||
|
*/ |
||||||
|
public String copyFileAndVcs(FileNode sourceFile, String targetDir) { |
||||||
|
return copyFile(sourceFile, targetDir, true); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 只拷贝文件, 不拷贝对应的版本控制文件 |
||||||
|
* @param sourceFile 源文件或目录 |
||||||
|
* @param targetDir 目标目录 |
||||||
|
* @return 复制后的目标文件的路径 |
||||||
|
*/ |
||||||
|
public String copyFile(FileNode sourceFile, String targetDir) { |
||||||
|
return copyFile(sourceFile, targetDir, false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 检测节点是否被锁住了 |
||||||
|
* @param node 待检测节点 |
||||||
|
* @param dNodes 没有锁住的节点集合 |
||||||
|
* @param lNodes 锁住的节点集合 |
||||||
|
* @return 是否存在被锁住的文件 |
||||||
|
*/ |
||||||
|
public boolean checkFreeOrLock(ExpandMutableTreeNode node, ArrayList<ExpandMutableTreeNode> dNodes, ArrayList<ExpandMutableTreeNode> lNodes) { |
||||||
|
// 自己没锁
|
||||||
|
boolean selfEmptyLock = false; |
||||||
|
boolean dir = false; |
||||||
|
Object userObj = node.getUserObject(); |
||||||
|
if (userObj instanceof FileNode) { |
||||||
|
FileNode fileNode = (FileNode) userObj; |
||||||
|
String lock = fileNode.getLock(); |
||||||
|
selfEmptyLock = lock == null || fileNode.getUserID().equals(lock); |
||||||
|
dir = fileNode.isDirectory(); |
||||||
|
} |
||||||
|
|
||||||
|
if (!dir) { |
||||||
|
if (selfEmptyLock) { |
||||||
|
dNodes.add(node); |
||||||
|
} else { |
||||||
|
lNodes.add(node); |
||||||
|
} |
||||||
|
return selfEmptyLock; |
||||||
|
} |
||||||
|
|
||||||
|
return checkChildNode(node, dNodes, lNodes, selfEmptyLock); |
||||||
|
} |
||||||
|
|
||||||
|
private boolean checkChildNode(ExpandMutableTreeNode node, ArrayList<ExpandMutableTreeNode> dNodes, ArrayList<ExpandMutableTreeNode> lNodes, boolean selfEmptyLock) { |
||||||
|
ExpandMutableTreeNode[] children = TemplateTreePane.getInstance().getTemplateFileTree().loadChildTreeNodes(node); |
||||||
|
|
||||||
|
boolean childrenEmptyLock = true; |
||||||
|
|
||||||
|
for (ExpandMutableTreeNode child : children) { |
||||||
|
childrenEmptyLock = checkFreeOrLock(child, dNodes, lNodes) && childrenEmptyLock; |
||||||
|
} |
||||||
|
|
||||||
|
boolean emptyLock = childrenEmptyLock && selfEmptyLock; |
||||||
|
if (emptyLock) { |
||||||
|
dNodes.add(node); |
||||||
|
} else { |
||||||
|
lNodes.add(node); |
||||||
|
} |
||||||
|
return emptyLock; |
||||||
|
} |
||||||
|
|
||||||
|
private String copyFile(FileNode sourceFile, String targetDir, boolean withCopyVcs) { |
||||||
|
String sourcePath = sourceFile.getEnvPath(); |
||||||
|
if (!TemplateResourceManager.getResource().exist(sourcePath)) { |
||||||
|
FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Source_File_Not_Exist", sourcePath), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Alert"), |
||||||
|
WARNING_MESSAGE); |
||||||
|
return StringUtils.EMPTY; |
||||||
|
} |
||||||
|
String name = getNoRepeatedName4Paste(targetDir, sourceFile.getName()); |
||||||
|
String targetFile = StableUtils.pathJoin(targetDir, name); |
||||||
|
if (sourceFile.isDirectory()) { |
||||||
|
copyDir(sourcePath, targetFile, withCopyVcs); |
||||||
|
} else { |
||||||
|
copyFile(sourcePath, targetFile, withCopyVcs); |
||||||
|
} |
||||||
|
return targetFile; |
||||||
|
} |
||||||
|
|
||||||
|
private void copyDir(String sourceDir, String targetDir, boolean withCopyVcs) { |
||||||
|
FileNode[] fileNodes = TemplateTreePane.getInstance().getTemplateFileTree().listFile(sourceDir); |
||||||
|
if (ArrayUtils.isEmpty(fileNodes)) { |
||||||
|
//空目录:相当于新建一个目录
|
||||||
|
if (!DesignerFrameFileDealerPane.getInstance().getSelectedOperation().mkdir(targetDir)) { |
||||||
|
throw new ResourceIOException(String.format("copy dir failed: from %s to %s.", sourceDir, targetDir)); |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
for (FileNode fileNode : fileNodes) { |
||||||
|
if (fileNode.isDirectory()) { |
||||||
|
copyDir(StableUtils.pathJoin(fileNode.getParent(), fileNode.getName()), StableUtils.pathJoin(targetDir, fileNode.getName()), withCopyVcs); |
||||||
|
} else { |
||||||
|
copyFile(StableUtils.pathJoin(sourceDir, fileNode.getName()), StableUtils.pathJoin(targetDir, fileNode.getName()), withCopyVcs); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void copyFile(String sourcePath, String targetPath, boolean withCopyVcs) { |
||||||
|
//检查源文件是不是还存在
|
||||||
|
if (!WorkContext.getWorkResource().exist(sourcePath)) { |
||||||
|
FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Source_File_Not_Exist", sourcePath), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Alert"), |
||||||
|
WARNING_MESSAGE); |
||||||
|
} else { |
||||||
|
if (!TemplateResourceManager.getResource().copy(sourcePath, targetPath)) { |
||||||
|
throw new ResourceIOException(String.format("copy file failed, from %s to %s", sourcePath, targetPath)); |
||||||
|
} else if (withCopyVcs){ |
||||||
|
sourcePath = sourcePath.replaceFirst(ProjectConstants.REPORTLETS_NAME, StringUtils.EMPTY); |
||||||
|
targetPath = targetPath.replaceFirst(ProjectConstants.REPORTLETS_NAME, StringUtils.EMPTY); |
||||||
|
VcsHelper.getInstance().moveVcs(sourcePath, targetPath); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 重名处理 |
||||||
|
* |
||||||
|
* @param targetDir |
||||||
|
* @param oldName |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private String getNoRepeatedName4Paste(String targetDir, String oldName) { |
||||||
|
while (isNameRepeaded(targetDir, oldName)) { |
||||||
|
int index = oldName.lastIndexOf("."); |
||||||
|
if (index > 0) { |
||||||
|
String oName = oldName.substring(0, index); |
||||||
|
oName = oName + Toolkit.i18nText("Fine-Design_Table_Data_Copy_Of_Table_Data"); |
||||||
|
oldName = oName.concat(oldName.substring(index)); |
||||||
|
} else { |
||||||
|
//目录重名
|
||||||
|
oldName = oldName + Toolkit.i18nText("Fine-Design_Table_Data_Copy_Of_Table_Data"); |
||||||
|
} |
||||||
|
} |
||||||
|
return oldName; |
||||||
|
} |
||||||
|
|
||||||
|
private boolean isNameRepeaded(String targetDir, String name) { |
||||||
|
FileNode[] fileNodes = TemplateTreePane.getInstance().getTemplateFileTree().listFile(targetDir); |
||||||
|
for (int i = 0; i < fileNodes.length; i++) { |
||||||
|
if (ComparatorUtils.equals(name, fileNodes[i].getName())) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,82 @@ |
|||||||
|
package com.fr.design.file; |
||||||
|
|
||||||
|
import com.fr.base.FRContext; |
||||||
|
import com.fr.design.gui.itree.filetree.TemplateDirTree; |
||||||
|
import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
|
import com.fr.design.mainframe.DesignerFrameFileDealerPane; |
||||||
|
import com.fr.design.mainframe.manager.search.searcher.control.pane.TemplateDirSearchRemindPane; |
||||||
|
import com.fr.file.filetree.IOFileNodeFilter; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.stable.ArrayUtils; |
||||||
|
|
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.tree.TreePath; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.Color; |
||||||
|
|
||||||
|
/** |
||||||
|
* 目录树面板 |
||||||
|
*/ |
||||||
|
public class TemplateDirTreePane extends JPanel { |
||||||
|
|
||||||
|
public static TemplateDirTreePane getInstance() { |
||||||
|
return TemplateDirTreePane.HOLDER.singleton; |
||||||
|
} |
||||||
|
|
||||||
|
private static class HOLDER { |
||||||
|
private static TemplateDirTreePane singleton = new TemplateDirTreePane(); |
||||||
|
} |
||||||
|
|
||||||
|
private TemplateDirTree templateDirTree; |
||||||
|
private TemplateDirSearchRemindPane remindPane; |
||||||
|
|
||||||
|
public TemplateDirTreePane() { |
||||||
|
this.setLayout(FRGUIPaneFactory.createBorderLayout()); |
||||||
|
this.setBorder(BorderFactory.createLineBorder(Color.gray)); |
||||||
|
templateDirTree = new TemplateDirTree(); |
||||||
|
remindPane = new TemplateDirSearchRemindPane(getTemplateDirTree()); |
||||||
|
|
||||||
|
this.add(remindPane, BorderLayout.CENTER); |
||||||
|
} |
||||||
|
|
||||||
|
public TemplateDirTree getTemplateDirTree() { |
||||||
|
return this.templateDirTree; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 判断所选的目录是否有权限 |
||||||
|
* |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public boolean selectedAccess() { |
||||||
|
TreePath[] selectedTreePaths = templateDirTree.getSelectionPaths(); |
||||||
|
|
||||||
|
if (ArrayUtils.isEmpty(selectedTreePaths)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
// 选中的是文件夹
|
||||||
|
TreePath treePath = selectedTreePaths[0]; |
||||||
|
ExpandMutableTreeNode currentTreeNode = (ExpandMutableTreeNode) treePath.getLastPathComponent(); |
||||||
|
return currentTreeNode != null && currentTreeNode.hasFullAuthority(); |
||||||
|
} |
||||||
|
|
||||||
|
public void refresh() { |
||||||
|
// 刷新远程文件夹权限
|
||||||
|
NodeAuthProcessor.getInstance().refresh(); |
||||||
|
templateDirTree.refresh(); |
||||||
|
DesignerFrameFileDealerPane.getInstance().refreshRightToolBarBy(null); |
||||||
|
FineLoggerFactory.getLogger().info(Toolkit.i18nText("Fine-Design_Basic_Template_File_Tree_Refresh_Successfully") + "!"); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 刷新 |
||||||
|
*/ |
||||||
|
public void refreshDockingView() { |
||||||
|
templateDirTree.setFileNodeFilter(new IOFileNodeFilter(FRContext.getFileNodes().getSupportedTypes())); |
||||||
|
templateDirTree.refreshEnv(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,53 @@ |
|||||||
|
package com.fr.design.gui.itree.filetree; |
||||||
|
|
||||||
|
import com.fr.base.FRContext; |
||||||
|
import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; |
||||||
|
import com.fr.design.mainframe.manager.search.TemplateDirTreeSearchManager; |
||||||
|
import com.fr.file.filetree.FileNode; |
||||||
|
|
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
/* |
||||||
|
* 目录树 |
||||||
|
*/ |
||||||
|
public class TemplateDirTree extends TemplateFileTree { |
||||||
|
|
||||||
|
public TemplateDirTree() { |
||||||
|
super(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 查找所有目录子节点 |
||||||
|
* |
||||||
|
* @param path |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public FileNode[] listFile(String path) { |
||||||
|
return Arrays.stream(FRContext.getFileNodes().list(path)).filter(FileNode::isDirectory).toArray(FileNode[]::new); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 搜索时刷新目录树 |
||||||
|
* @param root |
||||||
|
*/ |
||||||
|
public void refreshTreeNode4TreeSearch(ExpandMutableTreeNode root) { |
||||||
|
if (interceptRefresh(root)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
currentTreeMode.clear(); |
||||||
|
allTreeNode = TemplateDirTreeSearchManager.getInstance().allFileNodes().entrySet().stream().collect( |
||||||
|
Collectors.toMap(Map.Entry::getKey, entry -> fileNodeArray2TreeNodeArray(new FileNode[]{entry.getValue()})[0])); |
||||||
|
root.removeAllChildren(); |
||||||
|
|
||||||
|
FileNode[] treeNodes = TemplateDirTreeSearchManager.getInstance().matchesNode(); |
||||||
|
for (FileNode fileNode : treeNodes) { |
||||||
|
ExpandMutableTreeNode treeNode = fileNodeArray2TreeNodeArray(new FileNode[]{fileNode})[0]; |
||||||
|
addToTreeModel(root, treeNode); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,207 @@ |
|||||||
|
package com.fr.design.mainframe; |
||||||
|
|
||||||
|
import com.fr.design.DesignModelAdapter; |
||||||
|
import com.fr.design.designer.TargetComponent; |
||||||
|
import com.fr.design.gui.frpane.HyperlinkGroupPane; |
||||||
|
import com.fr.design.gui.frpane.HyperlinkGroupPaneActionProvider; |
||||||
|
import com.fr.design.gui.imenu.UIMenuItem; |
||||||
|
import com.fr.design.mainframe.template.info.TemplateProcessInfo; |
||||||
|
import com.fr.design.menu.ShortCut; |
||||||
|
import com.fr.design.menu.ToolBarDef; |
||||||
|
|
||||||
|
import javax.swing.Icon; |
||||||
|
import javax.swing.JComponent; |
||||||
|
import javax.swing.JPanel; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/08/09 |
||||||
|
**/ |
||||||
|
public class JNullTemplate extends JTemplate { |
||||||
|
|
||||||
|
public static final JTemplate NULL = new JNullTemplate(); |
||||||
|
|
||||||
|
@Override |
||||||
|
public void copy() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean paste() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean cut() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public AuthorityEditPane createAuthorityEditPane() { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public JPanel getEastUpPane() { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public JPanel getEastDownPane() { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public ToolBarDef[] toolbars4Target() { |
||||||
|
return new ToolBarDef[0]; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public JComponent[] toolBarButton4Form() { |
||||||
|
return new JComponent[0]; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void refreshEastPropertiesPane() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public TargetComponent getCurrentElementCasePane() { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public JComponent getCurrentReportComponentPane() { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public TemplateProcessInfo getProcessInfo() { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setJTemplateResolution(int resolution) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getJTemplateResolution() { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected JComponent createCenterPane() { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeTemplateSelection() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void refreshContainer() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeParameterPaneSelection() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setScale(int resolution) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getScale() { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int selfAdaptUpdate() { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected DesignModelAdapter createDesignModel() { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public UIMenuItem[] createMenuItem4Preview() { |
||||||
|
return new UIMenuItem[0]; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected BaseUndoState<?> createUndoState() { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void applyUndoState(BaseUndoState baseUndoState) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String suffix() { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public ShortCut[] shortcut4TemplateMenu() { |
||||||
|
return new ShortCut[0]; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public ShortCut[] shortCuts4Authority() { |
||||||
|
return new ShortCut[0]; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isJWorkBook() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public HyperlinkGroupPane getHyperLinkPane(HyperlinkGroupPaneActionProvider hyperlinkGroupPaneActionProvider) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public HyperlinkGroupPane getHyperLinkPaneNoPop(HyperlinkGroupPaneActionProvider hyperlinkGroupPaneActionProvider) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setAuthorityMode(boolean isUpMode) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Icon getIcon() { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String route() { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public JPanel[] toolbarPanes4Form() { |
||||||
|
return new JPanel[0]; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public JComponent toolBar4Authority() { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getToolBarHeight() { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,66 @@ |
|||||||
|
package com.fr.design.mainframe.manager.clip; |
||||||
|
|
||||||
|
import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* 模板复制黏贴 |
||||||
|
*/ |
||||||
|
public class TemplateTreeClipboard { |
||||||
|
|
||||||
|
private List<ExpandMutableTreeNode> clip = new ArrayList<>(); |
||||||
|
|
||||||
|
private static class Holder { |
||||||
|
private static final TemplateTreeClipboard INSTANCE = new TemplateTreeClipboard(); |
||||||
|
} |
||||||
|
|
||||||
|
private TemplateTreeClipboard() { |
||||||
|
} |
||||||
|
|
||||||
|
public static TemplateTreeClipboard getInstance() { |
||||||
|
return TemplateTreeClipboard.Holder.INSTANCE; |
||||||
|
} |
||||||
|
|
||||||
|
public List<ExpandMutableTreeNode> transferNameObjectArray2Map(ExpandMutableTreeNode[] selectedTreeNodes) { |
||||||
|
List<ExpandMutableTreeNode> resultMap = new ArrayList<>(); |
||||||
|
if (selectedTreeNodes == null) { |
||||||
|
return resultMap; |
||||||
|
} |
||||||
|
for (ExpandMutableTreeNode selectTreeNode : selectedTreeNodes) { |
||||||
|
ExpandMutableTreeNode cloned = (ExpandMutableTreeNode) selectTreeNode.clone(); |
||||||
|
if (cloned != null) { |
||||||
|
resultMap.add(cloned); |
||||||
|
} |
||||||
|
} |
||||||
|
return resultMap; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 添加选中的模板数据到剪切板,覆盖原本剪切板内数据 |
||||||
|
* |
||||||
|
* @param copyList |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public void addToClip(List<ExpandMutableTreeNode> copyList) { |
||||||
|
this.clip = copyList; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 取出剪切板内的所有模板数据,剪切板不清空 |
||||||
|
* |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public List<ExpandMutableTreeNode> takeFromClip() { |
||||||
|
return clip; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 清空剪切板 |
||||||
|
*/ |
||||||
|
public void reset() { |
||||||
|
clip.clear(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,205 @@ |
|||||||
|
package com.fr.design.mainframe.manager.search; |
||||||
|
|
||||||
|
import com.fr.design.file.TemplateDirTreePane; |
||||||
|
import com.fr.design.gui.itree.filetree.TemplateDirTree; |
||||||
|
import com.fr.design.mainframe.manager.search.searcher.TemplateDirTreeSearcher; |
||||||
|
import com.fr.design.mainframe.manager.search.searcher.TemplateTreeSearcher; |
||||||
|
import com.fr.design.search.TreeSearchStatus; |
||||||
|
import com.fr.design.search.event.TreeSearchStatusChangeEvent; |
||||||
|
import com.fr.design.search.event.TreeSearchStatusChangeListener; |
||||||
|
import com.fr.design.search.view.TreeSearchRendererHelper; |
||||||
|
import com.fr.file.filetree.FileNode; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
|
||||||
|
import javax.swing.SwingUtilities; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
public class TemplateDirTreeSearchManager { |
||||||
|
|
||||||
|
/** |
||||||
|
* 文件树搜索器 |
||||||
|
*/ |
||||||
|
private TemplateTreeSearcher treeSearcher; |
||||||
|
|
||||||
|
/** |
||||||
|
* 搜索任务的状态 |
||||||
|
*/ |
||||||
|
private TreeSearchStatus treeSearchStatus; |
||||||
|
|
||||||
|
/** |
||||||
|
* 缓存上次搜索文本,避免重复搜索 |
||||||
|
*/ |
||||||
|
private String lastSearchText; |
||||||
|
|
||||||
|
/** |
||||||
|
* 存储与复原 原本模板的UI |
||||||
|
*/ |
||||||
|
private TreeSearchRendererHelper rendererHelper; |
||||||
|
|
||||||
|
/** |
||||||
|
* 搜索状态变化监听 |
||||||
|
*/ |
||||||
|
private List<TreeSearchStatusChangeListener> listeners = new ArrayList<>(); |
||||||
|
|
||||||
|
private TemplateDirTreeSearchManager() { |
||||||
|
init(); |
||||||
|
} |
||||||
|
|
||||||
|
private void init() { |
||||||
|
this.treeSearchStatus = TreeSearchStatus.NOT_IN_SEARCH_MODE; |
||||||
|
} |
||||||
|
|
||||||
|
private static class Holder { |
||||||
|
private static final TemplateDirTreeSearchManager INSTANCE = new TemplateDirTreeSearchManager(); |
||||||
|
} |
||||||
|
|
||||||
|
public static TemplateDirTreeSearchManager getInstance() { |
||||||
|
return TemplateDirTreeSearchManager.Holder.INSTANCE; |
||||||
|
} |
||||||
|
|
||||||
|
public TreeSearchStatus getTreeSearchStatus() { |
||||||
|
return treeSearchStatus; |
||||||
|
} |
||||||
|
|
||||||
|
public void setTreeSearchStatus(TreeSearchStatus treeSearchStatus) { |
||||||
|
this.treeSearchStatus = treeSearchStatus; |
||||||
|
// 每次设置搜索状态,都触发下监听,让页面跟随变化
|
||||||
|
SwingUtilities.invokeLater(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
for (TreeSearchStatusChangeListener listener : listeners) { |
||||||
|
listener.updateTreeSearchChange(new TreeSearchStatusChangeEvent(treeSearchStatus)); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
public void registerTreeSearchStatusChangeListener(TreeSearchStatusChangeListener listener) { |
||||||
|
listeners.add(listener); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取当前的目录树 |
||||||
|
* |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private TemplateDirTree getCurrentTemplateDirTree() { |
||||||
|
return TemplateDirTreePane.getInstance().getTemplateDirTree(); |
||||||
|
} |
||||||
|
|
||||||
|
public void beforeSearch(TemplateDirTree templateDirTree) { |
||||||
|
setTreeSearchStatus(TreeSearchStatus.SEARCH_NOT_BEGIN); |
||||||
|
rendererHelper = new TreeSearchRendererHelper(); |
||||||
|
rendererHelper.save(templateDirTree.getFileTreeCellRenderer()); |
||||||
|
treeSearcher = new TemplateDirTreeSearcher(); |
||||||
|
FineLoggerFactory.getLogger().debug("switch to template dir search"); |
||||||
|
treeSearcher.beforeSearch(templateDirTree); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 开始搜索 |
||||||
|
* |
||||||
|
* @param searchText |
||||||
|
*/ |
||||||
|
public void startSearch(String searchText) { |
||||||
|
if (isRepeatSearch(searchText) || StringUtils.isEmpty(searchText)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
setTreeSearchStatus(TreeSearchStatus.SEARCHING); |
||||||
|
rendererHelper.replaceTreeRenderer(getCurrentTemplateDirTree(), searchText); |
||||||
|
FineLoggerFactory.getLogger().debug("start template dir search for search text: {}", searchText); |
||||||
|
treeSearcher.startSearch(searchText); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 中断搜索 |
||||||
|
*/ |
||||||
|
public void stopSearch() { |
||||||
|
setTreeSearchStatus(TreeSearchStatus.SEARCH_STOPPED); |
||||||
|
FineLoggerFactory.getLogger().debug("stop template dir search for search text: {}", lastSearchText); |
||||||
|
treeSearcher.stopSearch(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 搜索完成 |
||||||
|
*/ |
||||||
|
public void completeSearch() { |
||||||
|
setTreeSearchStatus(TreeSearchStatus.SEARCH_COMPLETED); |
||||||
|
FineLoggerFactory.getLogger().debug("complete template dir search for search text: {}", lastSearchText); |
||||||
|
treeSearcher.completeSearch(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 退出搜索模式 |
||||||
|
*/ |
||||||
|
public void outOfSearchMode() { |
||||||
|
setTreeSearchStatus(TreeSearchStatus.NOT_IN_SEARCH_MODE); |
||||||
|
FineLoggerFactory.getLogger().info("out of template search"); |
||||||
|
lastSearchText = null; |
||||||
|
if (treeSearcher != null) { |
||||||
|
treeSearcher.exitSearch(); |
||||||
|
} |
||||||
|
if (rendererHelper != null) { |
||||||
|
rendererHelper.restore(getCurrentTemplateDirTree()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 所有匹配的目录节点 |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public FileNode[] matchesNode() { |
||||||
|
return treeSearcher.getMatchSets().toArray(new FileNode[0]); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 当前目录树中搜索的目录节点 |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public Map<String, FileNode> allFileNodes() { |
||||||
|
return treeSearcher.getAllTemplates(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 搜索结果是否为空 |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public boolean isMatchSetsEmpty() { |
||||||
|
return treeSearcher.isMatchSetsEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 恢复到模板树面板 |
||||||
|
*/ |
||||||
|
public void restoreTreePane() { |
||||||
|
setTreeSearchStatus(TreeSearchStatus.SEARCH_NOT_BEGIN); |
||||||
|
lastSearchText = null; |
||||||
|
if (rendererHelper != null) { |
||||||
|
rendererHelper.restore(getCurrentTemplateDirTree()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 刷新树,更新搜索的结果 |
||||||
|
*/ |
||||||
|
public void updateTemplateTree() { |
||||||
|
getCurrentTemplateDirTree().refresh4TreeSearch(); |
||||||
|
} |
||||||
|
|
||||||
|
private boolean isRepeatSearch(String searchText) { |
||||||
|
boolean repeat = StringUtils.equals(lastSearchText, searchText); |
||||||
|
lastSearchText = searchText; |
||||||
|
return repeat; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 当前是否处于搜索模式 |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public boolean isInSearchMode() { |
||||||
|
return getTreeSearchStatus() != TreeSearchStatus.NOT_IN_SEARCH_MODE; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,258 @@ |
|||||||
|
package com.fr.design.mainframe.manager.search; |
||||||
|
|
||||||
|
import com.fr.design.search.event.TreeSearchStatusChangeEvent; |
||||||
|
import com.fr.design.search.event.TreeSearchStatusChangeListener; |
||||||
|
import com.fr.design.search.TreeSearchStatus; |
||||||
|
import com.fr.design.search.view.TreeSearchRendererHelper; |
||||||
|
import com.fr.design.file.TemplateTreePane; |
||||||
|
import com.fr.design.gui.itree.filetree.TemplateFileTree; |
||||||
|
import com.fr.design.mainframe.manager.search.searcher.TemplateTreeSearcher; |
||||||
|
import com.fr.file.filetree.FileNode; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
|
||||||
|
import javax.swing.SwingUtilities; |
||||||
|
import javax.swing.SwingWorker; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Set; |
||||||
|
import java.util.concurrent.atomic.AtomicBoolean; |
||||||
|
|
||||||
|
/** |
||||||
|
* 文件树搜索管理器 |
||||||
|
*/ |
||||||
|
public class TemplateTreeSearchManager { |
||||||
|
|
||||||
|
/** |
||||||
|
* 文件树搜索器 |
||||||
|
*/ |
||||||
|
private TemplateTreeSearcher treeSearcher; |
||||||
|
|
||||||
|
/** |
||||||
|
* 搜索任务的状态 |
||||||
|
*/ |
||||||
|
private TreeSearchStatus treeSearchStatus; |
||||||
|
|
||||||
|
/** |
||||||
|
* 缓存上次搜索文本,避免重复搜索 |
||||||
|
*/ |
||||||
|
private String lastSearchText; |
||||||
|
|
||||||
|
/** |
||||||
|
* 存储与复原 原本模板的UI |
||||||
|
*/ |
||||||
|
private TreeSearchRendererHelper rendererHelper; |
||||||
|
|
||||||
|
/** |
||||||
|
* 是否在更新搜索结果的标识 |
||||||
|
*/ |
||||||
|
private AtomicBoolean isRefreshing = new AtomicBoolean(false); |
||||||
|
|
||||||
|
/** |
||||||
|
* 搜索状态变化监听 |
||||||
|
*/ |
||||||
|
private List<TreeSearchStatusChangeListener> listeners = new ArrayList<>(); |
||||||
|
|
||||||
|
private TemplateTreeSearchManager() { |
||||||
|
init(); |
||||||
|
} |
||||||
|
|
||||||
|
private void init() { |
||||||
|
this.treeSearchStatus = TreeSearchStatus.NOT_IN_SEARCH_MODE; |
||||||
|
} |
||||||
|
|
||||||
|
private static class Holder { |
||||||
|
private static final TemplateTreeSearchManager INSTANCE = new TemplateTreeSearchManager(); |
||||||
|
} |
||||||
|
|
||||||
|
public static TemplateTreeSearchManager getInstance() { |
||||||
|
return TemplateTreeSearchManager.Holder.INSTANCE; |
||||||
|
} |
||||||
|
|
||||||
|
public TreeSearchStatus getTreeSearchStatus() { |
||||||
|
return treeSearchStatus; |
||||||
|
} |
||||||
|
|
||||||
|
public void setTreeSearchStatus(TreeSearchStatus treeSearchStatus) { |
||||||
|
this.treeSearchStatus = treeSearchStatus; |
||||||
|
// 每次设置搜索状态,都触发下监听,让页面跟随变化
|
||||||
|
SwingUtilities.invokeLater(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
for (TreeSearchStatusChangeListener listener : listeners) { |
||||||
|
listener.updateTreeSearchChange(new TreeSearchStatusChangeEvent(treeSearchStatus)); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
public void registerTreeSearchStatusChangeListener(TreeSearchStatusChangeListener listener) { |
||||||
|
listeners.add(listener); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 工具栏处切换到搜索面板 |
||||||
|
* |
||||||
|
* @param templateFileTree |
||||||
|
*/ |
||||||
|
public void switchToSearch(TemplateFileTree templateFileTree) { |
||||||
|
setTreeSearchStatus(TreeSearchStatus.SEARCH_NOT_BEGIN); |
||||||
|
rendererHelper = new TreeSearchRendererHelper(); |
||||||
|
rendererHelper.save(templateFileTree.getFileTreeCellRenderer()); |
||||||
|
treeSearcher = new TemplateTreeSearcher(); |
||||||
|
FineLoggerFactory.getLogger().debug("switch to template search"); |
||||||
|
treeSearcher.beforeSearch(templateFileTree); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取当前的模板树 |
||||||
|
* |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private TemplateFileTree getCurrentTemplateTree() { |
||||||
|
return TemplateTreePane.getInstance().getTemplateFileTree(); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isMatchSetsEmpty() { |
||||||
|
return treeSearcher.isMatchSetsEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 开始搜索 |
||||||
|
* |
||||||
|
* @param searchText |
||||||
|
*/ |
||||||
|
public void startSearch(String searchText) { |
||||||
|
if (isRepeatSearch(searchText) || StringUtils.isEmpty(searchText)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
setTreeSearchStatus(TreeSearchStatus.SEARCHING); |
||||||
|
rendererHelper.replaceTreeRenderer(getCurrentTemplateTree(), searchText); |
||||||
|
FineLoggerFactory.getLogger().debug("start template search for search text: {}", searchText); |
||||||
|
new SwingWorker<Void, Void>() { |
||||||
|
|
||||||
|
@Override |
||||||
|
protected Void doInBackground() throws Exception { |
||||||
|
treeSearcher.startSearch(searchText); |
||||||
|
return null; |
||||||
|
} |
||||||
|
}.execute(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 中断搜索 |
||||||
|
*/ |
||||||
|
public void stopSearch() { |
||||||
|
setTreeSearchStatus(TreeSearchStatus.SEARCH_STOPPED); |
||||||
|
FineLoggerFactory.getLogger().debug("stop template search for search text: {}", lastSearchText); |
||||||
|
treeSearcher.stopSearch(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 搜索完成 |
||||||
|
*/ |
||||||
|
public void completeSearch() { |
||||||
|
setTreeSearchStatus(TreeSearchStatus.SEARCH_COMPLETED); |
||||||
|
setRefreshing(false); |
||||||
|
FineLoggerFactory.getLogger().debug("complete template search for search text: {}", lastSearchText); |
||||||
|
treeSearcher.completeSearch(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 刷新树,更新搜索的结果 |
||||||
|
*/ |
||||||
|
public void updateTemplateTree() { |
||||||
|
getCurrentTemplateTree().refresh4TreeSearch(); |
||||||
|
} |
||||||
|
|
||||||
|
private boolean isRepeatSearch(String searchText) { |
||||||
|
boolean repeat = StringUtils.equals(lastSearchText, searchText); |
||||||
|
lastSearchText = searchText; |
||||||
|
return repeat; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 切换回工具栏,恢复数据集树UI |
||||||
|
*/ |
||||||
|
public void restoreToolBarAndTreePane() { |
||||||
|
setTreeSearchStatus(TreeSearchStatus.NOT_IN_SEARCH_MODE); |
||||||
|
FineLoggerFactory.getLogger().info("out of template search"); |
||||||
|
if (treeSearcher != null) { |
||||||
|
treeSearcher.exitSearch(); |
||||||
|
} |
||||||
|
setRefreshing(false); |
||||||
|
lastSearchText = null; |
||||||
|
if (rendererHelper != null) { |
||||||
|
rendererHelper.restore(getCurrentTemplateTree()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 恢复文件树UI |
||||||
|
*/ |
||||||
|
public void restoreTreePane() { |
||||||
|
setTreeSearchStatus(TreeSearchStatus.SEARCH_NOT_BEGIN); |
||||||
|
lastSearchText = null; |
||||||
|
if (rendererHelper != null) { |
||||||
|
rendererHelper.restore(getCurrentTemplateTree()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 所有匹配的目录节点 |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public FileNode[] matchesNode() { |
||||||
|
if (treeSearcher.getMatchSets().size() == 0) { |
||||||
|
return new FileNode[0]; |
||||||
|
} |
||||||
|
return treeSearcher.getMatchSets().toArray(new FileNode[0]); |
||||||
|
} |
||||||
|
|
||||||
|
public void deleteMatchedNode(Set<FileNode> fileNodes) { |
||||||
|
Set<FileNode> deleteNode = new HashSet<>(); |
||||||
|
for (FileNode matchedNode : treeSearcher.getMatchSets()) { |
||||||
|
if (fileNodes.stream().anyMatch(fileNode -> fileNode.getEnvPath().equals(matchedNode.getEnvPath()))) { |
||||||
|
deleteNode.add(matchedNode); |
||||||
|
} |
||||||
|
} |
||||||
|
treeSearcher.getMatchSets().removeAll(deleteNode); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 当前模板树中的所有模板节点 |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public Map<String, FileNode> allFileNodes() { |
||||||
|
return treeSearcher.getAllTemplates(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 是否处于搜索模式 |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public boolean isInSearchMode() { |
||||||
|
return getTreeSearchStatus() != TreeSearchStatus.NOT_IN_SEARCH_MODE; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 退出搜索模式 |
||||||
|
*/ |
||||||
|
public void outOfSearchMode() { |
||||||
|
restoreToolBarAndTreePane(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 搜索结果数是否处于更新状态 |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public boolean isRefreshing() { |
||||||
|
return isRefreshing.get(); |
||||||
|
} |
||||||
|
|
||||||
|
public void setRefreshing(boolean isRefreshing) { |
||||||
|
this.isRefreshing.set(isRefreshing); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,195 @@ |
|||||||
|
package com.fr.design.mainframe.manager.search.searcher; |
||||||
|
|
||||||
|
import com.fr.concurrent.NamedThreadFactory; |
||||||
|
import com.fr.design.gui.itree.filetree.TemplateFileTree; |
||||||
|
import com.fr.design.mainframe.manager.search.TemplateDirTreeSearchManager; |
||||||
|
import com.fr.design.mainframe.manager.search.searcher.control.common.TemplateDirSearchCallBack; |
||||||
|
import com.fr.design.mainframe.manager.search.searcher.control.common.TemplateSearchTask; |
||||||
|
import com.fr.design.mainframe.manager.search.searcher.control.pre.TemplateDirPreSearchTask; |
||||||
|
import com.fr.design.mainframe.manager.search.searcher.control.pre.TemplatePreSearchCallBack; |
||||||
|
import com.fr.design.search.TreeSearchStatus; |
||||||
|
import com.fr.file.filetree.FileNode; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.stable.project.ProjectConstants; |
||||||
|
|
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Set; |
||||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||||
|
import java.util.concurrent.ExecutorService; |
||||||
|
import java.util.concurrent.Executors; |
||||||
|
import java.util.concurrent.atomic.AtomicInteger; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
/** |
||||||
|
* 目录搜索器 |
||||||
|
*/ |
||||||
|
public class TemplateDirTreeSearcher extends TemplateTreeSearcher { |
||||||
|
|
||||||
|
private ExecutorService executorService; |
||||||
|
|
||||||
|
private final Map<String, FileNode> allDirs = new ConcurrentHashMap<>(); |
||||||
|
|
||||||
|
private final AtomicInteger outLayerDirCount = new AtomicInteger(0); |
||||||
|
|
||||||
|
private final Set<String> notCalculatedSets = new HashSet<>(); |
||||||
|
|
||||||
|
private final Set<String> calculatedSets = new HashSet<>(); |
||||||
|
|
||||||
|
private final Set<FileNode> matchSets = new HashSet<>(); |
||||||
|
|
||||||
|
private final Object lock = new Object(); |
||||||
|
|
||||||
|
public Set<FileNode> getMatchSets() { |
||||||
|
return this.matchSets; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isMatchSetsEmpty() { |
||||||
|
return matchSets.isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
public int getAllDirSize() { |
||||||
|
return allDirs.size(); |
||||||
|
} |
||||||
|
|
||||||
|
public Map<String, FileNode> getAllTemplates() { |
||||||
|
return allDirs; |
||||||
|
} |
||||||
|
|
||||||
|
public int getCalculatedCount() { |
||||||
|
return this.calculatedSets.size(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 将模板添加到已经计算过的集合中,不管模板是不是匹配 |
||||||
|
* |
||||||
|
* @param templateNames |
||||||
|
*/ |
||||||
|
public synchronized void addToCalculatedSets(List<String> templateNames) { |
||||||
|
for (String templateName : templateNames) { |
||||||
|
FileNode fileNode = allDirs.get(templateName); |
||||||
|
if (fileNode == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
calculatedSets.add(templateName); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 将搜索匹配的目录添加到匹配的集合中 |
||||||
|
* |
||||||
|
* @param matchNodes |
||||||
|
*/ |
||||||
|
public synchronized void addToMatchSets(List<FileNode> matchNodes) { |
||||||
|
matchSets.addAll(matchNodes); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 将目录添加到未计算的集合中 |
||||||
|
* |
||||||
|
* @param fileNodes |
||||||
|
*/ |
||||||
|
public synchronized void addToNotCalculatedSets(List<FileNode> fileNodes) { |
||||||
|
synchronized (lock) { |
||||||
|
Map<String, FileNode> chileMap = fileNodes.stream().collect(Collectors.toMap(FileNode::getEnvPath, treeNode -> treeNode)); |
||||||
|
notCalculatedSets.addAll(chileMap.keySet()); |
||||||
|
allDirs.putAll(chileMap); |
||||||
|
outLayerDirCount.decrementAndGet(); |
||||||
|
lock.notify(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 正式搜索前,预加载一下每个最外层目录里面的所有子节点 |
||||||
|
* |
||||||
|
*/ |
||||||
|
public void beforeSearch(TemplateFileTree templateFileTree) { |
||||||
|
executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new NamedThreadFactory(TemplateDirTreeSearcher.class)); |
||||||
|
collectOutLayerTemplate(templateFileTree); |
||||||
|
preCalculateChild(); |
||||||
|
} |
||||||
|
|
||||||
|
protected void preCalculateChild() { |
||||||
|
for (String notCalculatedNode : notCalculatedSets) { |
||||||
|
FileNode fileNode = allDirs.get(notCalculatedNode); |
||||||
|
//计算最外层目录下的所有子节点
|
||||||
|
if (TemplateDirTreeSearchManager.getInstance().getTreeSearchStatus() == TreeSearchStatus.SEARCH_NOT_BEGIN && fileNode.isDirectory()) { |
||||||
|
executorService.execute(new TemplateDirPreSearchTask(new TemplatePreSearchCallBack(this), fileNode)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 收集下此次搜索需要进行取数最外层的FileNode |
||||||
|
* |
||||||
|
* @param templateFileTree |
||||||
|
*/ |
||||||
|
private void collectOutLayerTemplate(TemplateFileTree templateFileTree) { |
||||||
|
FileNode[] fileNodes = templateFileTree.listFile(ProjectConstants.REPORTLETS_NAME); |
||||||
|
Map<String, FileNode> fileNodeMap = Arrays.stream(fileNodes).collect(Collectors.toMap(FileNode::getEnvPath, treeNode -> treeNode)); |
||||||
|
outLayerDirCount.addAndGet(fileNodes.length); |
||||||
|
notCalculatedSets.addAll(fileNodeMap.keySet()); |
||||||
|
allDirs.putAll(fileNodeMap); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 开始搜索 |
||||||
|
* |
||||||
|
* @param searchText |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void startSearch(String searchText) { |
||||||
|
reset(); |
||||||
|
do { |
||||||
|
synchronized (lock) { |
||||||
|
if (notCalculatedSets.isEmpty()) { |
||||||
|
try { |
||||||
|
lock.wait(100); |
||||||
|
} catch (InterruptedException e) { |
||||||
|
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||||
|
} |
||||||
|
} |
||||||
|
for (String notCalculated : notCalculatedSets) { |
||||||
|
FileNode fileNode = allDirs.get(notCalculated); |
||||||
|
if (TemplateDirTreeSearchManager.getInstance().getTreeSearchStatus() == TreeSearchStatus.SEARCHING) { |
||||||
|
executorService.execute(new TemplateSearchTask(searchText, fileNode, new TemplateDirSearchCallBack(this))); |
||||||
|
} |
||||||
|
} |
||||||
|
FineLoggerFactory.getLogger().info("[Template Search] At this stage calculate size: {}.", notCalculatedSets.size()); |
||||||
|
notCalculatedSets.clear(); |
||||||
|
} |
||||||
|
} while (outLayerDirCount.get() != 0); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 停止搜索 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void stopSearch() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 完成搜索 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void completeSearch() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private void reset() { |
||||||
|
matchSets.clear(); |
||||||
|
calculatedSets.clear(); |
||||||
|
notCalculatedSets.addAll(allDirs.keySet()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 退出搜索时的处理 |
||||||
|
*/ |
||||||
|
public void exitSearch() { |
||||||
|
allDirs.clear(); |
||||||
|
executorService.shutdownNow(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,193 @@ |
|||||||
|
package com.fr.design.mainframe.manager.search.searcher; |
||||||
|
|
||||||
|
import com.fr.concurrent.NamedThreadFactory; |
||||||
|
import com.fr.design.mainframe.manager.search.TemplateTreeSearchManager; |
||||||
|
import com.fr.design.mainframe.manager.search.searcher.control.common.TemplateSearchCallBack; |
||||||
|
import com.fr.design.mainframe.manager.search.searcher.control.common.TemplateSearchTask; |
||||||
|
import com.fr.design.mainframe.manager.search.searcher.control.pre.TemplatePreSearchCallBack; |
||||||
|
import com.fr.design.mainframe.manager.search.searcher.control.pre.TemplatePreSearchTask; |
||||||
|
import com.fr.design.search.TreeSearchStatus; |
||||||
|
import com.fr.design.search.TreeSearcher; |
||||||
|
import com.fr.design.gui.itree.filetree.TemplateFileTree; |
||||||
|
import com.fr.file.filetree.FileNode; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.stable.project.ProjectConstants; |
||||||
|
|
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Set; |
||||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||||
|
import java.util.concurrent.Executors; |
||||||
|
import java.util.concurrent.ExecutorService; |
||||||
|
import java.util.concurrent.atomic.AtomicInteger; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
public class TemplateTreeSearcher implements TreeSearcher { |
||||||
|
|
||||||
|
private ExecutorService executorService; |
||||||
|
|
||||||
|
private final Map<String, FileNode> allTemplates = new ConcurrentHashMap<>(); |
||||||
|
|
||||||
|
private final Object lock = new Object(); |
||||||
|
|
||||||
|
private final AtomicInteger outLayerDirCount = new AtomicInteger(0); |
||||||
|
|
||||||
|
private final Set<String> calculatedSets = new HashSet<>(); |
||||||
|
|
||||||
|
private final Set<String> notCalculatedSets = new HashSet<>(); |
||||||
|
|
||||||
|
private final Set<FileNode> matchSets = new HashSet<>(); |
||||||
|
|
||||||
|
public boolean isMatchSetsEmpty() { |
||||||
|
return matchSets.isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
public int getAllTemplateSize() { |
||||||
|
return allTemplates.size(); |
||||||
|
} |
||||||
|
|
||||||
|
public Map<String, FileNode> getAllTemplates() { |
||||||
|
return allTemplates; |
||||||
|
} |
||||||
|
|
||||||
|
public Set<FileNode> getMatchSets() { |
||||||
|
return this.matchSets; |
||||||
|
} |
||||||
|
|
||||||
|
public int getCalculatedCount() { |
||||||
|
return this.calculatedSets.size(); |
||||||
|
} |
||||||
|
|
||||||
|
public int getOutLayerCount() { |
||||||
|
return this.outLayerDirCount.get(); |
||||||
|
} |
||||||
|
|
||||||
|
public synchronized void addToCalculatedSets(List<String> templateNames) { |
||||||
|
for (String templateName : templateNames) { |
||||||
|
FileNode fileNode = allTemplates.get(templateName); |
||||||
|
if (fileNode == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
calculatedSets.add(templateName); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 将搜索匹配的模板添加到匹配的集合中 |
||||||
|
* |
||||||
|
* @param matchNodes |
||||||
|
*/ |
||||||
|
public synchronized void addToMatchSets(List<FileNode> matchNodes) { |
||||||
|
matchSets.addAll(matchNodes); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 将模板添加到未计算的集合中 |
||||||
|
* |
||||||
|
* @param fileNodes |
||||||
|
*/ |
||||||
|
public synchronized void addToNotCalculatedSets(List<FileNode> fileNodes) { |
||||||
|
synchronized (lock) { |
||||||
|
Map<String, FileNode> chileMap = fileNodes.stream().collect(Collectors.toMap(FileNode::getEnvPath, treeNode -> treeNode)); |
||||||
|
notCalculatedSets.addAll(chileMap.keySet()); |
||||||
|
allTemplates.putAll(chileMap); |
||||||
|
outLayerDirCount.decrementAndGet(); |
||||||
|
lock.notify(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 正式搜索前,预加载一下每个最外层目录里面的所有子节点 |
||||||
|
* |
||||||
|
*/ |
||||||
|
public void beforeSearch(TemplateFileTree templateFileTree) { |
||||||
|
executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new NamedThreadFactory(TemplateTreeSearcher.class)); |
||||||
|
collectOutLayerTemplate(templateFileTree); |
||||||
|
preCalculateChild(); |
||||||
|
} |
||||||
|
|
||||||
|
protected void preCalculateChild() { |
||||||
|
for (String notCalculatedNode : notCalculatedSets) { |
||||||
|
FileNode fileNode = allTemplates.get(notCalculatedNode); |
||||||
|
//计算最外层目录下的所有子节点
|
||||||
|
if (TemplateTreeSearchManager.getInstance().getTreeSearchStatus() == TreeSearchStatus.SEARCH_NOT_BEGIN && fileNode.isDirectory()) { |
||||||
|
executorService.execute(new TemplatePreSearchTask(new TemplatePreSearchCallBack(this), fileNode)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 收集下此次搜索需要进行取数最外层的FileNode |
||||||
|
* |
||||||
|
* @param templateFileTree |
||||||
|
*/ |
||||||
|
private void collectOutLayerTemplate(TemplateFileTree templateFileTree) { |
||||||
|
FileNode[] fileNodes = templateFileTree.listFile(ProjectConstants.REPORTLETS_NAME); |
||||||
|
Map<String, FileNode> fileNodeMap = Arrays.stream(fileNodes).collect(Collectors.toMap(FileNode::getEnvPath, treeNode -> treeNode)); |
||||||
|
long dirCount = Arrays.stream(fileNodes).filter(FileNode::isDirectory).count(); |
||||||
|
outLayerDirCount.addAndGet((int) dirCount); |
||||||
|
notCalculatedSets.addAll(fileNodeMap.keySet()); |
||||||
|
allTemplates.putAll(fileNodeMap); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 开始搜索 |
||||||
|
* |
||||||
|
* @param searchText |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void startSearch(String searchText) { |
||||||
|
reset(); |
||||||
|
do { |
||||||
|
synchronized (lock) { |
||||||
|
if (notCalculatedSets.isEmpty()) { |
||||||
|
try { |
||||||
|
lock.wait(100); |
||||||
|
} catch (InterruptedException e) { |
||||||
|
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||||
|
} |
||||||
|
} |
||||||
|
for (String notCalculated : notCalculatedSets) { |
||||||
|
FileNode fileNode = allTemplates.get(notCalculated); |
||||||
|
if (TemplateTreeSearchManager.getInstance().getTreeSearchStatus() == TreeSearchStatus.SEARCHING) { |
||||||
|
executorService.execute(new TemplateSearchTask(searchText, fileNode, new TemplateSearchCallBack(this))); |
||||||
|
} |
||||||
|
} |
||||||
|
FineLoggerFactory.getLogger().info("[Template Search] At this stage calculate size: {}.", notCalculatedSets.size()); |
||||||
|
notCalculatedSets.clear(); |
||||||
|
} |
||||||
|
} while (outLayerDirCount.get() != 0); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 停止搜索 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void stopSearch() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 完成搜索 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void completeSearch() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private void reset() { |
||||||
|
matchSets.clear(); |
||||||
|
calculatedSets.clear(); |
||||||
|
notCalculatedSets.addAll(allTemplates.keySet()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 退出搜索模式 |
||||||
|
*/ |
||||||
|
public void exitSearch() { |
||||||
|
allTemplates.clear(); |
||||||
|
executorService.shutdownNow(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,53 @@ |
|||||||
|
package com.fr.design.mainframe.manager.search.searcher.control.common; |
||||||
|
|
||||||
|
import com.fr.design.mainframe.manager.search.TemplateDirTreeSearchManager; |
||||||
|
import com.fr.design.mainframe.manager.search.searcher.TemplateDirTreeSearcher; |
||||||
|
import com.fr.design.search.TreeSearchStatus; |
||||||
|
import com.fr.design.search.control.TreeSearchCallback; |
||||||
|
import com.fr.design.search.control.TreeSearchResult; |
||||||
|
|
||||||
|
import javax.swing.SwingUtilities; |
||||||
|
|
||||||
|
/** |
||||||
|
* 目录树搜索回调处理 |
||||||
|
*/ |
||||||
|
public class TemplateDirSearchCallBack implements TreeSearchCallback { |
||||||
|
|
||||||
|
protected TemplateDirTreeSearcher treeSearcher; |
||||||
|
|
||||||
|
public TemplateDirSearchCallBack(TemplateDirTreeSearcher treeSearcher) { |
||||||
|
this.treeSearcher = treeSearcher; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void done(TreeSearchResult treeSearchResult) { |
||||||
|
if (TemplateDirTreeSearchManager.getInstance().getTreeSearchStatus() != TreeSearchStatus.SEARCHING) { |
||||||
|
return; |
||||||
|
} |
||||||
|
// 添加结果
|
||||||
|
addToTreeSearcher(treeSearchResult); |
||||||
|
if (treeSearcher.getCalculatedCount() == treeSearcher.getAllDirSize()) { |
||||||
|
updateTemplateTree(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 更新目录树搜索结果 |
||||||
|
*/ |
||||||
|
protected void updateTemplateTree() { |
||||||
|
SwingUtilities.invokeLater(() -> { |
||||||
|
if (TemplateDirTreeSearchManager.getInstance().getTreeSearchStatus() != TreeSearchStatus.SEARCHING) { |
||||||
|
return; |
||||||
|
} |
||||||
|
TemplateDirTreeSearchManager.getInstance().updateTemplateTree(); |
||||||
|
TemplateDirTreeSearchManager.getInstance().completeSearch(); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private void addToTreeSearcher(TreeSearchResult treeSearchResult) { |
||||||
|
// 添加到已计算结果集
|
||||||
|
treeSearcher.addToCalculatedSets(treeSearchResult.getAddToCalculated()); |
||||||
|
// 添加到匹配结果集
|
||||||
|
treeSearcher.addToMatchSets(((TemplateSearchResult)treeSearchResult).getAddToMatchNode()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,63 @@ |
|||||||
|
package com.fr.design.mainframe.manager.search.searcher.control.common; |
||||||
|
|
||||||
|
import com.fr.design.search.control.TreeSearchCallback; |
||||||
|
import com.fr.design.search.control.TreeSearchResult; |
||||||
|
import com.fr.design.search.TreeSearchStatus; |
||||||
|
import com.fr.design.mainframe.manager.search.TemplateTreeSearchManager; |
||||||
|
import com.fr.design.mainframe.manager.search.searcher.TemplateTreeSearcher; |
||||||
|
import javax.swing.SwingUtilities; |
||||||
|
|
||||||
|
/** |
||||||
|
* 模板搜索回调处理 |
||||||
|
*/ |
||||||
|
public class TemplateSearchCallBack implements TreeSearchCallback { |
||||||
|
|
||||||
|
protected TemplateTreeSearcher treeSearcher; |
||||||
|
private static final int BATCH_SIZE = 500; |
||||||
|
|
||||||
|
public TemplateSearchCallBack(TemplateTreeSearcher treeSearcher) { |
||||||
|
this.treeSearcher = treeSearcher; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void done(TreeSearchResult treeSearchResult) { |
||||||
|
if (TemplateTreeSearchManager.getInstance().getTreeSearchStatus() != TreeSearchStatus.SEARCHING) { |
||||||
|
return; |
||||||
|
} |
||||||
|
// 添加结果
|
||||||
|
addToTreeSearcher(treeSearchResult); |
||||||
|
updateTemplateTree(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 更新目录树搜索结果 |
||||||
|
*/ |
||||||
|
protected void updateTemplateTree() { |
||||||
|
int calculatedCount = treeSearcher.getCalculatedCount(); |
||||||
|
boolean allCalculated = calculatedCount == treeSearcher.getAllTemplateSize(); |
||||||
|
boolean isFinish = allCalculated && treeSearcher.getOutLayerCount() == 0; |
||||||
|
if (calculatedCount % BATCH_SIZE == 0 || allCalculated) { |
||||||
|
// 更新UI
|
||||||
|
updateOrFinish(isFinish); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected void updateOrFinish(boolean isFinished) { |
||||||
|
SwingUtilities.invokeLater(() -> { |
||||||
|
if (TemplateTreeSearchManager.getInstance().getTreeSearchStatus() != TreeSearchStatus.SEARCHING) { |
||||||
|
return; |
||||||
|
} |
||||||
|
TemplateTreeSearchManager.getInstance().updateTemplateTree(); |
||||||
|
if (isFinished) { |
||||||
|
TemplateTreeSearchManager.getInstance().completeSearch(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
protected void addToTreeSearcher(TreeSearchResult treeSearchResult) { |
||||||
|
// 添加到已计算结果集
|
||||||
|
treeSearcher.addToCalculatedSets(treeSearchResult.getAddToCalculated()); |
||||||
|
// 添加到匹配结果集
|
||||||
|
treeSearcher.addToMatchSets(((TemplateSearchResult)treeSearchResult).getAddToMatchNode()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,130 @@ |
|||||||
|
package com.fr.design.mainframe.manager.search.searcher.control.common; |
||||||
|
|
||||||
|
import com.fr.design.search.control.TreeSearchResult; |
||||||
|
import com.fr.file.filetree.FileNode; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* 模板搜索结果 |
||||||
|
*/ |
||||||
|
public class TemplateSearchResult implements TreeSearchResult { |
||||||
|
|
||||||
|
private boolean success; |
||||||
|
|
||||||
|
private List<String> addToExpand; |
||||||
|
|
||||||
|
private List<String> addToCalculated; |
||||||
|
|
||||||
|
private List<FileNode> addToNotCalculated; |
||||||
|
|
||||||
|
private List<FileNode> addToMatchNode; |
||||||
|
|
||||||
|
protected TemplateSearchResult(TemplateSearchResult.Builder builder) { |
||||||
|
this.success = builder.success; |
||||||
|
this.addToMatchNode = builder.addToMatchNode; |
||||||
|
this.addToExpand = builder.addToExpand; |
||||||
|
this.addToCalculated = builder.addToCalculated; |
||||||
|
this.addToNotCalculated = builder.addToNotCalculated; |
||||||
|
} |
||||||
|
|
||||||
|
public void setSuccess(boolean success) { |
||||||
|
this.success = success; |
||||||
|
} |
||||||
|
|
||||||
|
public void setAddToMatchNode(List<FileNode> addToMatchNode) { |
||||||
|
this.addToMatchNode = addToMatchNode; |
||||||
|
} |
||||||
|
|
||||||
|
public void setAddToExpand(List<String> addToExpand) { |
||||||
|
this.addToExpand = addToExpand; |
||||||
|
} |
||||||
|
|
||||||
|
public void setAddToCalculated(List<String> addToCalculated) { |
||||||
|
this.addToCalculated = addToCalculated; |
||||||
|
} |
||||||
|
|
||||||
|
public void setAddToNotCalculated(List<FileNode> addToNotCalculated) { |
||||||
|
this.addToNotCalculated = addToNotCalculated; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isSuccess() { |
||||||
|
return this.success; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public List<String> getAddToMatch() { |
||||||
|
return new ArrayList<>(); |
||||||
|
} |
||||||
|
|
||||||
|
public List<FileNode> getAddToMatchNode() { |
||||||
|
return addToMatchNode; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public List<String> getAddToExpand() { |
||||||
|
return this.addToExpand; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public List<String> getAddToCalculated() { |
||||||
|
return this.addToCalculated; |
||||||
|
} |
||||||
|
|
||||||
|
public List<FileNode> getAddToNotCalculated() { |
||||||
|
return this.addToNotCalculated; |
||||||
|
} |
||||||
|
|
||||||
|
public static class Builder { |
||||||
|
|
||||||
|
private boolean success; |
||||||
|
|
||||||
|
private List<FileNode> addToMatchNode; |
||||||
|
|
||||||
|
private List<String> addToExpand; |
||||||
|
|
||||||
|
private List<String> addToCalculated; |
||||||
|
|
||||||
|
private List<FileNode> addToNotCalculated; |
||||||
|
|
||||||
|
|
||||||
|
public Builder() { |
||||||
|
this.success = false; |
||||||
|
this.addToMatchNode = new ArrayList<>(); |
||||||
|
this.addToExpand = new ArrayList<>(); |
||||||
|
this.addToCalculated = new ArrayList<>(); |
||||||
|
this.addToNotCalculated = new ArrayList<>(); |
||||||
|
} |
||||||
|
|
||||||
|
public TemplateSearchResult.Builder buildSuccess(boolean success) { |
||||||
|
this.success = success; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public TemplateSearchResult.Builder buildAddToMatch(List<FileNode> addToMatch) { |
||||||
|
this.addToMatchNode = addToMatch; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public TemplateSearchResult.Builder buildAddToExpand(List<String> addToExpand) { |
||||||
|
this.addToExpand = addToExpand; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public TemplateSearchResult.Builder buildAddToCalculated(List<String> addToCalculated) { |
||||||
|
this.addToCalculated = addToCalculated; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public TemplateSearchResult.Builder buildAddToNotCalculated(List<FileNode> addToNotCalculated) { |
||||||
|
this.addToNotCalculated = addToNotCalculated; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public TemplateSearchResult build() { |
||||||
|
return new TemplateSearchResult(this); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,79 @@ |
|||||||
|
package com.fr.design.mainframe.manager.search.searcher.control.common; |
||||||
|
|
||||||
|
import com.fr.design.search.control.TreeSearchCallback; |
||||||
|
import com.fr.design.search.control.TreeSearchResult; |
||||||
|
import com.fr.design.search.control.TreeSearchTask; |
||||||
|
import com.fr.file.filetree.FileNode; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Arrays; |
||||||
|
|
||||||
|
/** |
||||||
|
* 模板搜索任务(比较模板路径与搜索关键字是否匹配) |
||||||
|
*/ |
||||||
|
public class TemplateSearchTask implements TreeSearchTask { |
||||||
|
|
||||||
|
/** |
||||||
|
* 用户搜索的文本 |
||||||
|
*/ |
||||||
|
private String searchText; |
||||||
|
|
||||||
|
/** |
||||||
|
* 目录节点 |
||||||
|
*/ |
||||||
|
private FileNode fileNode; |
||||||
|
|
||||||
|
private TreeSearchCallback callback; |
||||||
|
|
||||||
|
public TemplateSearchTask(String searchText, FileNode fileNode, TreeSearchCallback callback) { |
||||||
|
this.searchText = searchText; |
||||||
|
this.fileNode = fileNode; |
||||||
|
this.callback = callback; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
TreeSearchResult result; |
||||||
|
try { |
||||||
|
result = dealWithTemplateWrapper(fileNode); |
||||||
|
FineLoggerFactory.getLogger().debug("calculate template: {} succeeded", fileNode.getName()); |
||||||
|
} catch (Throwable e) { |
||||||
|
FineLoggerFactory.getLogger().error(e, "calculate template: {} failed", fileNode.getName()); |
||||||
|
result = dealWithErrorTemplateWrapper(fileNode); |
||||||
|
} |
||||||
|
callback.done(result); |
||||||
|
} |
||||||
|
|
||||||
|
private TreeSearchResult dealWithTemplateWrapper(FileNode fileNode) { |
||||||
|
String fileName = fileNode.getName(); |
||||||
|
|
||||||
|
boolean isNameMatch = isMatchSearch(fileName, searchText); |
||||||
|
return new TemplateSearchResult.Builder() |
||||||
|
.buildSuccess(true) |
||||||
|
.buildAddToMatch(isNameMatch ? Arrays.asList(fileNode) : new ArrayList<>()) |
||||||
|
.buildAddToExpand(fileNode.isDirectory() ? Arrays.asList(fileName) : new ArrayList<>()) |
||||||
|
.buildAddToCalculated(Arrays.asList(fileNode.getEnvPath())) |
||||||
|
.build(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 处理错误情况 |
||||||
|
* |
||||||
|
* @param fileNode |
||||||
|
*/ |
||||||
|
private TreeSearchResult dealWithErrorTemplateWrapper(FileNode fileNode) { |
||||||
|
return new TemplateSearchResult.Builder().buildSuccess(false) |
||||||
|
.buildAddToCalculated(Arrays.asList(fileNode.getEnvPath())).build(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 判断是否匹配搜索文本,不区分大小写 |
||||||
|
* |
||||||
|
* @param str |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private boolean isMatchSearch(String str, String searchText) { |
||||||
|
return str.toUpperCase().contains(searchText.toUpperCase()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,213 @@ |
|||||||
|
package com.fr.design.mainframe.manager.search.searcher.control.pane; |
||||||
|
|
||||||
|
import com.fr.base.svg.IconUtils; |
||||||
|
import com.fr.design.constants.UIConstants; |
||||||
|
import com.fr.design.gui.itree.filetree.EnvFileTree; |
||||||
|
import com.fr.design.mainframe.manager.search.TemplateDirTreeSearchManager; |
||||||
|
import com.fr.design.search.event.TreeSearchStatusChangeEvent; |
||||||
|
import com.fr.design.search.event.TreeSearchStatusChangeListener; |
||||||
|
import com.fr.design.search.TreeSearchStatus; |
||||||
|
import com.fr.design.gui.icontainer.UIScrollPane; |
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.SwingConstants; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.CardLayout; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.event.MouseAdapter; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
import java.awt.event.MouseListener; |
||||||
|
import java.awt.FlowLayout; |
||||||
|
|
||||||
|
/** |
||||||
|
* 目录树搜索提示面板:整合了目录树面板和搜索时的提示面板 |
||||||
|
*/ |
||||||
|
public class TemplateDirSearchRemindPane extends JPanel implements TreeSearchStatusChangeListener { |
||||||
|
|
||||||
|
private TemplateDirSearchRemindPane.RemindPane remindPane; |
||||||
|
private TemplateDirSearchRemindPane.TreePane treePane; |
||||||
|
|
||||||
|
public TemplateDirSearchRemindPane(EnvFileTree templateFileTree) { |
||||||
|
this.setLayout(FRGUIPaneFactory.createBorderLayout()); |
||||||
|
remindPane = new TemplateDirSearchRemindPane.RemindPane(); |
||||||
|
treePane = new TemplateDirSearchRemindPane.TreePane(templateFileTree); |
||||||
|
// 初始状态
|
||||||
|
this.add(remindPane, BorderLayout.NORTH); |
||||||
|
this.add(treePane, BorderLayout.CENTER); |
||||||
|
TemplateDirTreeSearchManager.getInstance().registerTreeSearchStatusChangeListener(this); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 根据搜索状态变化,来调整自身面板的显示 |
||||||
|
* |
||||||
|
* @param event |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void updateTreeSearchChange(TreeSearchStatusChangeEvent event) { |
||||||
|
TreeSearchStatus status = event.getTreeSearchStatus(); |
||||||
|
if (status == TreeSearchStatus.SEARCH_NOT_BEGIN || status == TreeSearchStatus.NOT_IN_SEARCH_MODE) { |
||||||
|
remindPane.onNotBegin(); |
||||||
|
treePane.onNotBegin(); |
||||||
|
} else if (status == TreeSearchStatus.SEARCHING) { |
||||||
|
remindPane.onInSearching(); |
||||||
|
treePane.onInSearching(); |
||||||
|
} else if (status == TreeSearchStatus.SEARCH_STOPPED) { |
||||||
|
remindPane.onStoppedSearching(); |
||||||
|
treePane.onStoppedSearching(); |
||||||
|
} else { |
||||||
|
boolean matchSetsEmpty = TemplateDirTreeSearchManager.getInstance().isMatchSetsEmpty(); |
||||||
|
// 代表是否搜索出结果
|
||||||
|
remindPane.onDoneSearching(matchSetsEmpty); |
||||||
|
treePane.onDoneSearching(matchSetsEmpty); |
||||||
|
} |
||||||
|
this.revalidate(); |
||||||
|
} |
||||||
|
|
||||||
|
private interface TreeSearchStatusChange { |
||||||
|
|
||||||
|
void onNotBegin(); |
||||||
|
|
||||||
|
void onInSearching(); |
||||||
|
|
||||||
|
void onStoppedSearching(); |
||||||
|
|
||||||
|
void onDoneSearching(boolean matchSetsEmpty); |
||||||
|
} |
||||||
|
|
||||||
|
private class TreePane extends JPanel implements TemplateDirSearchRemindPane.TreeSearchStatusChange { |
||||||
|
|
||||||
|
private UIScrollPane scrollPane; |
||||||
|
|
||||||
|
private JPanel notFoundPane; |
||||||
|
|
||||||
|
private CardLayout cardLayout; |
||||||
|
|
||||||
|
private static final String SCROLL_PANE = "scrollPane"; |
||||||
|
|
||||||
|
private static final String NOT_FOUND_PANE = "notFoundPane"; |
||||||
|
|
||||||
|
public TreePane(EnvFileTree templateFileTree) { |
||||||
|
init(templateFileTree); |
||||||
|
} |
||||||
|
|
||||||
|
private void init(EnvFileTree templateFileTree) { |
||||||
|
|
||||||
|
scrollPane = new UIScrollPane(templateFileTree); |
||||||
|
scrollPane.setBorder(null); |
||||||
|
|
||||||
|
notFoundPane = FRGUIPaneFactory.createVerticalFlowLayout_Pane(false, FlowLayout.CENTER, 0, 5); |
||||||
|
UILabel emptyPicLabel = new UILabel(); |
||||||
|
emptyPicLabel.setIcon(IconUtils.readIcon("com/fr/base/images/share/no_match_icon.png")); |
||||||
|
emptyPicLabel.setHorizontalAlignment(SwingConstants.CENTER); |
||||||
|
emptyPicLabel.setPreferredSize(new Dimension(570, 100)); |
||||||
|
UILabel textLabel = new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Tree_Search_Not_Match"), SwingConstants.CENTER); |
||||||
|
textLabel.setForeground(Color.gray); |
||||||
|
textLabel.setHorizontalAlignment(SwingConstants.CENTER); |
||||||
|
textLabel.setPreferredSize(new Dimension(570, 20)); |
||||||
|
notFoundPane.add(emptyPicLabel); |
||||||
|
notFoundPane.add(textLabel); |
||||||
|
|
||||||
|
cardLayout = new CardLayout(); |
||||||
|
this.setLayout(cardLayout); |
||||||
|
this.add(scrollPane, SCROLL_PANE); |
||||||
|
this.add(notFoundPane, NOT_FOUND_PANE); |
||||||
|
cardLayout.show(this, SCROLL_PANE); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onNotBegin() { |
||||||
|
switchPane(SCROLL_PANE); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onInSearching() { |
||||||
|
switchPane(SCROLL_PANE); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onStoppedSearching() { |
||||||
|
switchPane(SCROLL_PANE); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onDoneSearching(boolean matchSetsEmpty) { |
||||||
|
if (matchSetsEmpty) { |
||||||
|
switchPane(NOT_FOUND_PANE); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void switchPane(String paneName) { |
||||||
|
cardLayout.show(this, paneName); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static class RemindPane extends JPanel implements TemplateDirSearchRemindPane.TreeSearchStatusChange { |
||||||
|
|
||||||
|
private static final String IN_SEARCHING = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Tree_Search_In_Searching"); |
||||||
|
private static final String STOP_SEARCHING = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Tree_Search_Stop_Search"); |
||||||
|
private static final String SEARCHING_STOPPED = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Tree_Search_Search_Stopped"); |
||||||
|
private static final String DONE_SEARCHING = Toolkit.i18nText("Fine-Design_Tree_Search_Search_Completed"); |
||||||
|
|
||||||
|
private UILabel textLabel; |
||||||
|
|
||||||
|
private UILabel stopLabel; |
||||||
|
|
||||||
|
private MouseListener stopSearch; |
||||||
|
|
||||||
|
public RemindPane() { |
||||||
|
init(); |
||||||
|
} |
||||||
|
|
||||||
|
private void init() { |
||||||
|
this.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 0)); |
||||||
|
// 初始情况下为Not_Begin
|
||||||
|
textLabel = new UILabel(); |
||||||
|
textLabel.setForeground(Color.gray); |
||||||
|
stopLabel = new UILabel(); |
||||||
|
stopLabel.setForeground(UIConstants.NORMAL_BLUE); |
||||||
|
stopSearch = new MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mouseClicked(MouseEvent e) { |
||||||
|
TemplateDirTreeSearchManager.getInstance().stopSearch(); |
||||||
|
} |
||||||
|
}; |
||||||
|
stopLabel.addMouseListener(stopSearch); |
||||||
|
this.add(textLabel); |
||||||
|
this.add(stopLabel); |
||||||
|
onNotBegin(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onNotBegin() { |
||||||
|
this.setVisible(false); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onInSearching() { |
||||||
|
this.textLabel.setVisible(false); |
||||||
|
this.stopLabel.setText(STOP_SEARCHING); |
||||||
|
this.stopLabel.setVisible(true); |
||||||
|
this.setVisible(true); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onStoppedSearching() { |
||||||
|
this.textLabel.setText(SEARCHING_STOPPED); |
||||||
|
this.textLabel.setVisible(true); |
||||||
|
this.stopLabel.setVisible(false); |
||||||
|
this.setVisible(true); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onDoneSearching(boolean matchSetsEmpty) { |
||||||
|
this.textLabel.setText(DONE_SEARCHING); |
||||||
|
this.textLabel.setVisible(true); |
||||||
|
this.stopLabel.setVisible(false); |
||||||
|
this.setVisible(true); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,152 @@ |
|||||||
|
package com.fr.design.mainframe.manager.search.searcher.control.pane; |
||||||
|
|
||||||
|
import com.fr.base.svg.IconUtils; |
||||||
|
import com.fr.design.constants.UIConstants; |
||||||
|
import com.fr.design.file.TemplateDirTreePane; |
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.design.gui.itextfield.UITextField; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
|
import com.fr.design.mainframe.manager.search.TemplateDirTreeSearchManager; |
||||||
|
import com.fr.design.search.event.TreeSearchStatusChangeEvent; |
||||||
|
import com.fr.design.search.event.TreeSearchStatusChangeListener; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
|
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.event.DocumentEvent; |
||||||
|
import javax.swing.event.DocumentListener; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Insets; |
||||||
|
import java.awt.event.FocusEvent; |
||||||
|
import java.awt.event.FocusListener; |
||||||
|
import java.awt.event.KeyAdapter; |
||||||
|
import java.awt.event.KeyEvent; |
||||||
|
import java.awt.event.MouseAdapter; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
|
||||||
|
/** |
||||||
|
* 目录树搜索面板 |
||||||
|
*/ |
||||||
|
public class TemplateDirTreeSearchPane extends JPanel implements TreeSearchStatusChangeListener { |
||||||
|
|
||||||
|
/** |
||||||
|
* 搜索输入框 |
||||||
|
*/ |
||||||
|
private UITextField searchTextField; |
||||||
|
|
||||||
|
/** |
||||||
|
* 搜索面板 |
||||||
|
*/ |
||||||
|
private JPanel searchPane; |
||||||
|
|
||||||
|
private final KeyAdapter enterPressed = new KeyAdapter() { |
||||||
|
@Override |
||||||
|
public void keyPressed(KeyEvent e) { |
||||||
|
if (e.getKeyCode() == KeyEvent.VK_ENTER) { |
||||||
|
TemplateDirTreeSearchManager.getInstance().startSearch(searchTextField.getText()); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
public TemplateDirTreeSearchPane() { |
||||||
|
this.setLayout(FRGUIPaneFactory.createBorderLayout()); |
||||||
|
this.setBorder(BorderFactory.createEmptyBorder(10, 15, 0, 10)); |
||||||
|
initSearchPane(); |
||||||
|
add(searchPane, BorderLayout.CENTER); |
||||||
|
TemplateDirTreeSearchManager.getInstance().registerTreeSearchStatusChangeListener(this); |
||||||
|
TemplateDirTreePane.getInstance().refreshDockingView(); |
||||||
|
TemplateDirTreeSearchManager.getInstance().beforeSearch(TemplateDirTreePane.getInstance().getTemplateDirTree()); |
||||||
|
} |
||||||
|
|
||||||
|
private void initSearchPane() { |
||||||
|
searchPane = new JPanel(FRGUIPaneFactory.createBorderLayout()); |
||||||
|
searchPane.setBorder(BorderFactory.createLineBorder(UIConstants.TOOLBAR_BORDER_COLOR)); |
||||||
|
searchPane.setBackground(Color.WHITE); |
||||||
|
// 左侧搜索图标
|
||||||
|
UILabel searchLabel = new UILabel(IconUtils.readIcon("/com/fr/design/images/data/search")); |
||||||
|
searchLabel.setBorder(BorderFactory.createEmptyBorder(0, 12, 0, 0)); |
||||||
|
searchLabel.addMouseListener(new MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mouseClicked(MouseEvent e) { |
||||||
|
// do nothing
|
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
// 中间输入框
|
||||||
|
initSearchTextField(); |
||||||
|
|
||||||
|
// 右侧返回图标
|
||||||
|
UILabel returnLabel = new UILabel(IconUtils.readIcon("/com/fr/design/images/data/clear")); |
||||||
|
returnLabel.setToolTipText(Toolkit.i18nText("Fine-Design_Tree_Search_Return")); |
||||||
|
returnLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 11)); |
||||||
|
returnLabel.addMouseListener(new MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mouseClicked(MouseEvent e) { |
||||||
|
TemplateDirTreeSearchManager.getInstance().outOfSearchMode(); |
||||||
|
TemplateDirTreePane.getInstance().refreshDockingView(); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
searchPane.add(searchLabel, BorderLayout.WEST); |
||||||
|
searchPane.add(searchTextField, BorderLayout.CENTER); |
||||||
|
searchPane.add(returnLabel, BorderLayout.EAST); |
||||||
|
} |
||||||
|
|
||||||
|
private void initSearchTextField() { |
||||||
|
searchTextField = new UITextField(){ |
||||||
|
@Override |
||||||
|
public Insets getInsets() { |
||||||
|
return new Insets(2, 4, 0, 4); |
||||||
|
} |
||||||
|
}; |
||||||
|
searchTextField.setBorderPainted(false); |
||||||
|
searchTextField.setPlaceholder(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Template_Dir_Search_Press_Enter_For_Search")); |
||||||
|
searchTextField.addFocusListener(new FocusListener() { |
||||||
|
@Override |
||||||
|
public void focusGained(FocusEvent e) { |
||||||
|
searchPane.setBorder(BorderFactory.createLineBorder(UIConstants.NORMAL_BLUE)); |
||||||
|
searchPane.repaint(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void focusLost(FocusEvent e) { |
||||||
|
searchPane.setBorder(BorderFactory.createLineBorder(UIConstants.TOOLBAR_BORDER_COLOR)); |
||||||
|
searchPane.repaint(); |
||||||
|
} |
||||||
|
}); |
||||||
|
this.searchTextField.getDocument().addDocumentListener(new DocumentListener() { |
||||||
|
@Override |
||||||
|
public void insertUpdate(DocumentEvent e) { |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeUpdate(DocumentEvent e) { |
||||||
|
dealWithTextChange(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void changedUpdate(DocumentEvent e) { |
||||||
|
} |
||||||
|
}); |
||||||
|
this.searchTextField.addKeyListener(enterPressed); |
||||||
|
} |
||||||
|
|
||||||
|
private void dealWithTextChange() { |
||||||
|
if (StringUtils.isEmpty(searchTextField.getText()) && TemplateDirTreeSearchManager.getInstance().isInSearchMode()) { |
||||||
|
// 如果是搜索模式下,看作是用户删除输入框文字,仅复原TemplateTreePane
|
||||||
|
TemplateDirTreeSearchManager.getInstance().restoreTreePane(); |
||||||
|
TemplateDirTreePane.getInstance().refreshDockingView(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 目录树不涉及到工具栏和搜索栏的切换,无需实现 |
||||||
|
* @param event |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void updateTreeSearchChange(TreeSearchStatusChangeEvent event) { |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,228 @@ |
|||||||
|
package com.fr.design.mainframe.manager.search.searcher.control.pane; |
||||||
|
|
||||||
|
import com.fr.base.svg.IconUtils; |
||||||
|
import com.fr.design.constants.UIConstants; |
||||||
|
import com.fr.design.gui.itree.filetree.EnvFileTree; |
||||||
|
import com.fr.design.search.event.TreeSearchStatusChangeEvent; |
||||||
|
import com.fr.design.search.event.TreeSearchStatusChangeListener; |
||||||
|
import com.fr.design.search.TreeSearchStatus; |
||||||
|
import com.fr.design.gui.icontainer.UIScrollPane; |
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
|
import com.fr.design.mainframe.manager.search.TemplateTreeSearchManager; |
||||||
|
|
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.SwingConstants; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.CardLayout; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.event.MouseAdapter; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
import java.awt.event.MouseListener; |
||||||
|
import java.awt.FlowLayout; |
||||||
|
|
||||||
|
/** |
||||||
|
* 模板搜索提示面板:整合了模板树和提示面板 |
||||||
|
*/ |
||||||
|
public class TemplateSearchRemindPane extends JPanel implements TreeSearchStatusChangeListener { |
||||||
|
|
||||||
|
private TemplateSearchRemindPane.RemindPane remindPane; |
||||||
|
private TemplateSearchRemindPane.TreePane treePane; |
||||||
|
|
||||||
|
public TemplateSearchRemindPane(EnvFileTree templateFileTree) { |
||||||
|
this.setLayout(FRGUIPaneFactory.createBorderLayout()); |
||||||
|
remindPane = new TemplateSearchRemindPane.RemindPane(); |
||||||
|
treePane = new TemplateSearchRemindPane.TreePane(templateFileTree); |
||||||
|
// 初始状态
|
||||||
|
this.add(remindPane, BorderLayout.NORTH); |
||||||
|
this.add(treePane, BorderLayout.CENTER); |
||||||
|
TemplateTreeSearchManager.getInstance().registerTreeSearchStatusChangeListener(this); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 根据搜索状态变化,来调整自身面板的显示 |
||||||
|
* |
||||||
|
* @param event |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void updateTreeSearchChange(TreeSearchStatusChangeEvent event) { |
||||||
|
TreeSearchStatus status = event.getTreeSearchStatus(); |
||||||
|
if (status == TreeSearchStatus.SEARCH_NOT_BEGIN || status == TreeSearchStatus.NOT_IN_SEARCH_MODE) { |
||||||
|
remindPane.onNotBegin(); |
||||||
|
treePane.onNotBegin(); |
||||||
|
} else if (status == TreeSearchStatus.SEARCHING) { |
||||||
|
remindPane.onInSearching(); |
||||||
|
treePane.onInSearching(); |
||||||
|
} else if (status == TreeSearchStatus.SEARCH_STOPPED) { |
||||||
|
remindPane.onStoppedSearching(); |
||||||
|
treePane.onStoppedSearching(); |
||||||
|
} else { |
||||||
|
boolean matchSetsEmpty = TemplateTreeSearchManager.getInstance().isMatchSetsEmpty(); |
||||||
|
// 代表是否搜索出结果
|
||||||
|
remindPane.onDoneSearching(matchSetsEmpty); |
||||||
|
treePane.onDoneSearching(matchSetsEmpty); |
||||||
|
} |
||||||
|
this.revalidate(); |
||||||
|
} |
||||||
|
|
||||||
|
private interface TreeSearchStatusChange { |
||||||
|
|
||||||
|
/** |
||||||
|
* 搜索未开始时 |
||||||
|
*/ |
||||||
|
void onNotBegin(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 搜索中 |
||||||
|
*/ |
||||||
|
void onInSearching(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 停止搜索 |
||||||
|
*/ |
||||||
|
void onStoppedSearching(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 搜索结束 |
||||||
|
* @param matchSetsEmpty |
||||||
|
*/ |
||||||
|
void onDoneSearching(boolean matchSetsEmpty); |
||||||
|
} |
||||||
|
|
||||||
|
private class TreePane extends JPanel implements TemplateSearchRemindPane.TreeSearchStatusChange { |
||||||
|
|
||||||
|
private UIScrollPane scrollPane; |
||||||
|
|
||||||
|
private JPanel notFoundPane; |
||||||
|
|
||||||
|
private CardLayout cardLayout; |
||||||
|
|
||||||
|
private static final String SCROLL_PANE = "scrollPane"; |
||||||
|
|
||||||
|
private static final String NOT_FOUND_PANE = "notFoundPane"; |
||||||
|
|
||||||
|
public TreePane(EnvFileTree templateFileTree) { |
||||||
|
init(templateFileTree); |
||||||
|
} |
||||||
|
|
||||||
|
private void init(EnvFileTree templateFileTree) { |
||||||
|
|
||||||
|
scrollPane = new UIScrollPane(templateFileTree); |
||||||
|
scrollPane.setBorder(null); |
||||||
|
|
||||||
|
notFoundPane = FRGUIPaneFactory.createVerticalFlowLayout_Pane(true, FlowLayout.LEADING, 0, 5); |
||||||
|
UILabel emptyPicLabel = new UILabel(); |
||||||
|
emptyPicLabel.setIcon(IconUtils.readIcon("com/fr/base/images/share/no_match_icon.png")); |
||||||
|
emptyPicLabel.setHorizontalAlignment(SwingConstants.CENTER); |
||||||
|
emptyPicLabel.setPreferredSize(new Dimension(240, 100)); |
||||||
|
UILabel textLabel = new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Tree_Search_Not_Match"), SwingConstants.CENTER); |
||||||
|
textLabel.setForeground(Color.gray); |
||||||
|
textLabel.setHorizontalAlignment(SwingConstants.CENTER); |
||||||
|
textLabel.setPreferredSize(new Dimension(240, 20)); |
||||||
|
notFoundPane.add(emptyPicLabel); |
||||||
|
notFoundPane.add(textLabel); |
||||||
|
notFoundPane.setBorder(BorderFactory.createEmptyBorder(80, 0, 0, 0)); |
||||||
|
|
||||||
|
cardLayout = new CardLayout(); |
||||||
|
this.setLayout(cardLayout); |
||||||
|
this.add(scrollPane, SCROLL_PANE); |
||||||
|
this.add(notFoundPane, NOT_FOUND_PANE); |
||||||
|
cardLayout.show(this, SCROLL_PANE); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onNotBegin() { |
||||||
|
switchPane(SCROLL_PANE); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onInSearching() { |
||||||
|
switchPane(SCROLL_PANE); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onStoppedSearching() { |
||||||
|
switchPane(SCROLL_PANE); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onDoneSearching(boolean matchSetsEmpty) { |
||||||
|
if (matchSetsEmpty) { |
||||||
|
switchPane(NOT_FOUND_PANE); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void switchPane(String paneName) { |
||||||
|
cardLayout.show(this, paneName); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static class RemindPane extends JPanel implements TemplateSearchRemindPane.TreeSearchStatusChange { |
||||||
|
|
||||||
|
private static final String IN_SEARCHING = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Tree_Search_In_Searching"); |
||||||
|
private static final String STOP_SEARCHING = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Tree_Search_Stop_Search"); |
||||||
|
private static final String SEARCHING_STOPPED = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Tree_Search_Search_Stopped"); |
||||||
|
private static final String DONE_SEARCHING = Toolkit.i18nText("Fine-Design_Tree_Search_Search_Completed"); |
||||||
|
|
||||||
|
private UILabel textLabel; |
||||||
|
|
||||||
|
private UILabel stopLabel; |
||||||
|
|
||||||
|
private MouseListener stopSearch; |
||||||
|
|
||||||
|
public RemindPane() { |
||||||
|
init(); |
||||||
|
} |
||||||
|
|
||||||
|
private void init() { |
||||||
|
this.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 0)); |
||||||
|
// 初始情况下为Not_Begin
|
||||||
|
textLabel = new UILabel(); |
||||||
|
textLabel.setForeground(Color.gray); |
||||||
|
stopLabel = new UILabel(); |
||||||
|
stopLabel.setForeground(UIConstants.NORMAL_BLUE); |
||||||
|
stopSearch = new MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mouseClicked(MouseEvent e) { |
||||||
|
TemplateTreeSearchManager.getInstance().stopSearch(); |
||||||
|
} |
||||||
|
}; |
||||||
|
stopLabel.addMouseListener(stopSearch); |
||||||
|
this.add(textLabel); |
||||||
|
this.add(stopLabel); |
||||||
|
onNotBegin(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onNotBegin() { |
||||||
|
this.setVisible(false); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onInSearching() { |
||||||
|
this.textLabel.setVisible(false); |
||||||
|
this.stopLabel.setText(STOP_SEARCHING); |
||||||
|
this.stopLabel.setVisible(true); |
||||||
|
this.setVisible(true); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onStoppedSearching() { |
||||||
|
this.textLabel.setText(SEARCHING_STOPPED); |
||||||
|
this.textLabel.setVisible(true); |
||||||
|
this.stopLabel.setVisible(false); |
||||||
|
this.setVisible(true); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onDoneSearching(boolean matchSetsEmpty) { |
||||||
|
this.textLabel.setText(DONE_SEARCHING); |
||||||
|
this.textLabel.setVisible(true); |
||||||
|
this.stopLabel.setVisible(false); |
||||||
|
this.setVisible(true); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,212 @@ |
|||||||
|
package com.fr.design.mainframe.manager.search.searcher.control.pane; |
||||||
|
|
||||||
|
import com.fr.base.svg.IconUtils; |
||||||
|
import com.fr.design.constants.UIConstants; |
||||||
|
import com.fr.design.search.event.TreeSearchStatusChangeEvent; |
||||||
|
import com.fr.design.search.event.TreeSearchStatusChangeListener; |
||||||
|
import com.fr.design.search.TreeSearchStatus; |
||||||
|
import com.fr.design.file.TemplateTreePane; |
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.design.gui.itextfield.UITextField; |
||||||
|
import com.fr.design.gui.itoolbar.UIToolbar; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
|
import com.fr.design.mainframe.manager.search.TemplateTreeSearchManager; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
|
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.event.DocumentEvent; |
||||||
|
import javax.swing.event.DocumentListener; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.CardLayout; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.Insets; |
||||||
|
import java.awt.event.FocusEvent; |
||||||
|
import java.awt.event.FocusListener; |
||||||
|
import java.awt.event.KeyAdapter; |
||||||
|
import java.awt.event.KeyEvent; |
||||||
|
import java.awt.event.MouseAdapter; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
|
||||||
|
/** |
||||||
|
* 模板搜索工具栏 |
||||||
|
*/ |
||||||
|
public class TemplateTreeSearchToolbarPane extends JPanel implements TreeSearchStatusChangeListener { |
||||||
|
|
||||||
|
public static final String TOOLBAR_PANE = "toolbarPane"; |
||||||
|
|
||||||
|
public static final String SEARCH_PANE = "searchPane"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 工具栏 |
||||||
|
*/ |
||||||
|
private UIToolbar toolbar; |
||||||
|
|
||||||
|
/** |
||||||
|
* 工具栏面板 |
||||||
|
*/ |
||||||
|
private JPanel toolbarPane; |
||||||
|
|
||||||
|
/** |
||||||
|
* 搜索面板 |
||||||
|
*/ |
||||||
|
private JPanel searchPane; |
||||||
|
|
||||||
|
/** |
||||||
|
* 搜索输入框 |
||||||
|
*/ |
||||||
|
private UITextField searchTextField; |
||||||
|
|
||||||
|
/** |
||||||
|
* 内容面板 |
||||||
|
*/ |
||||||
|
private JPanel contentPane; |
||||||
|
|
||||||
|
/** |
||||||
|
* 卡片布局管理器 |
||||||
|
*/ |
||||||
|
private CardLayout cardLayout; |
||||||
|
|
||||||
|
private final KeyAdapter enterPressed = new KeyAdapter() { |
||||||
|
@Override |
||||||
|
public void keyPressed(KeyEvent e) { |
||||||
|
if (e.getKeyCode() == KeyEvent.VK_ENTER) { |
||||||
|
TemplateTreeSearchManager.getInstance().startSearch(searchTextField.getText()); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
public TemplateTreeSearchToolbarPane(UIToolbar toolbar) { |
||||||
|
this.toolbar = toolbar; |
||||||
|
this.setLayout(FRGUIPaneFactory.createBorderLayout()); |
||||||
|
initToolbarPane(); |
||||||
|
initSearchPane(); |
||||||
|
initContentPane(); |
||||||
|
add(contentPane, BorderLayout.CENTER); |
||||||
|
setPreferredSize(new Dimension(240, 30)); |
||||||
|
TemplateTreeSearchManager.getInstance().registerTreeSearchStatusChangeListener(this); |
||||||
|
} |
||||||
|
|
||||||
|
private void initContentPane() { |
||||||
|
cardLayout = new CardLayout(); |
||||||
|
contentPane = new JPanel(cardLayout); |
||||||
|
contentPane.add(searchPane, SEARCH_PANE); |
||||||
|
contentPane.add(toolbarPane, TOOLBAR_PANE); |
||||||
|
cardLayout.show(contentPane, TOOLBAR_PANE); |
||||||
|
} |
||||||
|
|
||||||
|
private void initSearchPane() { |
||||||
|
searchPane = new JPanel(FRGUIPaneFactory.createBorderLayout()); |
||||||
|
searchPane.setBorder(BorderFactory.createLineBorder(UIConstants.TOOLBAR_BORDER_COLOR)); |
||||||
|
searchPane.setBackground(Color.WHITE); |
||||||
|
// 左侧搜索图标
|
||||||
|
UILabel searchLabel = new UILabel(IconUtils.readIcon("/com/fr/design/images/data/search")); |
||||||
|
searchLabel.setBorder(BorderFactory.createEmptyBorder(0, 12, 0, 0)); |
||||||
|
searchLabel.addMouseListener(new MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mouseClicked(MouseEvent e) { |
||||||
|
// do nothing
|
||||||
|
} |
||||||
|
}); |
||||||
|
// 中间输入框
|
||||||
|
initSearchTextField(); |
||||||
|
// 右侧返回图标
|
||||||
|
UILabel returnLabel = new UILabel(IconUtils.readIcon("/com/fr/design/images/data/clear")); |
||||||
|
returnLabel.setToolTipText(Toolkit.i18nText("Fine-Design_Tree_Search_Return")); |
||||||
|
returnLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 11)); |
||||||
|
returnLabel.addMouseListener(new MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mouseClicked(MouseEvent e) { |
||||||
|
TemplateTreeSearchManager.getInstance().outOfSearchMode(); |
||||||
|
TemplateTreePane.getInstance().refreshDockingView(); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
searchPane.add(searchLabel, BorderLayout.WEST); |
||||||
|
searchPane.add(searchTextField, BorderLayout.CENTER); |
||||||
|
searchPane.add(returnLabel, BorderLayout.EAST); |
||||||
|
} |
||||||
|
|
||||||
|
private void initSearchTextField() { |
||||||
|
searchTextField = new UITextField(){ |
||||||
|
@Override |
||||||
|
public Insets getInsets() { |
||||||
|
return new Insets(2, 4, 0, 4); |
||||||
|
} |
||||||
|
}; |
||||||
|
searchTextField.setBorderPainted(false); |
||||||
|
searchTextField.setPlaceholder(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Template_Search_Press_Enter_For_Search")); |
||||||
|
searchTextField.addFocusListener(new FocusListener() { |
||||||
|
@Override |
||||||
|
public void focusGained(FocusEvent e) { |
||||||
|
searchPane.setBorder(BorderFactory.createLineBorder(UIConstants.NORMAL_BLUE)); |
||||||
|
searchPane.repaint(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void focusLost(FocusEvent e) { |
||||||
|
searchPane.setBorder(BorderFactory.createLineBorder(UIConstants.TOOLBAR_BORDER_COLOR)); |
||||||
|
searchPane.repaint(); |
||||||
|
} |
||||||
|
}); |
||||||
|
this.searchTextField.getDocument().addDocumentListener(new DocumentListener() { |
||||||
|
@Override |
||||||
|
public void insertUpdate(DocumentEvent e) { |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeUpdate(DocumentEvent e) { |
||||||
|
dealWithTextChange(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void changedUpdate(DocumentEvent e) { |
||||||
|
} |
||||||
|
}); |
||||||
|
this.searchTextField.addKeyListener(enterPressed); |
||||||
|
} |
||||||
|
|
||||||
|
private void dealWithTextChange() { |
||||||
|
if (StringUtils.isEmpty(searchTextField.getText()) && TemplateTreeSearchManager.getInstance().isInSearchMode()) { |
||||||
|
// 如果是搜索模式下,看作是用户删除输入框文字,仅复原TemplateTreePane
|
||||||
|
TemplateTreeSearchManager.getInstance().restoreTreePane(); |
||||||
|
TemplateTreePane.getInstance().refreshDockingView(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void initToolbarPane() { |
||||||
|
toolbarPane = new JPanel(); |
||||||
|
toolbarPane.setLayout(FRGUIPaneFactory.createBorderLayout()); |
||||||
|
toolbarPane.add(toolbar, BorderLayout.CENTER); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 交换当前面板层级 |
||||||
|
*/ |
||||||
|
public void switchPane(String name) { |
||||||
|
cardLayout.show(contentPane, name); |
||||||
|
} |
||||||
|
|
||||||
|
public void setPlaceHolder(String placeHolder) { |
||||||
|
this.searchTextField.setPlaceholder(placeHolder); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 根据搜索状态变化,来调整自身面板的显示 |
||||||
|
* |
||||||
|
* @param event |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void updateTreeSearchChange(TreeSearchStatusChangeEvent event) { |
||||||
|
TreeSearchStatus treeSearchStatus = event.getTreeSearchStatus(); |
||||||
|
if (treeSearchStatus == TreeSearchStatus.NOT_IN_SEARCH_MODE) { |
||||||
|
this.searchTextField.setText(StringUtils.EMPTY); |
||||||
|
switchPane(TOOLBAR_PANE); |
||||||
|
} else { |
||||||
|
switchPane(SEARCH_PANE); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
package com.fr.design.mainframe.manager.search.searcher.control.pre; |
||||||
|
|
||||||
|
import com.fr.design.file.TemplateDirTreePane; |
||||||
|
import com.fr.design.search.control.TreeSearchCallback; |
||||||
|
import com.fr.file.filetree.FileNode; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* 目录树搜索时的前置处理,跟模板搜索前置处理类似 |
||||||
|
*/ |
||||||
|
public class TemplateDirPreSearchTask extends TemplatePreSearchTask { |
||||||
|
|
||||||
|
public TemplateDirPreSearchTask(TreeSearchCallback callback, FileNode fileNode) { |
||||||
|
super(callback, fileNode); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 加载最外层目录下所有的子节点 |
||||||
|
* |
||||||
|
* @param fileNodes |
||||||
|
* @param fileNode |
||||||
|
*/ |
||||||
|
protected void dealWithChildTemplate(List<FileNode> fileNodes, FileNode fileNode) { |
||||||
|
FileNode[] childNodes = TemplateDirTreePane.getInstance().getTemplateDirTree().listFile(fileNode.getEnvPath()); |
||||||
|
for (FileNode childNode : childNodes) { |
||||||
|
if (childNode.isDirectory()) { |
||||||
|
fileNodes.add(childNode); |
||||||
|
dealWithChildTemplate(fileNodes, childNode); |
||||||
|
} else { |
||||||
|
fileNodes.add(childNode); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
package com.fr.design.mainframe.manager.search.searcher.control.pre; |
||||||
|
|
||||||
|
import com.fr.design.mainframe.manager.search.searcher.control.common.TemplateSearchCallBack; |
||||||
|
import com.fr.design.mainframe.manager.search.searcher.control.common.TemplateSearchResult; |
||||||
|
import com.fr.design.search.control.TreeSearchResult; |
||||||
|
import com.fr.design.mainframe.manager.search.searcher.TemplateTreeSearcher; |
||||||
|
|
||||||
|
/** |
||||||
|
* 模板搜索前置回调处理 |
||||||
|
* 主要是将最外层目录下的所有模板添加到未计算的集合中 |
||||||
|
*/ |
||||||
|
public class TemplatePreSearchCallBack extends TemplateSearchCallBack { |
||||||
|
|
||||||
|
public TemplatePreSearchCallBack(TemplateTreeSearcher treeSearcher) { |
||||||
|
super(treeSearcher); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void done(TreeSearchResult searchResult) { |
||||||
|
addToTreeSearcher(searchResult); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 将结果添加到未计算的集合中 |
||||||
|
* |
||||||
|
* @param searchResult |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
protected void addToTreeSearcher(TreeSearchResult searchResult) { |
||||||
|
TemplateSearchResult templateSearchResult = (TemplateSearchResult) searchResult; |
||||||
|
treeSearcher.addToNotCalculatedSets(templateSearchResult.getAddToNotCalculated()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,62 @@ |
|||||||
|
package com.fr.design.mainframe.manager.search.searcher.control.pre; |
||||||
|
|
||||||
|
import com.fr.design.mainframe.manager.search.searcher.control.common.TemplateSearchResult; |
||||||
|
import com.fr.design.search.control.TreeSearchCallback; |
||||||
|
import com.fr.design.search.control.TreeSearchResult; |
||||||
|
import com.fr.design.search.control.TreeSearchTask; |
||||||
|
import com.fr.design.file.TemplateTreePane; |
||||||
|
import com.fr.file.filetree.FileNode; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* 模板搜索前置任务 |
||||||
|
* 主要是计算最外层目录下面所有的子节点,并记录到结果集中 |
||||||
|
*/ |
||||||
|
public class TemplatePreSearchTask implements TreeSearchTask { |
||||||
|
|
||||||
|
protected TreeSearchCallback callback; |
||||||
|
|
||||||
|
//最外层的目录节点
|
||||||
|
protected FileNode fileNode; |
||||||
|
|
||||||
|
public TemplatePreSearchTask(TreeSearchCallback callback, FileNode fileNode) { |
||||||
|
this.callback = callback; |
||||||
|
this.fileNode = fileNode; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
TreeSearchResult result; |
||||||
|
try { |
||||||
|
List<FileNode> allChildNode = new ArrayList<>(); |
||||||
|
dealWithChildTemplate(allChildNode, fileNode); |
||||||
|
result = new TemplateSearchResult.Builder().buildAddToNotCalculated(allChildNode).buildSuccess(true).build(); |
||||||
|
FineLoggerFactory.getLogger().info("[Template Search] calculate {} child nodes success. total child node num is: {}", fileNode.getEnvPath(), allChildNode.size()); |
||||||
|
} catch (Exception e) { |
||||||
|
FineLoggerFactory.getLogger().error("[Template Search] calculate {} child nodes failed", fileNode.getEnvPath()); |
||||||
|
result = new TemplateSearchResult.Builder().buildAddToNotCalculated(new ArrayList<>()).buildSuccess(false).build(); |
||||||
|
} |
||||||
|
callback.done(result); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 加载最外层目录下所有的子节点 |
||||||
|
* |
||||||
|
* @param fileNodes |
||||||
|
* @param fileNode |
||||||
|
*/ |
||||||
|
protected void dealWithChildTemplate(List<FileNode> fileNodes, FileNode fileNode) { |
||||||
|
FileNode[] childNodes = TemplateTreePane.getInstance().getTemplateFileTree().listFile(fileNode.getEnvPath()); |
||||||
|
for (FileNode childNode : childNodes) { |
||||||
|
if (childNode.isDirectory()) { |
||||||
|
fileNodes.add(childNode); |
||||||
|
dealWithChildTemplate(fileNodes, childNode); |
||||||
|
} else { |
||||||
|
fileNodes.add(childNode); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,194 @@ |
|||||||
|
package com.fr.design.mainframe.toast; |
||||||
|
|
||||||
|
import com.fr.concurrent.NamedThreadFactory; |
||||||
|
import com.fr.design.dialog.UIDialog; |
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
|
import com.fr.module.ModuleContext; |
||||||
|
|
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.Icon; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.SwingConstants; |
||||||
|
import javax.swing.SwingUtilities; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.Point; |
||||||
|
import java.awt.Window; |
||||||
|
import java.util.concurrent.ScheduledExecutorService; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
|
||||||
|
/** |
||||||
|
* toast弹窗 |
||||||
|
* */ |
||||||
|
public class SimpleToast extends UIDialog { |
||||||
|
private static final int MIN_HEIGHT = 36; |
||||||
|
private static final String TOAST_MSG_TIMER = "TOAST_MSG_TIMER"; |
||||||
|
private static final long DEFAULT_DISAPPEAR_DELAY = 5000; |
||||||
|
private static final TimeUnit DEFAULT_TIME_UNIT = TimeUnit.MILLISECONDS; |
||||||
|
|
||||||
|
|
||||||
|
private ScheduledExecutorService timer; |
||||||
|
private int hideHeight = 0; |
||||||
|
private JPanel contentPane; |
||||||
|
private boolean show = false; |
||||||
|
private Window parent; |
||||||
|
private boolean autoDisappear; |
||||||
|
|
||||||
|
public SimpleToast(Window parent, Icon icon, String text, boolean autoDisappear) { |
||||||
|
super(parent); |
||||||
|
this.parent = parent; |
||||||
|
this.autoDisappear = autoDisappear; |
||||||
|
JPanel panel = createContentPane(icon, text); |
||||||
|
init(panel); |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createContentPane(Icon icon, String text) { |
||||||
|
JPanel pane = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
|
||||||
|
UILabel iconLabel = new UILabel(icon); |
||||||
|
iconLabel.setVerticalAlignment(SwingConstants.TOP); |
||||||
|
iconLabel.setBorder(BorderFactory.createEmptyBorder(3, 0, 0, 0)); |
||||||
|
|
||||||
|
|
||||||
|
UILabel textLabel = new UILabel(text); |
||||||
|
pane.add(iconLabel, BorderLayout.WEST); |
||||||
|
pane.add(textLabel, BorderLayout.CENTER); |
||||||
|
pane.setBorder(BorderFactory.createEmptyBorder(8, 15, 8, 15)); |
||||||
|
|
||||||
|
return pane; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private void init(JPanel panel) { |
||||||
|
setFocusable(false); |
||||||
|
setAutoRequestFocus(false); |
||||||
|
setUndecorated(true); |
||||||
|
contentPane = panel; |
||||||
|
initComponent(); |
||||||
|
} |
||||||
|
|
||||||
|
private void initComponent() { |
||||||
|
this.getContentPane().setLayout(null); |
||||||
|
this.getContentPane().add(contentPane); |
||||||
|
Dimension dimension = calculatePreferSize(); |
||||||
|
hideHeight = dimension.height; |
||||||
|
setSize(new Dimension(dimension.width, 0)); |
||||||
|
contentPane.setSize(dimension); |
||||||
|
setRelativeLocation(dimension); |
||||||
|
if (autoDisappear) { |
||||||
|
disappear(contentPane); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void setRelativeLocation(Dimension dimension) { |
||||||
|
int positionX = parent.getLocationOnScreen().x + (parent.getWidth() - dimension.width) / 2; |
||||||
|
int positionY = parent.getLocationOnScreen().y + 10; |
||||||
|
this.setLocation(positionX, positionY); |
||||||
|
} |
||||||
|
|
||||||
|
private Dimension calculatePreferSize() { |
||||||
|
Dimension contentDimension = contentPane.getPreferredSize(); |
||||||
|
int height = Math.max(MIN_HEIGHT, contentDimension.height); |
||||||
|
return new Dimension(contentDimension.width, height); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public void display(JPanel outerPanel) { |
||||||
|
show = true; |
||||||
|
outerPanel.setLocation(0, -hideHeight); |
||||||
|
ScheduledExecutorService tipToolTimer = createToastScheduleExecutorService(); |
||||||
|
tipToolTimer.scheduleAtFixedRate(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
SwingUtilities.invokeLater(()->{ |
||||||
|
displayStep(outerPanel, tipToolTimer); |
||||||
|
}); |
||||||
|
} |
||||||
|
}, 0, 50, TimeUnit.MILLISECONDS); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void displayStep(JPanel outerPanel, ScheduledExecutorService timer) { |
||||||
|
Point point = outerPanel.getLocation(); |
||||||
|
if (point.y >= 0 && !timer.isShutdown()) { |
||||||
|
timer.shutdown(); |
||||||
|
} |
||||||
|
int showDistance = 5 + point.y < 0 ? 5 : -point.y; |
||||||
|
outerPanel.setLocation(point.x, point.y + showDistance); |
||||||
|
Dimension dimension = SimpleToast.this.getSize(); |
||||||
|
SimpleToast.this.setSize(new Dimension(dimension.width, dimension.height + showDistance)); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void disappear(JPanel outerPanel, long delay, TimeUnit timeUnit) { |
||||||
|
timer = createToastScheduleExecutorService(); |
||||||
|
timer.schedule(new DisappearMotion(outerPanel), delay, timeUnit); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* toast消失的动画效果 |
||||||
|
* */ |
||||||
|
class DisappearMotion implements Runnable { |
||||||
|
JPanel panel; |
||||||
|
|
||||||
|
DisappearMotion(JPanel panel) { |
||||||
|
this.panel = panel; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
ScheduledExecutorService tipToolTimer = createToastScheduleExecutorService(); |
||||||
|
tipToolTimer.scheduleAtFixedRate(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
SwingUtilities.invokeLater(()->{ |
||||||
|
disappearStep(tipToolTimer); |
||||||
|
}); |
||||||
|
} |
||||||
|
}, 0, 50, TimeUnit.MILLISECONDS); |
||||||
|
} |
||||||
|
|
||||||
|
void disappearStep(ScheduledExecutorService timer) { |
||||||
|
Point point = panel.getLocation(); |
||||||
|
if (point.y <= -hideHeight && !timer.isShutdown()) { |
||||||
|
timer.shutdown(); |
||||||
|
SimpleToast.this.setVisible(false); |
||||||
|
SimpleToast.this.dispose(); |
||||||
|
SimpleToast.this.show = false; |
||||||
|
} |
||||||
|
panel.setLocation(point.x, point.y - 5); |
||||||
|
Dimension dimension = SimpleToast.this.getSize(); |
||||||
|
SimpleToast.this.setSize(new Dimension(dimension.width, dimension.height - 5)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void disappear(JPanel outerPanel) { |
||||||
|
disappear(outerPanel, DEFAULT_DISAPPEAR_DELAY, DEFAULT_TIME_UNIT); |
||||||
|
} |
||||||
|
|
||||||
|
private ScheduledExecutorService createToastScheduleExecutorService() { |
||||||
|
return ModuleContext.getExecutor().newSingleThreadScheduledExecutor(new NamedThreadFactory(TOAST_MSG_TIMER)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void checkValid() throws Exception { |
||||||
|
} |
||||||
|
|
||||||
|
public void setVisible(boolean visible) { |
||||||
|
super.setVisible(visible); |
||||||
|
if (visible) { |
||||||
|
display(contentPane); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void dispose() { |
||||||
|
super.dispose(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -1,4 +1,4 @@ |
|||||||
package com.fr.design.data.datapane.management.search.searcher; |
package com.fr.design.search; |
||||||
|
|
||||||
/** |
/** |
||||||
* @author Yvan |
* @author Yvan |
@ -1,4 +1,4 @@ |
|||||||
package com.fr.design.data.datapane.management.search.searcher; |
package com.fr.design.search; |
||||||
|
|
||||||
|
|
||||||
/** |
/** |
@ -1,4 +1,4 @@ |
|||||||
package com.fr.design.data.datapane.management.search.control; |
package com.fr.design.search.control; |
||||||
|
|
||||||
|
|
||||||
/** |
/** |
@ -1,4 +1,4 @@ |
|||||||
package com.fr.design.data.datapane.management.search.control; |
package com.fr.design.search.control; |
||||||
|
|
||||||
/** |
/** |
||||||
* @author Yvan |
* @author Yvan |
@ -1,6 +1,6 @@ |
|||||||
package com.fr.design.data.datapane.management.search.event; |
package com.fr.design.search.event; |
||||||
|
|
||||||
import com.fr.design.data.datapane.management.search.searcher.TreeSearchStatus; |
import com.fr.design.search.TreeSearchStatus; |
||||||
|
|
||||||
import java.util.EventObject; |
import java.util.EventObject; |
||||||
|
|
@ -1,4 +1,4 @@ |
|||||||
package com.fr.design.data.datapane.management.search.event; |
package com.fr.design.search.event; |
||||||
|
|
||||||
import java.util.EventListener; |
import java.util.EventListener; |
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue