You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
236 lines
8.3 KiB
236 lines
8.3 KiB
package com.fr.design.gui.icombobox; |
|
|
|
import com.fr.concurrent.NamedThreadFactory; |
|
import com.fr.data.core.DataCoreUtils; |
|
import com.fr.data.core.db.TableProcedure; |
|
import com.fr.data.impl.Connection; |
|
import com.fr.design.DesignerEnvManager; |
|
import com.fr.design.data.datapane.ChoosePane; |
|
import com.fr.design.dialog.FineJOptionPane; |
|
import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; |
|
import com.fr.design.mainframe.DesignerContext; |
|
import com.fr.log.FineLoggerFactory; |
|
import com.fr.module.ModuleContext; |
|
import com.fr.stable.ArrayUtils; |
|
import com.fr.stable.Filter; |
|
import com.fr.stable.StringUtils; |
|
|
|
import javax.swing.JOptionPane; |
|
import javax.swing.JTree; |
|
import javax.swing.SwingWorker; |
|
import javax.swing.event.PopupMenuEvent; |
|
import javax.swing.event.PopupMenuListener; |
|
import javax.swing.tree.DefaultMutableTreeNode; |
|
import javax.swing.tree.DefaultTreeModel; |
|
import javax.swing.tree.TreeCellRenderer; |
|
import javax.swing.tree.TreeNode; |
|
import javax.swing.tree.TreePath; |
|
import java.util.Enumeration; |
|
import java.util.concurrent.ExecutorService; |
|
|
|
/** |
|
* 实现模糊搜索表名的FRTreeComboBox |
|
* FRTreeComboBox:搜索后滚动到首个匹配节点 |
|
* SearchFRTreeComboBox:显示所有匹配的节点 |
|
* |
|
* @author Lucian.Chen |
|
* @version 10.0 |
|
* Created by Lucian.Chen on 2021/4/14 |
|
*/ |
|
public class TableSearchTreeComboBox extends FRTreeComboBox { |
|
// 持有父容器,需要实时获取其他组件值 |
|
private final ChoosePane parent; |
|
/** |
|
* 保证模糊搜索的原子性操作 |
|
*/ |
|
private final ExecutorService singleExecutor = ModuleContext.getExecutor().newSingleThreadScheduledExecutor(new NamedThreadFactory("TableSearchTreeComboBox")); |
|
|
|
public TableSearchTreeComboBox(ChoosePane parent, JTree tree, TreeCellRenderer renderer) { |
|
super(tree, renderer); |
|
this.parent = parent; |
|
initPopupListener(); |
|
} |
|
|
|
protected UIComboBoxEditor createEditor() { |
|
return new TableSearchComboBoxEditor(this); |
|
} |
|
|
|
@Override |
|
protected String pathToString(TreePath path) { |
|
Object obj = ((DefaultMutableTreeNode) path.getLastPathComponent()).getUserObject(); |
|
if (obj instanceof TableProcedure) { |
|
return ((TableProcedure) obj).getName(); |
|
} |
|
return super.pathToString(path); |
|
} |
|
|
|
@Override |
|
public void setSelectedItemString(String _name) { |
|
super.setSelectedItemString(_name); |
|
// 会因为连续两次选中的值一致,导致未触发编辑框联动 |
|
this.getEditor().setItem(_name); |
|
} |
|
|
|
/** |
|
* 执行模糊搜索 |
|
*/ |
|
private void searchExecute() { |
|
UIComboBoxEditor searchEditor = (UIComboBoxEditor) this.getEditor(); |
|
String searchText = (String) searchEditor.getItem(); |
|
singleExecutor.execute(new SwingWorker<Void, Void>() { |
|
@Override |
|
protected Void doInBackground() { |
|
processTableDataNames( |
|
parent.getDSName(), |
|
parent.getConnection(), |
|
parent.getSchema(), |
|
createFilter(searchText)); |
|
return null; |
|
} |
|
|
|
@Override |
|
protected void done() { |
|
expandTree(); |
|
// 输入框获取焦点 |
|
searchEditor.getEditorComponent().requestFocus(); |
|
} |
|
}); |
|
} |
|
|
|
private TableNameFilter createFilter(String text) { |
|
return StringUtils.isEmpty(text) ? EMPTY_FILTER : new TableNameFilter(text); |
|
} |
|
|
|
/** |
|
* 查询数据库表,并构建节点目录 |
|
* |
|
* @param databaseName 数据库名 |
|
* @param connection 数据连接 |
|
* @param schema 模式 |
|
* @param filter 模糊搜索过滤器 |
|
*/ |
|
private void processTableDataNames(String databaseName, Connection connection, String schema, TableNameFilter filter) { |
|
if (tree == null) { |
|
return; |
|
} |
|
DefaultMutableTreeNode rootTreeNode = (DefaultMutableTreeNode) tree.getModel().getRoot(); |
|
rootTreeNode.removeAllChildren(); |
|
|
|
if (connection == null) { |
|
return; |
|
} |
|
try { |
|
schema = StringUtils.isEmpty(schema) ? null : schema; |
|
TableProcedure[] sqlTableArray = DataCoreUtils.getTables(connection, TableProcedure.TABLE, schema, DesignerEnvManager.getEnvManager().isOracleSystemSpace()); |
|
if (ArrayUtils.isNotEmpty(sqlTableArray)) { |
|
ExpandMutableTreeNode tableTreeNode = new ExpandMutableTreeNode(databaseName + "-" + com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_SQL_Table")); |
|
rootTreeNode.add(tableTreeNode); |
|
addArrayNode(tableTreeNode, sqlTableArray, filter); |
|
} |
|
TableProcedure[] sqlViewArray = DataCoreUtils.getTables(connection, TableProcedure.VIEW, schema, DesignerEnvManager.getEnvManager().isOracleSystemSpace()); |
|
if (ArrayUtils.isNotEmpty(sqlViewArray)) { |
|
ExpandMutableTreeNode viewTreeNode = new ExpandMutableTreeNode(databaseName + "-" + com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_SQL_View")); |
|
rootTreeNode.add(viewTreeNode); |
|
addArrayNode(viewTreeNode, sqlViewArray, filter); |
|
} |
|
} catch (Exception e) { |
|
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
|
FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Database_Connection_Failed"), |
|
com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Failed"), JOptionPane.ERROR_MESSAGE); |
|
} |
|
} |
|
|
|
private void addArrayNode(ExpandMutableTreeNode rootNode, TableProcedure[] sqlArray, TableNameFilter filter) { |
|
if (sqlArray != null) { |
|
for (TableProcedure procedure : sqlArray) { |
|
if (filter.accept(procedure)) { |
|
ExpandMutableTreeNode viewChildTreeNode = new ExpandMutableTreeNode(procedure); |
|
rootNode.add(viewChildTreeNode); |
|
} |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* 展开节点 |
|
*/ |
|
private void expandTree() { |
|
((DefaultTreeModel) tree.getModel()).reload(); |
|
// daniel 展开所有tree |
|
TreeNode root = (TreeNode) tree.getModel().getRoot(); |
|
TreePath parent = new TreePath(root); |
|
TreeNode node = (TreeNode) parent.getLastPathComponent(); |
|
for (Enumeration e = node.children(); e.hasMoreElements(); ) { |
|
TreeNode n = (TreeNode) e.nextElement(); |
|
TreePath path = parent.pathByAddingChild(n); |
|
tree.expandPath(path); |
|
} |
|
} |
|
|
|
/** |
|
* 表名模糊搜索实现 |
|
*/ |
|
private static class TableNameFilter implements Filter<TableProcedure> { |
|
private String searchFilter; |
|
|
|
public TableNameFilter() { |
|
} |
|
|
|
public TableNameFilter(String searchFilter) { |
|
this.searchFilter = searchFilter.toLowerCase().trim(); |
|
} |
|
|
|
// 表名匹配 |
|
@Override |
|
public boolean accept(TableProcedure procedure) { |
|
return procedure.getName().toLowerCase().contains(searchFilter); |
|
} |
|
} |
|
|
|
private static final TableNameFilter EMPTY_FILTER = new TableNameFilter() { |
|
public boolean accept(TableProcedure procedure) { |
|
return true; |
|
} |
|
}; |
|
|
|
private void initPopupListener() { |
|
// 点击下拉时触发模糊搜索 |
|
this.addPopupMenuListener(new PopupMenuListener() { |
|
|
|
@Override |
|
public void popupMenuWillBecomeVisible(PopupMenuEvent e) { |
|
searchExecute(); |
|
} |
|
|
|
@Override |
|
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { |
|
|
|
} |
|
|
|
@Override |
|
public void popupMenuCanceled(PopupMenuEvent e) { |
|
|
|
} |
|
}); |
|
} |
|
|
|
/** |
|
* 重写输入框编辑器,实现输入框模糊搜索逻辑 |
|
*/ |
|
private class TableSearchComboBoxEditor extends FrTreeSearchComboBoxEditor { |
|
|
|
public TableSearchComboBoxEditor(FRTreeComboBox comboBox) { |
|
super(comboBox); |
|
} |
|
|
|
@Override |
|
protected void changeHandler() { |
|
if (isSetting()) { |
|
return; |
|
} |
|
setPopupVisible(true); |
|
this.item = textField.getText(); |
|
searchExecute(); |
|
} |
|
} |
|
}
|
|
|