RCP_TreeViewer的ContentProvider详解,以及Lazy载入

本文介绍Eclipse中TreeView的三种加载模式:简单模式、懒加载模式及显示Pending的懒加载模式。通过具体代码示例展示了不同场景下各种模式的应用方式,帮助开发者根据实际需求选择合适的加载策略。

 为了实现这样的效果:
    载入的过程中,显示Pending。


    载入完毕,Pending消失。

   

我们先从基础的说起,先来看ITreeContentProvider 接口:

Java代码
  1. public   interface  ITreeContentProvider {  
  2.     public  Object[] getElements(Object inputElement) ;  
  3.     public  Object[] getChildren(Object parentElement) ;  
  4.     public   boolean  hasChildren(Object element) ;  
  5.     // ...   
  6. }  
public interface ITreeContentProvider {
	public Object[] getElements(Object inputElement) ;
	public Object[] getChildren(Object parentElement) ;
	public boolean hasChildren(Object element) ;
	// ...
}


    getElements 表示在setInput(Object)的时候,如何从Object中得到一个数组,而使用这个数组去将树的第一层结点显示出来。这个方法只在setInput时才使用一次。
    getChildren表示在每次展开树的节点时,如果得到下一级节点的值,当然,树的节点展开了一次就不会再调用这个方法了,因为在树节点对应的控件TreeItem中,已经创建好了子节点控件了。
    hasChildren就是判断当前节点是否有子节点,有的话,就显示+号。

ITreeContentProvider的使用类型可以分成3种(我自己分的,^-^)。
1。简单模式
2。Lazy Loader模式(简单)
3。Lazy Loader模式(显示Pending)


一. 简单模式
其实就是在setInput的模型中,已经预先将所有的子模型都载入完毕,而不用再动态的载入子模型了。
模型说明:
    这个模型代表树节点,是支持父子结构的。当然,在Jface里面是有一个TreeNode 的,org.eclipse.jface.viewers.TreeNode,相应的还提供了一个 org.eclipse.jface.viewers.TreeNodeContentProvider。

Java代码
  1. public   class  TreeNode {  
  2.     private  TreeNode parent;  
  3.     private  List<TreeNode> children =  new  ArrayList<TreeNode>();  
  4.     private  Object value;  
public class TreeNode {
	private TreeNode parent;
	private List<TreeNode> children = new ArrayList<TreeNode>();
	private Object value;



ContentProvider代码:

Java代码
  1. public   class  SimpleContentProvider  implements  ITreeContentProvider {  
  2. public  Object[] getElements(Object inputElement) {  
  3.     return  ((TreeNode) inputElement).getChildren().toArray();  
  4. }  
  5. public  Object[] getChildren(Object parentElement) {  
  6.     return  ((TreeNode) parentElement).getChildren().toArray();  
  7. }  
  8. public   boolean  hasChildren(Object element) {  
  9.     return  ((TreeNode) element).getChildren().size() >  0 ;  
  10. }  
	public class SimpleContentProvider implements ITreeContentProvider {
	public Object[] getElements(Object inputElement) {
		return ((TreeNode) inputElement).getChildren().toArray();
	}
	public Object[] getChildren(Object parentElement) {
		return ((TreeNode) parentElement).getChildren().toArray();
	}
	public boolean hasChildren(Object element) {
		return ((TreeNode) element).getChildren().size() > 0;
	}



构建setInput的模型
就是在构建模型的时候,将所有的子模型都加载好了:

Java代码
  1. private  TreeNode buildLazyContent() {  
  2.     TreeNode root = new  TreeNode( null );  
  3.     TreeNode level_1_1 = new  TreeNode( "1.1" );  
  4.     root.addChild(level_1_1);  
  5.     // ...   
  6.     return  root;  
  7. }  
	private TreeNode buildLazyContent() {
		TreeNode root = new TreeNode(null);
		TreeNode level_1_1 = new TreeNode("1.1");
		root.addChild(level_1_1);
		// ...
		return root;
	}



其他代码:

Java代码
  1. viewer.setLabelProvider( new  TreeViewerLabelProvider());  
  2. viewer.setContentProvider(new  SimpleContentProvider());  
  3. viewer.setInput(this .buildSimpleContent());  
		viewer.setLabelProvider(new TreeViewerLabelProvider());
		viewer.setContentProvider(new SimpleContentProvider());
		viewer.setInput(this.buildSimpleContent());




二. Lazy Load模式(简单)
在setInput的模型中,只有最顶层的模型,再每次点击+的时间,将下一层的模型动态的载入,从而将树的子节点构造出来。也就是在getChildren中动态载入即可。

ContentProvider代码。
    注意getChildren 和hasChildren的方法实现,hasChildren返回true,表示需要给这个树节点显示+号。点击+号,getChildren会动态的载入子节点数据,这样就实现了Lazy Load了。

Java代码
  1. public   class  SimpleLazyContentProvider  implements  ITreeContentProvider {  
  2.     public  Object[] getChildren(Object parentElement) {  
  3.         TreeNode parent = (TreeNode) parentElement;  
  4.         this .loadChildren(parent);   
  5.         return  parent.getChildren().toArray();  
  6.     }  
  7.     public   boolean  hasChildren(Object element) {  
  8.         return   true ;   
  9.     }  
  10.     private   void  loadChildren(TreeNode parent) {  
  11.         TreeNode child_1 = new  TreeNode( "1" );  
  12.         parent.addChild(child_1);  
  13.           
  14.         TreeNode child_2 = new  TreeNode( "2" );  
  15.         parent.addChild(child_2);  
  16.     }  
public class SimpleLazyContentProvider implements ITreeContentProvider {
	public Object[] getChildren(Object parentElement) {
		TreeNode parent = (TreeNode) parentElement;
		this.loadChildren(parent); 
		return parent.getChildren().toArray();
	}
	public boolean hasChildren(Object element) {
		return true; 
	}
	private void loadChildren(TreeNode parent) {
		TreeNode child_1 = new TreeNode("1");
		parent.addChild(child_1);
		
		TreeNode child_2 = new TreeNode("2");
		parent.addChild(child_2);
	}




构建setInput的模型:
    只需要构建第一层树节点即可,后面的就交给ContentProvider去处理。

Java代码
  1. private  TreeNode buildSimpleLazyContent() {  
  2.     TreeNode root = new  TreeNode( null );  
  3.     TreeNode level_1_1 = new  TreeNode( "1.1" );  
  4.     root.addChild(level_1_1);  
  5.     return  root;  
  6. }  
	private TreeNode buildSimpleLazyContent() {
		TreeNode root = new TreeNode(null);
		TreeNode level_1_1 = new TreeNode("1.1");
		root.addChild(level_1_1);
		return root;
	}



其他代码:

Java代码
  1. viewer.setLabelProvider( new  TreeViewerLabelProvider());  
  2. viewer.setContentProvider(new  SimpleLazyContentProvider());  
  3. viewer.setInput(this .buildSimpleLazyContent());  
		viewer.setLabelProvider(new TreeViewerLabelProvider());
		viewer.setContentProvider(new SimpleLazyContentProvider());
		viewer.setInput(this.buildSimpleLazyContent());




三. Lazy Loader模式(显示Pending)
   和模式是二一样的,都是点+载入子模型。但是,如果是需要长时间的加载,模式二会将界面卡住,而这个模式,再点击+号后,会先显示一个Pending树节 点(并可以显示进度条),并看到随着数据一个一个载入树节点一个一个添加出来,所有数据都载入后,Pending树节点消失,这样用户体验就会好很多。在 这个模式,需要用到很多其他的类,具体看代码:

模型代码:
1. 需要实现IDeferredWorkbenchAdapter接口。
2. 注意fetchDeferredChildren方法。将载入子数据的过程放到了模型里面(很怪)。

Java代码
  1. public   class  PendingTreeNode  extends  TreeNode  implements  IDeferredWorkbenchAdapter {  
  2.     public   void  fetchDeferredChildren(Object object, IElementCollector collector,  
  3.             IProgressMonitor monitor) {  
  4.         PendingTreeNode parent = (PendingTreeNode) object;  
  5.           
  6.         try  {  
  7.             Thread.sleep(500 );  
  8.         } catch  (InterruptedException e) {  
  9.             e.printStackTrace();  
  10.         }  
  11.         PendingTreeNode node1 = new  PendingTreeNode( "1" );  
  12.         parent.addChild(node1);  
  13.         collector.add(node1, monitor);  
  14.           
  15.         try  {  
  16.             Thread.sleep(500 );  
  17.         } catch  (InterruptedException e) {  
  18.             e.printStackTrace();  
  19.         }  
  20.         PendingTreeNode node2 = new  PendingTreeNode( "2" );  
  21.         parent.addChild(node2);  
  22.         collector.add(node2, monitor);  
  23.     }  
public class PendingTreeNode extends TreeNode implements IDeferredWorkbenchAdapter {
	public void fetchDeferredChildren(Object object, IElementCollector collector,
			IProgressMonitor monitor) {
		PendingTreeNode parent = (PendingTreeNode) object;
		
		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		PendingTreeNode node1 = new PendingTreeNode("1");
		parent.addChild(node1);
		collector.add(node1, monitor);
		
		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		PendingTreeNode node2 = new PendingTreeNode("2");
		parent.addChild(node2);
		collector.add(node2, monitor);
	}



ContentProvider代码。
1. 添加了一个DeferredTreeContentManager,并使用该manager代理getChildren方法。
2. hasChildren返回true。
3. 修改了getElements方法。

Java代码
  1. public   class  PendingLazyContentProvider  implements  ITreeContentProvider {  
  2.     private  DeferredTreeContentManager manager;  
  3.       
  4.     public  Object[] getChildren(Object parentElement) {  
  5.         return  manager.getChildren(parentElement);  
  6.     }  
  7.     public   boolean  hasChildren(Object element) {  
  8.         return   true ;  
  9.     }  
  10.     public  Object[] getElements(Object inputElement) {  
  11.         if  (inputElement  instanceof  PendingTreeNode) {  
  12.             return  ((PendingTreeNode) inputElement).getChildren().toArray();              
  13.         }   
  14.         return   new  Object[ 0 ];  
  15.     }  
  16.     public   void  inputChanged(Viewer viewer, Object oldInput, Object newInput) {  
  17.         if  (manager ==  null ) {  
  18.             manager = new  DeferredTreeContentManager( this , (TreeViewer) viewer);  
  19.         }  
  20.     }  
  21. }  
public class PendingLazyContentProvider implements ITreeContentProvider {
	private DeferredTreeContentManager manager;
	
	public Object[] getChildren(Object parentElement) {
		return manager.getChildren(parentElement);
	}
	public boolean hasChildren(Object element) {
		return true;
	}
	public Object[] getElements(Object inputElement) {
		if (inputElement instanceof PendingTreeNode) {
			return ((PendingTreeNode) inputElement).getChildren().toArray();			
		} 
		return new Object[0];
	}
	public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
		if (manager == null) {
			manager = new DeferredTreeContentManager(this, (TreeViewer) viewer);
		}
	}
}



LabelProvider代码:
1. 修改了getText方法。由于我们说中间会创建一个显示Pending的节点,这个节点就是PendingUpdateAdapter,需要特别指定显示的text。

Java代码
  1. public   class  PendingTreeViewerLabelProvider  extends  LabelProvider {  
  2.     public  String getText(Object obj) {  
  3.         if  (obj  instanceof  TreeNode) {  
  4.             return  ((TreeNode) obj).getValue().toString();  
  5.         } else   if  (obj  instanceof  PendingUpdateAdapter) {  
  6.             return  ((PendingUpdateAdapter) obj).getLabel( null );   
  7.         }  
  8.         return  obj.toString();  
  9.     }  
public class PendingTreeViewerLabelProvider extends LabelProvider {
	public String getText(Object obj) {
		if (obj instanceof TreeNode) {
			return ((TreeNode) obj).getValue().toString();
		} else if (obj instanceof PendingUpdateAdapter) {
			return ((PendingUpdateAdapter) obj).getLabel(null); 
		}
		return obj.toString();
	}



构建setInput的模型:
1. 只需要预先放入根节点即可。子节点的载入就依赖于点击+号,lazy载入了。

Java代码
  1. private  PendingTreeNode buildPendingLazyContent() {  
  2.     PendingTreeNode root = new  PendingTreeNode( null );  
  3.   
  4.     PendingTreeNode level_1_1 = new  PendingTreeNode( "1.1" );  
  5.     root.addChild(level_1_1);  
  6.     return  root;  
  7. }  
	private PendingTreeNode buildPendingLazyContent() {
		PendingTreeNode root = new PendingTreeNode(null);

		PendingTreeNode level_1_1 = new PendingTreeNode("1.1");
		root.addChild(level_1_1);
		return root;
	}




其他代码:

Java代码
  1. viewer.setLabelProvider( new  PendingTreeViewerLabelProvider());  
  2. viewer.setContentProvider(new  PendingLazyContentProvider());  
  3. viewer.setInput(this .buildPendingLazyContent());  
		viewer.setLabelProvider(new PendingTreeViewerLabelProvider());
		viewer.setContentProvider(new PendingLazyContentProvider());
		viewer.setInput(this.buildPendingLazyContent());




四. 总结
1. 简单模式适合于你一次性将数据全部载入,而后在setInput一次显示出来。
2. Lazy Load模式(简单)适合于性能影响不大的点击+号Lazy的载入数据。
3. Lazy Load模式(显示Pending)适合于载入过程比较长,为了提供用户交互而使用
4. Lazy Load模式都存在着遍历的问题。因为有的模型的子节点还没有完全载入,而你却依赖所有这些子节点的时候,这时就必须在代码里面将所有子节点打开,让其载入所有子数据至最后一层,对于(简单)和(显示Pending)都可以这么去做。
5. 还是4的情况,由于(显示Pending)是异步载入的,需要更复杂的判断,并基于线程去判断某节点是否载入完毕,等你自己去实现的时候,你会发现这是有多复杂的^-^


五. 其他相关的类
org.eclipse.jface.viewers.TreeNode
org.eclipse.jface.viewers.TreeNodeContentProvider
org.eclipse.jface.viewers.ArrayContentProvider
org.eclipse.ui.progress.IDeferredWorkbenchAdapter
org.eclipse.ui.progress.DeferredTreeContentManager

<think> 我们正在讨论的是嵌入式SQL(Embedded SQL)中的FETCH语句。用户提供的代码片段是: EXEC SQL FETCH c_vten INTO :h_vten.rcp_ten_jname:h_vten.rcp_ten_jname_i, :h_vten.rcp_ten_kname:h_vten.rcp_ten_kname_i; 为了理解这段代码,我们需要从嵌入式SQL的基础开始。 ### 1. **嵌入式SQL背景** 嵌入式SQL允许在高级语言(如C、COBOL)中直接编写SQL语句,通过预处理器转换为宿主语言的可调用接口。`EXEC SQL`是嵌入式SQL的标识,后面紧跟SQL语句。 ### 2. **游标(Cursor)操作** 在嵌入式SQL中,游标用于处理多行结果集。典型操作流程为: 1. **DECLARE**:声明游标(关联SELECT语句) 2. **OPEN**:打开游标(执行SELECT) 3. **FETCH**:逐行获取结果 4. **CLOSE**:关闭游标 此处`FETCH c_vten`表示从已打开的游标`c_vten`中获取一行数据。 ### 3. **INTO子句解析** `INTO`子句指定接收查询结果的宿主变量(host variables)。特别注意: - 宿主变量前需加冒号(`:`)作为标识 - 变量顺序、类型必须与SELECT字段匹配 ### 4. **宿主变量的特殊语法** 代码中使用了带指示符变量的宿主变量: ```sql :宿主变量名:指示符变量名 ``` 这是嵌入式SQL中处理NULL值的标准方式。 ### 5. **具体变量分析** - `h_vten.rcp_ten_jname`:接收字段值的字符数组(主变量) - `h_vten.rcp_ten_jname_i`:整型指示符变量(indicator variable) ### 6. **指示符变量作用** 当数据库字段值为NULL时,指示符变量将被设置为负值(通常为-1)。若字段非NULL,则指示符变量有两种可能情况: - **0**:表示值完整存储,无截断 - **正值**:表示值被截断,数值为原始数据长度 ### 7. **代码执行流程** 1. 执行`FETCH`从游标`c_vten`获取下一行 2. 将第一列数据存入`h_vten.rcp_ten_jname` - 同时设置`h_vten.rcp_ten_jname_i`的值: - 若列为NULL → 设为-1 - 若列非NULL → - 数据完整 → 设为0 - 数据截断 → 设为原始长度 3. 同理处理第二列到`h_vten.rcp_ten_kname`及其指示符 ### 8. **关联初始化代码** 用户之前询问的初始化代码: ```c memset(h_vten.rcp_ten_jname,'\0',sizeof(h_vten.rcp_ten_jname)); h_vten.rcp_ten_jname_i = 0; ``` 其作用是确保FETCH前缓冲区干净,避免脏数据干扰。指示符归零符合"无NULL、无截断"的初始预期状态。 ### 9. **错误处理** 实际应用中,每次FETCH后应检查SQL状态(如SQLCA中的`SQLCODE`)以判断: - 是否成功获取数据(`SQLCODE=0`) - 是否到达结果集末尾(如`SQLCODE=100`) - 是否发生错误(负值错误码) > 技术细节:在Oracle Pro*C中,指示符变量类型为`short`;在DB2中为`SQLIND`(通常也定义为短整型)[^1]。 §§相关问题§§ 1. 如果指示符变量返回正值(截断),如何处理完整数据? 2. 如何在嵌入式SQL中批量获取多行数据? 3. 当FETCH返回NULL时,宿主变量数组的内容是什么? 4. 不同数据库(Oracle/DB2/SQL Server)的指示符变量处理有何差异? [^1]: 指示符变量的具体行为由SQL标准定义,各数据库实现基本一致,但错误代码可能不同。 </think> ### EXEC SQL FETCH语句解析 您提供的代码是数据库编程中的嵌入式SQL语句,用于从游标中提取数据到宿主变量。具体分析如下: ```sql EXEC SQL FETCH c_vten INTO :h_vten.rcp_ten_jname:h_vten.rcp_ten_jname_i, :h_vten.rcp_ten_kname:h_vten.rcp_ten_kname_i; ``` #### 1. **组件解析** | 组件 | 说明 | |------|------| | `EXEC SQL` | 嵌入式SQL标识符(如Pro*C, ESQL/C等) | | `FETCH c_vten` | 从名为`c_vten`的游标获取下一行数据 | | `INTO :h_vten...` | 指定接收查询结果的宿主变量 | | `:h_vten.rcp_ten_jname` | 主变量(存储字段值) | | `:h_vten.rcp_ten_jname_i` | **指示符变量**(关键组件) | #### 2. **指示符变量的作用** 这是嵌入式SQL特有的NULL值处理机制: - 当数据库字段为**NULL**时:指示符变量设为**负值**(通常为-1) - 当数据**非NULL**时: - **0**:值完整存储无截断 - **正值**:数据被截断,值=原始数据长度 #### 3. **代码执行流程 ```mermaid flowchart TD A[执行FETCH c_vten] --> B{第一列是否NULL?} B -->|是| C[设置 rcp_ten_jname_i = -1] B -->|否| D[存储值到 rcp_ten_jname] D --> E{缓冲区是否足够?} E -->|是| F[设置 rcp_ten_jname_i = 0] E -->|否| G[设置 rcp_ten_jname_i = 原始长度] A --> H{第二列处理} H --> 类似流程... ``` #### 4. **与初始化代码的关联** 您之前询问的初始化代码: ```c memset(h_vten.rcp_ten_jname,'\0',sizeof(...)); h_vten.rcp_ten_jname_i = 0; // 初始化指示符 ``` 作用是: 1. 清空字符串缓冲区(避免脏数据) 2. 设置指示符初始状态为"0"(预期无NULL/截断) 3. 为FETCH操作准备干净环境 #### 5. **实际应用场景 假设数据库表结构: ```sql CREATE TABLE vten_table ( jname VARCHAR(20), -- 日文名 kname VARCHAR(20) -- 韩文名 ); ``` 当执行FETCH时: - 若`jname`为NULL → `rcp_ten_jname_i = -1` - 若`kname='길동'`且缓冲区足够 → `rcp_ten_kname_i = 0` - 若`kname='가나다라마바사'`(长度10)但缓冲区仅8字节 → `rcp_ten_kname_i = 10` > **关键点**:指示符变量使应用程序能精确区分**空值**、**截断数据**和**完整数据**三种状态,避免将截断数据误认为完整数据[^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值