forked from fanruan/design
Browse Source
【问题原因】rt 【改动方案】rt 【review建议】详见开发文档:https://kms.fineres.com/pages/viewpage.action?pageId=340985192feature/x
Yvan
3 years ago
35 changed files with 2272 additions and 90 deletions
@ -0,0 +1,68 @@
|
||||
package com.fr.design.data.datapane.management.clip; |
||||
|
||||
import com.fr.design.data.tabledata.wrapper.AbstractTableDataWrapper; |
||||
import com.fr.general.NameObject; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* 用于数据集的复制粘贴 |
||||
* |
||||
* @author Yvan |
||||
*/ |
||||
public class TableDataTreeClipboard { |
||||
|
||||
/** |
||||
* 数据集名称 - 数据集Wrapper |
||||
*/ |
||||
private Map<String, AbstractTableDataWrapper> clip = new HashMap<>(); |
||||
|
||||
private static class Holder { |
||||
private static final TableDataTreeClipboard INSTANCE = new TableDataTreeClipboard(); |
||||
} |
||||
|
||||
private TableDataTreeClipboard() { |
||||
} |
||||
|
||||
public static TableDataTreeClipboard getInstance() { |
||||
return Holder.INSTANCE; |
||||
} |
||||
|
||||
/** |
||||
* 添加选中的数据集数据到剪切板,覆盖原本剪切板内数据 |
||||
* |
||||
* @param copyMap |
||||
* @return |
||||
*/ |
||||
public void addToClip(Map<String, AbstractTableDataWrapper> copyMap) { |
||||
this.clip = copyMap; |
||||
} |
||||
|
||||
public Map<String, AbstractTableDataWrapper> transferNameObjectArray2Map(NameObject[] selectedNameObjects) { |
||||
Map<String, AbstractTableDataWrapper> resultMap = new HashMap<>(); |
||||
if (selectedNameObjects == null) { |
||||
return resultMap; |
||||
} |
||||
for (NameObject selectedNameObject : selectedNameObjects) { |
||||
resultMap.put(selectedNameObject.getName(), (AbstractTableDataWrapper) selectedNameObject.getObject()); |
||||
} |
||||
return resultMap; |
||||
} |
||||
|
||||
/** |
||||
* 取出剪切板内的所有数据集数据,剪切板不清空 |
||||
* |
||||
* @return |
||||
*/ |
||||
public Map<String, AbstractTableDataWrapper> takeFromClip() { |
||||
return clip; |
||||
} |
||||
|
||||
/** |
||||
* 清空剪切板 |
||||
*/ |
||||
public void reset() { |
||||
clip.clear(); |
||||
} |
||||
} |
@ -0,0 +1,268 @@
|
||||
package com.fr.design.data.datapane.management.search; |
||||
|
||||
import com.fr.data.TableDataSource; |
||||
import com.fr.data.impl.storeproc.StoreProcedure; |
||||
import com.fr.design.DesignModelAdapter; |
||||
import com.fr.design.data.datapane.TableDataTree; |
||||
import com.fr.design.data.datapane.TableDataTreePane; |
||||
import com.fr.design.data.datapane.management.search.event.TreeSearchStatusChangeEvent; |
||||
import com.fr.design.data.datapane.management.search.event.TreeSearchStatusChangeListener; |
||||
import com.fr.design.data.datapane.management.search.searcher.TableDataSearchMode; |
||||
import com.fr.design.data.datapane.management.search.searcher.TableDataTreeSearcher; |
||||
import com.fr.design.data.datapane.management.search.searcher.TreeSearchStatus; |
||||
import com.fr.design.data.datapane.management.search.time.TableDataSearchTimer; |
||||
import com.fr.design.data.datapane.management.search.view.TreeSearchRendererHelper; |
||||
import com.fr.design.data.tabledata.wrapper.TableDataWrapper; |
||||
import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; |
||||
import com.fr.general.NameObject; |
||||
import com.fr.stable.StringUtils; |
||||
|
||||
import javax.swing.SwingUtilities; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.concurrent.atomic.AtomicInteger; |
||||
|
||||
/** |
||||
* 数据集树搜索管理器 |
||||
* |
||||
* @author Yvan |
||||
*/ |
||||
public class TableDataTreeSearchManager { |
||||
|
||||
/** |
||||
* 数据集树搜索器 |
||||
*/ |
||||
private TableDataTreeSearcher treeSearcher; |
||||
|
||||
/** |
||||
* 搜索任务的状态 |
||||
*/ |
||||
private TreeSearchStatus treeSearchStatus; |
||||
|
||||
/** |
||||
* 缓存上次搜索文本,避免重复搜索 |
||||
*/ |
||||
private String lastSearchText; |
||||
|
||||
/** |
||||
* 存储与复原 原本数据集树的UI |
||||
*/ |
||||
private TreeSearchRendererHelper rendererHelper; |
||||
|
||||
/** |
||||
* 取数计数器 |
||||
*/ |
||||
private AtomicInteger count; |
||||
|
||||
/** |
||||
* 搜索状态变化监听 |
||||
*/ |
||||
private List<TreeSearchStatusChangeListener> listeners = new ArrayList<>(); |
||||
|
||||
private TableDataTreeSearchManager() { |
||||
init(); |
||||
} |
||||
|
||||
private void init() { |
||||
this.treeSearchStatus = TreeSearchStatus.SEARCH_NOT_BEGIN; |
||||
} |
||||
|
||||
private static class Holder { |
||||
private static final TableDataTreeSearchManager INSTANCE = new TableDataTreeSearchManager(); |
||||
} |
||||
|
||||
public static TableDataTreeSearchManager getInstance() { |
||||
return 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 searchMode |
||||
* @param tableDataSource |
||||
*/ |
||||
public void switchToSearch(TableDataSearchMode searchMode, TableDataSource tableDataSource) { |
||||
setTreeSearchStatus(TreeSearchStatus.SEARCH_NOT_BEGIN); |
||||
rendererHelper = new TreeSearchRendererHelper(); |
||||
rendererHelper.save(getCurrentTableDataTree()); |
||||
treeSearcher = new TableDataTreeSearcher(); |
||||
treeSearcher.beforeSearch(searchMode, tableDataSource); |
||||
} |
||||
|
||||
/** |
||||
* 获取当前的tableDataTree |
||||
* |
||||
* @return |
||||
*/ |
||||
private TableDataTree getCurrentTableDataTree() { |
||||
DesignModelAdapter<?, ?> currentModelAdapter = DesignModelAdapter.getCurrentModelAdapter(); |
||||
TableDataTreePane tableDataTreePane = (TableDataTreePane) TableDataTreePane.getInstance(currentModelAdapter); |
||||
return tableDataTreePane.getDataTree(); |
||||
} |
||||
|
||||
public boolean isMatchSetsEmpty() { |
||||
return treeSearcher.isMatchSetsEmpty(); |
||||
} |
||||
|
||||
/** |
||||
* 开始搜索 |
||||
* |
||||
* @param searchText |
||||
*/ |
||||
public void startSearch(String searchText) { |
||||
if (isRepeatSearch(searchText)) { |
||||
return; |
||||
} |
||||
setTreeSearchStatus(TreeSearchStatus.SEARCHING); |
||||
rendererHelper.replaceTreeRenderer(getCurrentTableDataTree(), searchText); |
||||
count = new AtomicInteger(treeSearcher.getNotCalculatedSetsSize()); |
||||
System.out.println("count = " + count.intValue()); |
||||
// 计时开始
|
||||
TableDataSearchTimer.getInstance().startClock(); |
||||
treeSearcher.startSearch(searchText); |
||||
} |
||||
|
||||
/** |
||||
* 计数-1 |
||||
*/ |
||||
public void decreaseCount() { |
||||
if (count == null) { |
||||
return; |
||||
} |
||||
int cunrrentCount = count.decrementAndGet(); |
||||
// 减到0后判断状态
|
||||
if (cunrrentCount == 0 && getTreeSearchStatus() == TreeSearchStatus.SEARCHING) { |
||||
completeSearch(); |
||||
} |
||||
} |
||||
|
||||
private boolean isRepeatSearch(String searchText) { |
||||
if (StringUtils.isEmpty(lastSearchText)) { |
||||
lastSearchText = searchText; |
||||
return false; |
||||
} |
||||
return StringUtils.equals(lastSearchText, searchText); |
||||
} |
||||
|
||||
/** |
||||
* 刷新树,更新搜索的结果 |
||||
*/ |
||||
public void updateTableDataTree() { |
||||
getCurrentTableDataTree().refresh4TreeSearch(); |
||||
} |
||||
|
||||
/** |
||||
* 中断搜索 |
||||
*/ |
||||
public void stopSearch() { |
||||
setTreeSearchStatus(TreeSearchStatus.SEARCH_STOPPED); |
||||
TableDataSearchTimer.getInstance().stopClock(); |
||||
count = null; |
||||
treeSearcher.stopSearch(); |
||||
} |
||||
|
||||
/** |
||||
* 搜索完成 |
||||
*/ |
||||
public void completeSearch() { |
||||
setTreeSearchStatus(TreeSearchStatus.SEARCH_COMPLETED); |
||||
TableDataSearchTimer.getInstance().stopClock(); |
||||
count = null; |
||||
treeSearcher.completeSearch(); |
||||
} |
||||
|
||||
/** |
||||
* 切换回工具栏 |
||||
*/ |
||||
public void switchBackToolBar() { |
||||
setTreeSearchStatus(TreeSearchStatus.SEARCH_NOT_BEGIN); |
||||
lastSearchText = null; |
||||
treeSearcher.afterSearch(); |
||||
rendererHelper.restore(getCurrentTableDataTree()); |
||||
} |
||||
|
||||
/** |
||||
* 节点是否应该添加到搜索结果树的根节点中 |
||||
* 只针对数据集节点 |
||||
* |
||||
* @param treeNode 数据集节点 |
||||
* @return |
||||
*/ |
||||
public boolean nodeMatches(ExpandMutableTreeNode treeNode) { |
||||
String nodeName = treeNode.getUserObject().toString(); |
||||
return treeSearcher.nodeMatches(nodeName); |
||||
} |
||||
|
||||
/** |
||||
* 节点是否应该展开 |
||||
* |
||||
* @param treeNode |
||||
* @return |
||||
*/ |
||||
public boolean nodeCanExpand(ExpandMutableTreeNode treeNode) { |
||||
String dsName = treeNode.getUserObject().toString(); |
||||
return treeSearcher.nodeCanExpand(dsName); |
||||
} |
||||
|
||||
/** |
||||
* 处理节点的展开,如果此节点是存储过程,还会处理其子表节点的展开 |
||||
* 只针对数据集节点 |
||||
* |
||||
* @param treeNode |
||||
* @return |
||||
*/ |
||||
public ExpandMutableTreeNode dealWithNodeExpand(ExpandMutableTreeNode treeNode) { |
||||
String tableDataName = treeNode.getUserObject().toString(); |
||||
// 主要还是处理存储过程
|
||||
if (isTreeNodeStoreProcedure(treeNode)) { |
||||
int childCount = treeNode.getChildCount(); |
||||
for (int i = 0; i < childCount; i++) { |
||||
ExpandMutableTreeNode child = (ExpandMutableTreeNode) treeNode.getChildAt(i); |
||||
if (treeSearcher.nodeCanExpand(tableDataName + "_" + child.getUserObject().toString())) { |
||||
child.setExpanded(true); |
||||
} |
||||
} |
||||
} |
||||
if (nodeCanExpand(treeNode)) { |
||||
treeNode.setExpanded(true); |
||||
} |
||||
return treeNode; |
||||
} |
||||
|
||||
/** |
||||
* 判断此节点是否为存储过程 |
||||
* |
||||
* @param treeNode |
||||
* @return |
||||
*/ |
||||
public boolean isTreeNodeStoreProcedure(ExpandMutableTreeNode treeNode) { |
||||
Object userObject = treeNode.getUserObject(); |
||||
if (userObject instanceof NameObject) { |
||||
NameObject nameObject = (NameObject) userObject; |
||||
TableDataWrapper tableDataWrapper = (TableDataWrapper) nameObject.getObject(); |
||||
return tableDataWrapper.getTableData() instanceof StoreProcedure; |
||||
} |
||||
return false; |
||||
} |
||||
} |
@ -0,0 +1,13 @@
|
||||
package com.fr.design.data.datapane.management.search.control; |
||||
|
||||
import com.fr.design.data.datapane.management.search.searcher.TreeSearcher; |
||||
|
||||
/** |
||||
* 搜索任务回调 |
||||
* |
||||
* @author Yvan |
||||
*/ |
||||
public interface TreeSearchCallback { |
||||
|
||||
void done(TreeSearchResult treeSearchResult); |
||||
} |
@ -0,0 +1,37 @@
|
||||
package com.fr.design.data.datapane.management.search.control; |
||||
|
||||
import java.util.List; |
||||
|
||||
/** |
||||
* @author Yvan |
||||
*/ |
||||
public interface TreeSearchResult { |
||||
|
||||
/** |
||||
* 任务结果是否成功 |
||||
* |
||||
* @return |
||||
*/ |
||||
boolean isSuccess(); |
||||
|
||||
/** |
||||
* 数据集名称匹配或者列名匹配时,需要将数据集名称添加到匹配结果集中 |
||||
* |
||||
* @return |
||||
*/ |
||||
List<String> getAddToMatch(); |
||||
|
||||
/** |
||||
* 数据集有列名匹配时,需要添加到展开结果集中 |
||||
* |
||||
* @return |
||||
*/ |
||||
List<String> getAddToExpand(); |
||||
|
||||
/** |
||||
* 数据集完成计算后,需要添加到完成结果集中 |
||||
* |
||||
* @return |
||||
*/ |
||||
List<String> getAddToCalculated(); |
||||
} |
@ -0,0 +1,10 @@
|
||||
package com.fr.design.data.datapane.management.search.control; |
||||
|
||||
/** |
||||
* @author Yvan |
||||
*/ |
||||
public interface TreeSearchTask extends Runnable { |
||||
|
||||
@Override |
||||
void run(); |
||||
} |
@ -0,0 +1,54 @@
|
||||
package com.fr.design.data.datapane.management.search.control.common; |
||||
|
||||
import com.fr.design.data.datapane.management.search.TableDataTreeSearchManager; |
||||
import com.fr.design.data.datapane.management.search.control.TreeSearchCallback; |
||||
import com.fr.design.data.datapane.management.search.control.TreeSearchResult; |
||||
import com.fr.design.data.datapane.management.search.searcher.TableDataTreeSearcher; |
||||
import com.fr.design.data.datapane.management.search.searcher.TreeSearchStatus; |
||||
|
||||
import javax.swing.SwingUtilities; |
||||
|
||||
/** |
||||
* @author Yvan |
||||
*/ |
||||
public class TableDataSearchCallBack implements TreeSearchCallback { |
||||
|
||||
protected TableDataTreeSearcher treeSearcher; |
||||
|
||||
public TableDataSearchCallBack(TableDataTreeSearcher treeSearcher) { |
||||
this.treeSearcher = treeSearcher; |
||||
} |
||||
|
||||
@Override |
||||
public void done(TreeSearchResult treeSearchResult) { |
||||
if (TableDataTreeSearchManager.getInstance().getTreeSearchStatus() != TreeSearchStatus.SEARCHING) { |
||||
return; |
||||
} |
||||
if (treeSearchResult.isSuccess()) { |
||||
// 添加结果
|
||||
addToTreeSearcher(treeSearchResult); |
||||
// 处理UI
|
||||
updateTableDataTree(); |
||||
} |
||||
} |
||||
|
||||
protected void updateTableDataTree() { |
||||
SwingUtilities.invokeLater(() -> { |
||||
if (TableDataTreeSearchManager.getInstance().getTreeSearchStatus() != TreeSearchStatus.SEARCHING) { |
||||
return; |
||||
} |
||||
TableDataTreeSearchManager.getInstance().updateTableDataTree(); |
||||
// todo 没想清楚为啥会这么快结束,暂时取消搜索计数
|
||||
// TableDataTreeSearchManager.getInstance().decreaseCount();
|
||||
}); |
||||
} |
||||
|
||||
protected void addToTreeSearcher(TreeSearchResult treeSearchResult) { |
||||
// 添加到已计算结果集
|
||||
treeSearcher.addToCalculatedSets(treeSearchResult.getAddToCalculated()); |
||||
// 添加到匹配结果集
|
||||
treeSearcher.addToMatchSets(treeSearchResult.getAddToMatch()); |
||||
// 添加到展开结果集
|
||||
treeSearcher.addToCanExpandSets(treeSearchResult.getAddToExpand()); |
||||
} |
||||
} |
@ -0,0 +1,105 @@
|
||||
package com.fr.design.data.datapane.management.search.control.common; |
||||
|
||||
import com.fr.design.data.datapane.management.search.control.TreeSearchResult; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* @author Yvan |
||||
*/ |
||||
public class TableDataSearchResult implements TreeSearchResult { |
||||
|
||||
private boolean success; |
||||
|
||||
private List<String> addToMatch; |
||||
|
||||
private List<String> addToExpand; |
||||
|
||||
private List<String> addToCalculated; |
||||
|
||||
protected TableDataSearchResult(Builder builder) { |
||||
this.success = builder.success; |
||||
this.addToMatch = builder.addToMatch; |
||||
this.addToExpand = builder.addToExpand; |
||||
this.addToCalculated = builder.addToCalculated; |
||||
} |
||||
|
||||
public void setSuccess(boolean success) { |
||||
this.success = success; |
||||
} |
||||
|
||||
public void setAddToMatch(List<String> addToMatch) { |
||||
this.addToMatch = addToMatch; |
||||
} |
||||
|
||||
public void setAddToExpand(List<String> addToExpand) { |
||||
this.addToExpand = addToExpand; |
||||
} |
||||
|
||||
public void setAddToCalculated(List<String> addToCalculated) { |
||||
this.addToCalculated = addToCalculated; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isSuccess() { |
||||
return this.success; |
||||
} |
||||
|
||||
@Override |
||||
public List<String> getAddToMatch() { |
||||
return this.addToMatch; |
||||
} |
||||
|
||||
@Override |
||||
public List<String> getAddToExpand() { |
||||
return this.addToExpand; |
||||
} |
||||
|
||||
@Override |
||||
public List<String> getAddToCalculated() { |
||||
return this.addToCalculated; |
||||
} |
||||
|
||||
public static class Builder { |
||||
|
||||
private boolean success; |
||||
|
||||
private List<String> addToMatch; |
||||
|
||||
private List<String> addToExpand; |
||||
|
||||
private List<String> addToCalculated; |
||||
|
||||
public Builder() { |
||||
this.success = false; |
||||
this.addToMatch = new ArrayList<>(); |
||||
this.addToExpand = new ArrayList<>(); |
||||
this.addToCalculated = new ArrayList<>(); |
||||
} |
||||
|
||||
public Builder buildSuccess(boolean success) { |
||||
this.success = success; |
||||
return this; |
||||
} |
||||
|
||||
public Builder buildAddToMatch(List<String> addToMatch) { |
||||
this.addToMatch = addToMatch; |
||||
return this; |
||||
} |
||||
|
||||
public Builder buildAddToExpand(List<String> addToExpand) { |
||||
this.addToExpand = addToExpand; |
||||
return this; |
||||
} |
||||
|
||||
public Builder buildAddToCalculated(List<String> addToCalculated) { |
||||
this.addToCalculated = addToCalculated; |
||||
return this; |
||||
} |
||||
|
||||
public TableDataSearchResult build() { |
||||
return new TableDataSearchResult(this); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,147 @@
|
||||
package com.fr.design.data.datapane.management.search.control.common; |
||||
|
||||
import com.fr.design.data.datapane.management.search.TableDataTreeSearchManager; |
||||
import com.fr.design.data.datapane.management.search.control.TreeSearchCallback; |
||||
import com.fr.design.data.datapane.management.search.control.TreeSearchResult; |
||||
import com.fr.design.data.datapane.management.search.control.TreeSearchTask; |
||||
import com.fr.design.data.datapane.management.search.searcher.TreeSearchStatus; |
||||
import com.fr.design.data.tabledata.wrapper.StoreProcedureDataWrapper; |
||||
import com.fr.design.data.tabledata.wrapper.TableDataWrapper; |
||||
import com.fr.log.FineLoggerFactory; |
||||
import com.fr.stable.StringUtils; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.HashSet; |
||||
import java.util.List; |
||||
import java.util.Set; |
||||
|
||||
/** |
||||
* @author Yvan |
||||
*/ |
||||
public class TableDataSearchTask implements TreeSearchTask { |
||||
|
||||
/** |
||||
* 用户搜索的文本 |
||||
*/ |
||||
private String searchText; |
||||
|
||||
private TableDataWrapper tableDataWrapper; |
||||
|
||||
private TreeSearchCallback callback; |
||||
|
||||
public TableDataSearchTask(String searchText, TableDataWrapper tableDataWrapper, TreeSearchCallback callback) { |
||||
this.searchText = searchText; |
||||
this.tableDataWrapper = tableDataWrapper; |
||||
this.callback = callback; |
||||
} |
||||
|
||||
@Override |
||||
public void run() { |
||||
TreeSearchResult result; |
||||
try { |
||||
if (TableDataTreeSearchManager.getInstance().getTreeSearchStatus() != TreeSearchStatus.SEARCHING) { |
||||
return; |
||||
} |
||||
if (isTableDataStoreProcedure(tableDataWrapper)) { |
||||
dealWithStoreProcedureTableDataWrapper((StoreProcedureDataWrapper) tableDataWrapper); |
||||
} else { |
||||
dealWithCommonTableDataWrapper(tableDataWrapper); |
||||
} |
||||
} catch (Throwable e) { |
||||
FineLoggerFactory.getLogger().error(e, e.getMessage()); |
||||
dealWithErrorTableDataWrapper(tableDataWrapper); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 处理错误情况 |
||||
* |
||||
* @param tableDataWrapper |
||||
*/ |
||||
private void dealWithErrorTableDataWrapper(TableDataWrapper tableDataWrapper) { |
||||
callback.done(new TableDataSearchResult.Builder().buildSuccess(false).build()); |
||||
} |
||||
|
||||
/** |
||||
* 处理普通数据集的搜索与匹配 |
||||
* |
||||
* @param tableDataWrapper |
||||
*/ |
||||
private void dealWithCommonTableDataWrapper(TableDataWrapper tableDataWrapper) { |
||||
String tableDataName = tableDataWrapper.getTableDataName(); |
||||
boolean isTableDataNameMatch = isMatchSearch(tableDataName, searchText); |
||||
List<String> columnNameList = tableDataWrapper.calculateColumnNameList(); |
||||
boolean isColumnMatch = columnNameList.stream().anyMatch(columnName -> isMatchSearch(columnName, searchText)); |
||||
TableDataSearchResult result = new TableDataSearchResult.Builder() |
||||
.buildSuccess(true) |
||||
.buildAddToMatch(isTableDataNameMatch || isColumnMatch ? Arrays.asList(tableDataName) : new ArrayList<>()) |
||||
.buildAddToExpand(isColumnMatch ? Arrays.asList(tableDataName) : new ArrayList<>()) |
||||
.buildAddToCalculated(Arrays.asList(tableDataName)) |
||||
.build(); |
||||
callback.done(result); |
||||
} |
||||
|
||||
/** |
||||
* 处理存储过程的搜索与匹配 |
||||
* |
||||
* @param procedureDataWrapper |
||||
*/ |
||||
private void dealWithStoreProcedureTableDataWrapper(StoreProcedureDataWrapper procedureDataWrapper) { |
||||
// 存储过程数据集名称,例如 Proc1_Table1
|
||||
String tableDataName = procedureDataWrapper.getTableDataName(); |
||||
// 存储过程名称,例如 Proc1
|
||||
String storeProcedureName = procedureDataWrapper.getStoreprocedureName(); |
||||
// 存储过程子表名称,例如 Table1
|
||||
String tableName = tableDataName.replaceFirst(storeProcedureName, StringUtils.EMPTY).replaceFirst("_", StringUtils.EMPTY); |
||||
boolean isStoreProcedureNameMatch = isMatchSearch(storeProcedureName, searchText); |
||||
boolean isTableNameMatch = isMatchSearch(tableName, searchText); |
||||
// 再处理子表的columns
|
||||
List<String> columnNameList = tableDataWrapper.calculateColumnNameList(); |
||||
boolean isColumnMatch = columnNameList.stream().anyMatch(columnName -> isMatchSearch(columnName, searchText)); |
||||
Set<String> addToMatch = new HashSet<>(); |
||||
Set<String> addToExpand = new HashSet<>(); |
||||
Set<String> addToCalculated = new HashSet<>(); |
||||
if (isStoreProcedureNameMatch) { |
||||
addToMatch.add(storeProcedureName); |
||||
} |
||||
if (isTableNameMatch) { |
||||
addToMatch.add(storeProcedureName); |
||||
addToExpand.add(storeProcedureName); |
||||
} |
||||
if (isColumnMatch) { |
||||
addToMatch.add(storeProcedureName); |
||||
addToExpand.add(storeProcedureName); |
||||
// 这里有重名风险,所以要添加 “Proc1_Table1”,在结果树展示的时候再去处理
|
||||
addToExpand.add(tableDataName); |
||||
} |
||||
addToCalculated.add(tableDataName); |
||||
TableDataSearchResult result = new TableDataSearchResult.Builder() |
||||
.buildSuccess(true) |
||||
.buildAddToMatch(new ArrayList<>(addToMatch)) |
||||
.buildAddToExpand(new ArrayList<>(addToExpand)) |
||||
.buildAddToCalculated(new ArrayList<>(addToCalculated)) |
||||
.build(); |
||||
callback.done(result); |
||||
} |
||||
|
||||
/** |
||||
* 判断TableDataWrapper内的TableData是否为存储过程 |
||||
* |
||||
* @param tableDataWrapper |
||||
* @return |
||||
*/ |
||||
private boolean isTableDataStoreProcedure(TableDataWrapper tableDataWrapper) { |
||||
return tableDataWrapper instanceof StoreProcedureDataWrapper; |
||||
} |
||||
|
||||
/** |
||||
* 判断是否匹配搜索文本,不区分大小写 |
||||
* |
||||
* @param str |
||||
* @return |
||||
*/ |
||||
private boolean isMatchSearch(String str, String searchText) { |
||||
return str.toUpperCase().contains(searchText.toUpperCase()); |
||||
} |
||||
} |
@ -0,0 +1,17 @@
|
||||
package com.fr.design.data.datapane.management.search.control.pre; |
||||
|
||||
import com.fr.design.data.datapane.management.search.control.common.TableDataSearchCallBack; |
||||
import com.fr.design.data.datapane.management.search.searcher.TableDataTreeSearcher; |
||||
|
||||
/** |
||||
* 预取数任务回调 |
||||
* |
||||
* @author Yvan |
||||
*/ |
||||
public class TableDataPreSearchCallBack extends TableDataSearchCallBack { |
||||
|
||||
|
||||
public TableDataPreSearchCallBack(TableDataTreeSearcher treeSearcher) { |
||||
super(treeSearcher); |
||||
} |
||||
} |
@ -0,0 +1,40 @@
|
||||
package com.fr.design.data.datapane.management.search.control.pre; |
||||
|
||||
import com.fr.design.data.datapane.management.search.control.TreeSearchResult; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* 预取数任务结果,不需要回调,因此空实现即可 |
||||
* |
||||
* @author Yvan |
||||
*/ |
||||
public class TableDataPreSearchResult implements TreeSearchResult { |
||||
|
||||
private boolean success; |
||||
|
||||
public TableDataPreSearchResult(boolean success) { |
||||
this.success = success; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isSuccess() { |
||||
return this.success; |
||||
} |
||||
|
||||
@Override |
||||
public List<String> getAddToMatch() { |
||||
return new ArrayList<>(); |
||||
} |
||||
|
||||
@Override |
||||
public List<String> getAddToExpand() { |
||||
return new ArrayList<>(); |
||||
} |
||||
|
||||
@Override |
||||
public List<String> getAddToCalculated() { |
||||
return new ArrayList<>(); |
||||
} |
||||
} |
@ -0,0 +1,42 @@
|
||||
package com.fr.design.data.datapane.management.search.control.pre; |
||||
|
||||
import com.fr.design.data.datapane.management.search.control.TreeSearchTask; |
||||
import com.fr.design.data.datapane.management.search.control.common.TableDataSearchResult; |
||||
import com.fr.design.data.datapane.management.search.control.TreeSearchCallback; |
||||
import com.fr.design.data.datapane.management.search.control.TreeSearchResult; |
||||
import com.fr.design.data.tabledata.wrapper.TableDataWrapper; |
||||
import com.fr.log.FineLoggerFactory; |
||||
|
||||
/** |
||||
* 预取数任务 |
||||
* |
||||
* @author Yvan |
||||
*/ |
||||
public class TableDataPreSearchTask implements TreeSearchTask { |
||||
|
||||
private TreeSearchCallback callback; |
||||
|
||||
private TableDataWrapper tableDataWrapper; |
||||
|
||||
public TableDataPreSearchTask(TreeSearchCallback callback, TableDataWrapper tableDataWrapper) { |
||||
this.callback = callback; |
||||
this.tableDataWrapper = tableDataWrapper; |
||||
} |
||||
|
||||
@Override |
||||
public void run() { |
||||
TreeSearchResult result; |
||||
try { |
||||
tableDataWrapper.calculateColumnNameList(); |
||||
result = new TableDataSearchResult.Builder() |
||||
.buildSuccess(true) |
||||
.build(); |
||||
} catch (Exception e) { |
||||
FineLoggerFactory.getLogger().error(e, "calculate table data {} failed", tableDataWrapper.getTableDataName()); |
||||
result = new TableDataSearchResult.Builder() |
||||
.buildSuccess(false) |
||||
.build(); |
||||
} |
||||
callback.done(result); |
||||
} |
||||
} |
@ -0,0 +1,22 @@
|
||||
package com.fr.design.data.datapane.management.search.event; |
||||
|
||||
import com.fr.design.data.datapane.management.search.searcher.TreeSearchStatus; |
||||
|
||||
import java.util.EventObject; |
||||
|
||||
/** |
||||
* @author Yvan |
||||
*/ |
||||
public class TreeSearchStatusChangeEvent extends EventObject { |
||||
|
||||
private TreeSearchStatus status; |
||||
|
||||
public TreeSearchStatusChangeEvent(Object source) { |
||||
super(source); |
||||
this.status = (TreeSearchStatus) source; |
||||
} |
||||
|
||||
public TreeSearchStatus getTreeSearchStatus() { |
||||
return status; |
||||
} |
||||
} |
@ -0,0 +1,11 @@
|
||||
package com.fr.design.data.datapane.management.search.event; |
||||
|
||||
import java.util.EventListener; |
||||
|
||||
/** |
||||
* @author Yvan |
||||
*/ |
||||
public interface TreeSearchStatusChangeListener extends EventListener { |
||||
|
||||
void updateTreeSearchChange(TreeSearchStatusChangeEvent event); |
||||
} |
@ -0,0 +1,212 @@
|
||||
package com.fr.design.data.datapane.management.search.pane; |
||||
|
||||
import com.fr.base.svg.IconUtils; |
||||
import com.fr.design.constants.UIConstants; |
||||
import com.fr.design.data.datapane.TableDataTree; |
||||
import com.fr.design.data.datapane.management.search.TableDataTreeSearchManager; |
||||
import com.fr.design.data.datapane.management.search.event.TreeSearchStatusChangeEvent; |
||||
import com.fr.design.data.datapane.management.search.event.TreeSearchStatusChangeListener; |
||||
import com.fr.design.data.datapane.management.search.searcher.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.FlowLayout; |
||||
import java.awt.event.MouseAdapter; |
||||
import java.awt.event.MouseEvent; |
||||
import java.awt.event.MouseListener; |
||||
|
||||
/** |
||||
* @author Yvan |
||||
*/ |
||||
public class TableDataSearchRemindPane extends JPanel implements TreeSearchStatusChangeListener { |
||||
|
||||
private RemindPane remindPane; |
||||
private TreePane treePane; |
||||
|
||||
public TableDataSearchRemindPane(TableDataTree tableDataTree) { |
||||
this.setLayout(new BorderLayout()); |
||||
remindPane = new RemindPane(); |
||||
treePane = new TreePane(tableDataTree); |
||||
// 初始状态
|
||||
this.add(remindPane, BorderLayout.NORTH); |
||||
this.add(treePane, BorderLayout.CENTER); |
||||
TableDataTreeSearchManager.getInstance().registerTreeSearchStatusChangeListener(this); |
||||
} |
||||
|
||||
/** |
||||
* 根据搜索状态变化,来调整自身面板的显示 |
||||
* |
||||
* @param event |
||||
*/ |
||||
@Override |
||||
public void updateTreeSearchChange(TreeSearchStatusChangeEvent event) { |
||||
TreeSearchStatus status = event.getTreeSearchStatus(); |
||||
if (status == TreeSearchStatus.SEARCH_NOT_BEGIN) { |
||||
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 = TableDataTreeSearchManager.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 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(TableDataTree tableDataTree) { |
||||
init(tableDataTree); |
||||
} |
||||
|
||||
private void init(TableDataTree tableDataTree) { |
||||
|
||||
scrollPane = new UIScrollPane(tableDataTree); |
||||
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(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 class RemindPane extends JPanel implements TreeSearchStatusChange { |
||||
|
||||
private static final String IN_SEARCHING = "Fine-Design_Tree_Search_In_Searching"; |
||||
private static final String STOP_SEARCHING = "Fine-Design_Tree_Search_Stop_Search"; |
||||
private static final String SEARCHING_STOPPED = "Fine-Design_Tree_Search_Search_Stopped"; |
||||
private static final String DONE_SEARCHING = "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(); |
||||
stopLabel = new UILabel(); |
||||
stopLabel.setForeground(UIConstants.NORMAL_BLUE); |
||||
stopSearch = new MouseAdapter() { |
||||
@Override |
||||
public void mouseClicked(MouseEvent e) { |
||||
TableDataTreeSearchManager.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.setText(IN_SEARCHING); |
||||
this.stopLabel.setText(STOP_SEARCHING); |
||||
this.stopLabel.setVisible(true); |
||||
this.setVisible(true); |
||||
} |
||||
|
||||
@Override |
||||
public void onStoppedSearching() { |
||||
this.textLabel.setText(SEARCHING_STOPPED); |
||||
this.stopLabel.setVisible(false); |
||||
this.setVisible(true); |
||||
} |
||||
|
||||
@Override |
||||
public void onDoneSearching(boolean matchSetsEmpty) { |
||||
this.textLabel.setText(DONE_SEARCHING); |
||||
this.stopLabel.setVisible(false); |
||||
this.setVisible(true); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,192 @@
|
||||
package com.fr.design.data.datapane.management.search.pane; |
||||
|
||||
import com.fr.base.svg.IconUtils; |
||||
import com.fr.design.DesignModelAdapter; |
||||
import com.fr.design.constants.UIConstants; |
||||
import com.fr.design.data.datapane.TableDataTreePane; |
||||
import com.fr.design.data.datapane.management.search.TableDataTreeSearchManager; |
||||
import com.fr.design.data.datapane.management.search.searcher.TreeSearchStatus; |
||||
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.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.Panel; |
||||
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; |
||||
|
||||
/** |
||||
* @author Yvan |
||||
*/ |
||||
public class TreeSearchToolbarPane extends Panel { |
||||
|
||||
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) { |
||||
TableDataTreeSearchManager.getInstance().startSearch(searchTextField.getText()); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
public TreeSearchToolbarPane(UIToolbar toolbar) { |
||||
this.toolbar = toolbar; |
||||
this.setLayout(FRGUIPaneFactory.createBorderLayout()); |
||||
initToolbarPane(); |
||||
initSearchPane(); |
||||
initContentPane(); |
||||
add(contentPane, BorderLayout.CENTER); |
||||
setPreferredSize(new Dimension(240, 30)); |
||||
} |
||||
|
||||
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, 8, 0, 0)); |
||||
searchLabel.addMouseListener(new MouseAdapter() { |
||||
@Override |
||||
public void mouseClicked(MouseEvent e) { |
||||
// do nothing
|
||||
} |
||||
}); |
||||
// 中间输入框
|
||||
searchTextField = new UITextField(); |
||||
searchTextField.setBorderPainted(false); |
||||
searchTextField.setPlaceholder(Toolkit.i18nText("Fine-Design_Tree_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) { |
||||
} |
||||
}); |
||||
// 右侧返回图标
|
||||
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, 0)); |
||||
returnLabel.addMouseListener(new MouseAdapter() { |
||||
@Override |
||||
public void mouseClicked(MouseEvent e) { |
||||
searchTextField.setText(StringUtils.EMPTY); |
||||
TableDataTreeSearchManager.getInstance().switchBackToolBar(); |
||||
switchPane(TOOLBAR_PANE); |
||||
} |
||||
}); |
||||
|
||||
searchPane.add(searchLabel, BorderLayout.WEST); |
||||
searchPane.add(searchTextField, BorderLayout.CENTER); |
||||
searchPane.add(returnLabel, BorderLayout.EAST); |
||||
} |
||||
|
||||
private void dealWithTextChange() { |
||||
// 判断搜索是否正在进行
|
||||
if (StringUtils.isEmpty(searchTextField.getText()) && TableDataTreeSearchManager.getInstance().getTreeSearchStatus() != TreeSearchStatus.SEARCH_NOT_BEGIN) { |
||||
TableDataTreeSearchManager.getInstance().setTreeSearchStatus(TreeSearchStatus.SEARCH_NOT_BEGIN); |
||||
DesignModelAdapter<?, ?> currentModelAdapter = DesignModelAdapter.getCurrentModelAdapter(); |
||||
TableDataTreePane tableDataTreePane = (TableDataTreePane) TableDataTreePane.getInstance(currentModelAdapter); |
||||
tableDataTreePane.refreshDockingView(); |
||||
} |
||||
} |
||||
|
||||
private void initToolbarPane() { |
||||
toolbarPane = new JPanel(); |
||||
toolbarPane.setLayout(FRGUIPaneFactory.createBorderLayout()); |
||||
toolbarPane.add(toolbar, BorderLayout.CENTER); |
||||
} |
||||
|
||||
/** |
||||
* 交换当前面板层级 |
||||
*/ |
||||
public void switchPane(String name) { |
||||
if (StringUtils.equals(name, TOOLBAR_PANE)) { |
||||
searchTextField.removeKeyListener(enterPressed); |
||||
searchTextField.setText(StringUtils.EMPTY); |
||||
} else if (StringUtils.equals(name, SEARCH_PANE)) { |
||||
searchTextField.addKeyListener(enterPressed); |
||||
} |
||||
cardLayout.show(contentPane, name); |
||||
} |
||||
|
||||
public void setPlaceHolder(String placeHolder) { |
||||
this.searchTextField.setPlaceholder(placeHolder); |
||||
} |
||||
} |
@ -0,0 +1,38 @@
|
||||
package com.fr.design.data.datapane.management.search.searcher; |
||||
|
||||
/** |
||||
* 搜索模式 |
||||
* |
||||
* @author Yvan |
||||
*/ |
||||
public enum TableDataSearchMode { |
||||
|
||||
/** |
||||
* 搜索模板数据集 |
||||
*/ |
||||
TEMPLATE_TABLE_DATA(0), |
||||
|
||||
/** |
||||
* 搜索服务器数据集 |
||||
*/ |
||||
SERVER_TABLE_DATA(1); |
||||
|
||||
private final int mode; |
||||
|
||||
TableDataSearchMode(int mode) { |
||||
this.mode = mode; |
||||
} |
||||
|
||||
public int getMode() { |
||||
return mode; |
||||
} |
||||
|
||||
public static TableDataSearchMode match(int mode) { |
||||
for (TableDataSearchMode searchMode : TableDataSearchMode.values()) { |
||||
if (searchMode.getMode() == mode) { |
||||
return searchMode; |
||||
} |
||||
} |
||||
return TEMPLATE_TABLE_DATA; |
||||
} |
||||
} |
@ -0,0 +1,174 @@
|
||||
package com.fr.design.data.datapane.management.search.searcher; |
||||
|
||||
import com.fr.concurrent.NamedThreadFactory; |
||||
import com.fr.data.TableDataSource; |
||||
import com.fr.design.data.DesignTableDataManager; |
||||
import com.fr.design.data.datapane.management.search.TableDataTreeSearchManager; |
||||
import com.fr.design.data.datapane.management.search.control.common.TableDataSearchCallBack; |
||||
import com.fr.design.data.datapane.management.search.control.common.TableDataSearchTask; |
||||
import com.fr.design.data.datapane.management.search.control.pre.TableDataPreSearchCallBack; |
||||
import com.fr.design.data.datapane.management.search.control.pre.TableDataPreSearchTask; |
||||
import com.fr.design.data.tabledata.wrapper.TableDataWrapper; |
||||
|
||||
import javax.swing.event.ChangeEvent; |
||||
import javax.swing.event.ChangeListener; |
||||
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.function.Consumer; |
||||
|
||||
/** |
||||
* @author Yvan |
||||
*/ |
||||
public class TableDataTreeSearcher implements TreeSearcher { |
||||
|
||||
private ExecutorService executorService; |
||||
|
||||
private Map<String, TableDataWrapper> allWrappers = new ConcurrentHashMap<>(); |
||||
|
||||
private final Set<String> calculatedSets = new HashSet<>(); |
||||
|
||||
private final Set<String> notCalculatedSets = new HashSet<>(); |
||||
|
||||
private final Set<String> matchSets = new HashSet<>(); |
||||
|
||||
private final Set<String> canExpandSets = new HashSet<>(); |
||||
|
||||
public TableDataTreeSearcher() { |
||||
initListener(); |
||||
} |
||||
|
||||
private void initListener() { |
||||
DesignTableDataManager.addDsChangeListener(new ChangeListener() { |
||||
@Override |
||||
public void stateChanged(ChangeEvent e) { |
||||
allWrappers.keySet().stream().filter(DesignTableDataManager::isDsNameChanged).forEach(new Consumer<String>() { |
||||
@Override |
||||
public void accept(String key) { |
||||
TableDataWrapper oldWrapper = allWrappers.remove(key); |
||||
calculatedSets.remove(key); |
||||
notCalculatedSets.remove(key); |
||||
matchSets.remove(key); |
||||
canExpandSets.remove(key); |
||||
String newName = DesignTableDataManager.getChangedDsNameByOldDsName(key); |
||||
TableDataWrapper newWrapper = DesignTableDataManager.getAllEditingDataSet(DesignTableDataManager.getEditingTableDataSource()).get(newName); |
||||
allWrappers.put(newName, newWrapper); |
||||
notCalculatedSets.add(newName); |
||||
} |
||||
}); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
public boolean isMatchSetsEmpty() { |
||||
return matchSets.isEmpty(); |
||||
} |
||||
|
||||
public int getNotCalculatedSetsSize() { |
||||
return notCalculatedSets.size(); |
||||
} |
||||
|
||||
public synchronized void addToCalculatedSets(List<String> tableDataNames) { |
||||
for (String tableDataName : tableDataNames) { |
||||
TableDataWrapper tableDataWrapper = allWrappers.get(tableDataName); |
||||
if (tableDataWrapper == null) { |
||||
return; |
||||
} |
||||
notCalculatedSets.remove(tableDataName); |
||||
calculatedSets.add(tableDataName); |
||||
} |
||||
} |
||||
|
||||
public synchronized void addToMatchSets(List<String> matchNodeNames) { |
||||
matchSets.addAll(matchNodeNames); |
||||
} |
||||
|
||||
public synchronized void addToCanExpandSets(List<String> canExpandNodeNames) { |
||||
canExpandSets.addAll(canExpandNodeNames); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 正式搜索前,预加载一下数据集列名 |
||||
* |
||||
* @param searchMode |
||||
* @param tableDataSource |
||||
*/ |
||||
public void beforeSearch(TableDataSearchMode searchMode, TableDataSource tableDataSource) { |
||||
executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new NamedThreadFactory(TableDataTreeSearchManager.class)); |
||||
collectTableDataWrappers(searchMode, tableDataSource); |
||||
preCalculateColumns(); |
||||
} |
||||
|
||||
/** |
||||
* 预先对数据集进行取列名的操作,提升用户搜索体验 |
||||
*/ |
||||
private void preCalculateColumns() { |
||||
for (String notCalculatedSet : notCalculatedSets) { |
||||
TableDataWrapper tableDataWrapper = allWrappers.get(notCalculatedSet); |
||||
if (TableDataTreeSearchManager.getInstance().getTreeSearchStatus() == TreeSearchStatus.SEARCH_NOT_BEGIN) { |
||||
executorService.execute(new TableDataPreSearchTask(new TableDataPreSearchCallBack(this), tableDataWrapper)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 收集下此次搜索需要进行取数的TableDataWrapper |
||||
* |
||||
* @param searchSubject |
||||
* @param tableDataSource |
||||
*/ |
||||
private void collectTableDataWrappers(TableDataSearchMode searchSubject, TableDataSource tableDataSource) { |
||||
Map<String, TableDataWrapper> dataSet = searchSubject == TableDataSearchMode.TEMPLATE_TABLE_DATA ? |
||||
DesignTableDataManager.getTemplateDataSet(tableDataSource) : |
||||
DesignTableDataManager.getGlobalDataSet(); |
||||
// 转化一下存储过程
|
||||
Map<String, TableDataWrapper> setIncludingProcedure = DesignTableDataManager.getAllDataSetIncludingProcedure(dataSet); |
||||
notCalculatedSets.addAll(setIncludingProcedure.keySet()); |
||||
allWrappers.putAll(setIncludingProcedure); |
||||
} |
||||
|
||||
@Override |
||||
public void startSearch(String searchText) { |
||||
for (String notCalculatedSet : notCalculatedSets) { |
||||
TableDataWrapper tableDataWrapper = allWrappers.get(notCalculatedSet); |
||||
if (TableDataTreeSearchManager.getInstance().getTreeSearchStatus() == TreeSearchStatus.SEARCHING) { |
||||
executorService.execute(new TableDataSearchTask(searchText, tableDataWrapper, new TableDataSearchCallBack(this))); |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void stopSearch() { |
||||
reset(); |
||||
} |
||||
|
||||
@Override |
||||
public void completeSearch() { |
||||
reset(); |
||||
} |
||||
|
||||
private void reset() { |
||||
matchSets.clear(); |
||||
canExpandSets.clear(); |
||||
calculatedSets.clear(); |
||||
notCalculatedSets.addAll(allWrappers.keySet()); |
||||
} |
||||
|
||||
public void afterSearch() { |
||||
allWrappers.clear(); |
||||
executorService.shutdownNow(); |
||||
} |
||||
|
||||
public boolean nodeMatches(String dsName) { |
||||
return matchSets.contains(dsName); |
||||
} |
||||
|
||||
public boolean nodeCanExpand(String dsName) { |
||||
return canExpandSets.contains(dsName); |
||||
} |
||||
} |
@ -0,0 +1,24 @@
|
||||
package com.fr.design.data.datapane.management.search.searcher; |
||||
|
||||
/** |
||||
* @author Yvan |
||||
*/ |
||||
public enum TreeSearchStatus { |
||||
|
||||
/** |
||||
* 搜索未开始 |
||||
*/ |
||||
SEARCH_NOT_BEGIN, |
||||
/** |
||||
* 搜索中 |
||||
*/ |
||||
SEARCHING, |
||||
/** |
||||
* 搜索已停止 |
||||
*/ |
||||
SEARCH_STOPPED, |
||||
/** |
||||
* 搜索已完成 |
||||
*/ |
||||
SEARCH_COMPLETED; |
||||
} |
@ -0,0 +1,27 @@
|
||||
package com.fr.design.data.datapane.management.search.searcher; |
||||
|
||||
|
||||
/** |
||||
* 用于搜索RefreshableJTree数据的搜索器 |
||||
* |
||||
* @author Yvan |
||||
*/ |
||||
public interface TreeSearcher { |
||||
|
||||
/** |
||||
* 开始搜索 |
||||
* |
||||
* @param text |
||||
*/ |
||||
void startSearch(String text); |
||||
|
||||
/** |
||||
* 停止搜索 |
||||
*/ |
||||
void stopSearch(); |
||||
|
||||
/** |
||||
* 搜索完成 |
||||
*/ |
||||
void completeSearch(); |
||||
} |
@ -0,0 +1,54 @@
|
||||
package com.fr.design.data.datapane.management.search.time; |
||||
|
||||
import com.fr.concurrent.NamedThreadFactory; |
||||
|
||||
import java.util.concurrent.Executors; |
||||
import java.util.concurrent.ScheduledExecutorService; |
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
/** |
||||
* 搜索任务定时器 |
||||
* |
||||
* @author Yvan |
||||
*/ |
||||
public class TableDataSearchTimer { |
||||
|
||||
/** |
||||
* 定时器 |
||||
*/ |
||||
private ScheduledExecutorService scheduler; |
||||
|
||||
/** |
||||
* 定时任务 |
||||
*/ |
||||
private TableDataSearchTimerTask timeTask; |
||||
|
||||
/** |
||||
* 最大单次startSearch的时间 |
||||
*/ |
||||
public static final long MAX_SEARCH_TIME = 5000; |
||||
|
||||
private TableDataSearchTimer() { |
||||
} |
||||
|
||||
private static class Holder { |
||||
private static final TableDataSearchTimer INSTANCE = new TableDataSearchTimer(); |
||||
} |
||||
|
||||
public static TableDataSearchTimer getInstance() { |
||||
return Holder.INSTANCE; |
||||
} |
||||
|
||||
public void startClock() { |
||||
this.scheduler = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory(TableDataSearchTimer.class)); |
||||
this.timeTask = new TableDataSearchTimerTask(); |
||||
scheduler.schedule(timeTask, MAX_SEARCH_TIME, TimeUnit.MILLISECONDS); |
||||
} |
||||
|
||||
public void stopClock() { |
||||
this.timeTask = null; |
||||
if (this.scheduler != null) { |
||||
this.scheduler.shutdownNow(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,21 @@
|
||||
package com.fr.design.data.datapane.management.search.time; |
||||
|
||||
import com.fr.design.data.datapane.management.search.TableDataTreeSearchManager; |
||||
import com.fr.design.data.datapane.management.search.searcher.TreeSearchStatus; |
||||
|
||||
/** |
||||
* @author Yvan |
||||
*/ |
||||
public class TableDataSearchTimerTask implements Runnable { |
||||
|
||||
public TableDataSearchTimerTask() { |
||||
} |
||||
|
||||
@Override |
||||
public void run() { |
||||
// 最大单次搜索时间过后,将结束搜索
|
||||
if (TableDataTreeSearchManager.getInstance().getTreeSearchStatus() == TreeSearchStatus.SEARCHING) { |
||||
TableDataTreeSearchManager.getInstance().completeSearch(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,82 @@
|
||||
package com.fr.design.data.datapane.management.search.view; |
||||
|
||||
import com.fr.design.data.datapane.TableDataTree; |
||||
|
||||
import javax.swing.JTree; |
||||
import javax.swing.tree.DefaultTreeCellRenderer; |
||||
import javax.swing.tree.TreeCellRenderer; |
||||
import java.awt.Component; |
||||
import java.util.regex.Pattern; |
||||
|
||||
/** |
||||
* @author Yvan |
||||
*/ |
||||
public class TreeSearchRendererHelper { |
||||
|
||||
/** |
||||
* 缓存下原来的渲染器 |
||||
*/ |
||||
private TreeCellRenderer originTreeCellRenderer; |
||||
|
||||
public TreeSearchRendererHelper() { |
||||
} |
||||
|
||||
public TreeCellRenderer getOriginTreeCellRenderer() { |
||||
return originTreeCellRenderer; |
||||
} |
||||
|
||||
public void setOriginTreeCellRenderer(TreeCellRenderer originTreeCellRenderer) { |
||||
this.originTreeCellRenderer = originTreeCellRenderer; |
||||
} |
||||
|
||||
public void replaceTreeRenderer(TableDataTree tableDataTree, String searchText) { |
||||
tableDataTree.setCellRenderer(getNewTreeCellRenderer(searchText)); |
||||
} |
||||
|
||||
public void save(TableDataTree tableDataTree) { |
||||
if (getOriginTreeCellRenderer() == null) { |
||||
setOriginTreeCellRenderer(tableDataTree.getTableDataTreeCellRenderer()); |
||||
} |
||||
} |
||||
|
||||
public void restore(TableDataTree tableDataTree) { |
||||
if (getOriginTreeCellRenderer() != null) { |
||||
tableDataTree.setCellRenderer(getOriginTreeCellRenderer()); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 获取新树渲染器,也就是搜索结果树的TreeCellRenderer,主要是为了文本高亮 |
||||
* |
||||
* @param searchText |
||||
* @return |
||||
*/ |
||||
private TreeCellRenderer getNewTreeCellRenderer(String searchText) { |
||||
return new DefaultTreeCellRenderer() { |
||||
@Override |
||||
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { |
||||
Component treeCellRendererComponent = getOriginTreeCellRenderer().getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus); |
||||
if (treeCellRendererComponent instanceof DefaultTreeCellRenderer) { |
||||
DefaultTreeCellRenderer defaultTreeCellRenderer = (DefaultTreeCellRenderer) treeCellRendererComponent; |
||||
String text = defaultTreeCellRenderer.getText(); |
||||
defaultTreeCellRenderer.setText(getHighlightText(text, searchText)); |
||||
} |
||||
return treeCellRendererComponent; |
||||
} |
||||
}; |
||||
} |
||||
|
||||
private String getHighlightText(String text, String textToHighlight) { |
||||
String highLightTemplate = "<font color=\"blue\">$1</font>"; |
||||
if (textToHighlight.length() == 0) { |
||||
return text; |
||||
} |
||||
try { |
||||
text = text.replaceAll("(?i)(" + Pattern.quote(textToHighlight) + ")", highLightTemplate); |
||||
} catch (Exception e) { |
||||
return text; |
||||
} |
||||
text = "<html>" + text + "</html>"; |
||||
return text; |
||||
} |
||||
} |
@ -0,0 +1,107 @@
|
||||
package com.fr.design.data.tabledata.paste; |
||||
|
||||
import com.fr.base.TableData; |
||||
import com.fr.data.TableDataSource; |
||||
import com.fr.design.DesignModelAdapter; |
||||
import com.fr.design.data.DesignTableDataManager; |
||||
import com.fr.design.data.datapane.TableDataTreePane; |
||||
import com.fr.design.data.tabledata.tabledatapane.AbstractTableDataPane; |
||||
import com.fr.design.data.tabledata.wrapper.AbstractTableDataWrapper; |
||||
import com.fr.design.data.tabledata.wrapper.TableDataWrapper; |
||||
import com.fr.design.data.tabledata.wrapper.TemplateTableDataWrapper; |
||||
import com.fr.form.ui.DictionaryContainer; |
||||
import com.fr.form.ui.Widget; |
||||
import com.fr.report.cell.tabledata.ElementUsedTableDataProvider; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.Set; |
||||
|
||||
/** |
||||
* 数据集跟随复制粘贴的工具类 |
||||
* |
||||
* @author Yvan |
||||
*/ |
||||
public class TableDataFollowingPasteUtils { |
||||
|
||||
/** |
||||
* 粘贴所有Map中的tabledata到当前模板 |
||||
* |
||||
* @param tableDataWrapperMap |
||||
*/ |
||||
public static void paste(Map<String, TableData> tableDataWrapperMap) { |
||||
if (tableDataWrapperMap == null) { |
||||
return; |
||||
} |
||||
// 获取当前的TableDataTreePane
|
||||
DesignModelAdapter<?, ?> currentModelAdapter = DesignModelAdapter.getCurrentModelAdapter(); |
||||
TableDataTreePane tableDataTreePane = (TableDataTreePane) TableDataTreePane.getInstance(currentModelAdapter); |
||||
// 粘贴(添加)数据集
|
||||
for (Map.Entry<String, TableData> dataWrapperEntry : tableDataWrapperMap.entrySet()) { |
||||
String oldName = dataWrapperEntry.getKey(); |
||||
// 处理名称重复情况
|
||||
String dsName = tableDataTreePane.getNoRepeatedDsName4Paste(oldName); |
||||
AbstractTableDataWrapper tableDataWrapper = new TemplateTableDataWrapper(dataWrapperEntry.getValue(), dsName); |
||||
AbstractTableDataPane<?> tableDataPane = tableDataWrapper.creatTableDataPane(); |
||||
tableDataTreePane.addDataPane(tableDataPane, dsName); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 处理 ElementUsedTableDataProvider,从中获取数据集名称 - 数据集Wrapper 的Map |
||||
* |
||||
* @param providers |
||||
* @return |
||||
*/ |
||||
public static Map<String, TableData> transferProvider2TableDataMap(ElementUsedTableDataProvider... providers) { |
||||
if (providers == null) { |
||||
return new HashMap<>(); |
||||
} |
||||
// 获取当前的所有模板数据集
|
||||
Map<String, TableDataWrapper> templateTableData = getCurrentTemplateTableDataWrapper(); |
||||
Map<String, TableData> resultMap = new HashMap<>(); |
||||
for (ElementUsedTableDataProvider tableDataProvider : providers) { |
||||
Set<String> usedTableDataNames = tableDataProvider.getElementUsedTableDataNames(); |
||||
for (String usedTableDataName : usedTableDataNames) { |
||||
if (templateTableData.containsKey(usedTableDataName)) { |
||||
resultMap.put(usedTableDataName, templateTableData.get(usedTableDataName).getTableData()); |
||||
} |
||||
} |
||||
} |
||||
return resultMap; |
||||
} |
||||
|
||||
/** |
||||
* 提取控件内使用的数据集,转化成Map返回 |
||||
* |
||||
* @param widgets |
||||
* @return |
||||
*/ |
||||
public static Map<String, TableData> transferWidgetArray2TableDataMap(Widget... widgets) { |
||||
if (widgets == null) { |
||||
return new HashMap<>(); |
||||
} |
||||
// 获取当前的所有模板数据集
|
||||
Map<String, TableDataWrapper> templateTableData = getCurrentTemplateTableDataWrapper(); |
||||
Map<String, TableData> resultMap = new HashMap<>(); |
||||
for (Widget widget : widgets) { |
||||
if (widget instanceof DictionaryContainer) { |
||||
Set<String> usedTableDataSets = ((DictionaryContainer) widget).getUsedTableDataSets(); |
||||
for (String usedTableDataSet : usedTableDataSets) { |
||||
if (templateTableData.containsKey(usedTableDataSet)) { |
||||
resultMap.put(usedTableDataSet, templateTableData.get(usedTableDataSet).getTableData()); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
return resultMap; |
||||
} |
||||
|
||||
private static Map<String, TableDataWrapper> getCurrentTemplateTableDataWrapper() { |
||||
TableDataSource tableDataSource = DesignTableDataManager.getEditingTableDataSource(); |
||||
List<Map<String, TableDataWrapper>> editingDataSet = DesignTableDataManager.getEditingDataSet(tableDataSource); |
||||
return editingDataSet.get(0); |
||||
} |
||||
|
||||
} |
After Width: | Height: | Size: 861 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 970 B |
Loading…
Reference in new issue