`

如何定制Eclipse中UI持久化及恢复功能

 
阅读更多
需求:在IDE(一款基于Eclipse的开发工具)下一次打开工程的时候,恢复上一次关闭前的状态。恢复粒度:和工程相关的。

1.先了解一下Eclipse本身的恢复功能:
经常用Eclipse做开发,我们知道,Eclipse的UI持久化及窗户恢复功能做的是很完善的,比如ProjectExplorer视图中工程的展开状态、Editor区域的文件的代码状态,各个视图的展现状态,Dialog,MenuBar,ToolBar等待,Eclipse都很好的恢复到了上次我们关闭前的状态。
在Eclipse关闭前他会启动UI持久化功能,把需要保持的各个控件的状态信息(IMemento对象)持久化到一个文件中(这个文件一般在Eclipse的安装目录的Workspace下:
..\.metadata\.plugins\org.eclipse.ui.workbench\workbench.xml).
恢复粒度:和整个IDE相关。

2.为什么要定制UI持久化框架
简单说就是Eclipse的本身持久化机制不能满足需要---不能满足需求中的对每个工程粒度的恢复。
需要做的就是为每一个IDE中的工程创建一份持久化文件及操作入口。
于是,先得弄清楚Eclipse的一整套状态恢复机制,找到其持久化和逆持久化的接口,然后展开定制工作。

3.debug方式跟踪Eclipse的UIPresent机制的内部实现
Eclipse什么实现的恢复功能呢,网上搜索资料当然好,但既然都是开源的东西,源码当然是做好的参考资料了。
浏览源码的最好方式就是:debug+断点--》一步步,代码的运行过程看的一清二楚。
(为了容易调试,可以创建一个RCP项目来模拟Eclipse进行调试,其实Eclipse本身也可以看做是一个复杂的RCP项目)
先从持久化入手(从恢复也一样),既然Eclipse工作台在关闭前时会进行持久化动作,那就通过在现在RCP程序的ApplicationWorkbenchWindowAdvisor类的方法:
@Override
    public boolean preWindowShellClose() {
    	return super.preWindowShellClose();
    }
中设置断点(preWindowShellClose方法在RCP关闭前调用),调试状态下启动RCP程序,然后关闭该RCP,调试线程就挺在了刚才的断点处,然后F5(Step Into)进去看内部实现,就这样一步步的跟下去,跟到了WorkbenchWindow的close方法:
public boolean close() {
		final boolean[] ret = new boolean[1];
		BusyIndicator.showWhile(null, new Runnable() {
			public void run() {
				ret[0] = busyClose();
			}
		});
		return ret[0];
	}

在ret[0] = busyClose();前面打上断点,继续更近,最后进入到workbench的busyClose方法中:
private boolean busyClose(final boolean force) {
    ......
	if (getWorkbenchConfigurer().getSaveAndRestore()) {// tag1
			SafeRunner.run(new SafeRunnable() {//tag2
				public void run() {
					XMLMemento mem = recordWorkbenchState();//tag3
					// Save the IMemento to a file.
					saveMementoToFile(mem);//tag4
				}
				public void handleException(Throwable e) {
					.......
				}
			});
		}
		......
		return true;
}
通过单步,结合方法的名称,很容判断出执行保存状态的代码部分,上面的代码片段就是Eclipse执行状态持久化的地方。代码后面有tag的注释字样,下面一一作一下解释:
tag1就是执行的条件getWorkbenchConfigurer().getSaveAndRestore(),通过跟踪代码可以看到其具体实现,在Workbench类中:
WorkbenchConfigurer getWorkbenchConfigurer() {
	if (workbenchConfigurer == null) {
		workbenchConfigurer = new WorkbenchConfigurer();
	}
	return workbenchConfigurer;
}
WorkbenchConfigurer类是Workbench的一个配置管理类,可以设置Workbench的很多配置项的值,来满足不同用户的个性化需求,比如可以通过
configurer.setSaveAndRestore(true);设置Workbench是否保存持久化操作,Eclipse中会通过XXXAdvisor(中文意思就是顾问)来设置这些配置项,管理着XXX的生命周期,
这里的XXX可以是Workbench,对应的是WorkbenchAdvisor,WorkbenchWindow,就对应WorkbenchWindowAdvisor--管理着IWorkbenchWindowConfigurer的设置。
对应RCP程序来说,如果想使程序具备持久化和恢复功能就需要在WorkbenchAdvisor的实现类ApplicationWorkbenchAdvisor中设置如下:
@Override
	public void initialize(IWorkbenchConfigurer configurer) {
		super.initialize(configurer);
		configurer.setSaveAndRestore(true);
  }
然后就有资格在tag1处,满足条件,进入持久化的动作。

Tag2处的SafeRunner是一个封装了异常处理的类,会把异常单独通过一个方法供开发者重写,没有什么特殊点。
Tag3、tag4当然就是持久化的核心实现方法了,其中tag3会搜集并合并模型(从workbench到workbenchWindow到workbenchPage到各个view、editor、透视图等)最终到root模型中XMLMemento ;tag4当然是把模型保存到文件中,以便下次打开App时执行恢复操作。
下面来一点点分享,继续单步调试:
private XMLMemento recordWorkbenchState() {
	XMLMemento memento = XMLMemento
				.createWriteRoot(IWorkbenchConstants.TAG_WORKBENCH);
	final IStatus status = saveState(memento);
		
	return memento;	
}

XMLMemento 实现了IMemento持久化模型接口,内部通过Dom的一套api对xml进行操作(关于xml的解析技术,在开发中也是经常用到的,有时间会对xml解析技术做一下归纳,这里先不过多的介绍)。XMLMemento 内部维护了两个字段:
private Document factory;//dom中的xml树形对象
private Element element;//doc中的root根元素
下面是createWriteRoot的实现,创建一个XMLMemento 的对象,作为整个持久化模型的根元素:
public static XMLMemento createWriteRoot(String type) throws DOMException{
        Document document;
        try {
            document = DocumentBuilderFactory.newInstance()
                    .newDocumentBuilder().newDocument();
            Element element = document.createElement(type);
            document.appendChild(element);
            return new XMLMemento(document, element);
        } catch (ParserConfigurationException e) {
//            throw new Error(e);
            throw new Error(e.getMessage());
        }
}
接着就是saveState(memento)对各个部分需要恢复的模型的合并:
/*
	 * Saves the current state of the workbench so it can be restored later on
	 */
	private IStatus saveState(IMemento memento) {
	  MultiStatus result = new MultiStatus(PlatformUI.PLUGIN_ID, IStatus.OK,WorkbenchMessages.Workbench_problemsSaving, null);
		// Save the version number.
		memento.putString(IWorkbenchConstants.TAG_VERSION, VERSION_STRING[1]);//tag1

		// Save how many plug-ins were loaded while restoring the workbench
		if (progressCount != -1) {//tag2
			memento.putInteger(IWorkbenchConstants.TAG_PROGRESS_COUNT,
					progressCount);
		}

		// Save the advisor state.
		IMemento advisorState = memento
				.createChild(IWorkbenchConstants.TAG_WORKBENCH_ADVISOR);
		result.add(getAdvisor().saveState(advisorState));//tag3

		// Save the workbench windows.
		IWorkbenchWindow[] windows = getWorkbenchWindows();
		for (int nX = 0; nX < windows.length; nX++) {//tag4
			WorkbenchWindow window = (WorkbenchWindow) windows[nX];
			IMemento childMem = memento
					.createChild(IWorkbenchConstants.TAG_WINDOW);
			result.merge(window.saveState(childMem));//tag5
		}
		result.add(getEditorHistory().saveState(	memento.
createChild(IWorkbenchConstants.TAG_MRU_LIST)));//tag6
		return result;
	}
其中tag1、tag2中在xml文件中的显示形如:
<workbench progressCount="34" version="2.0">
tag3保存了WorkbenchAdvisor中的持久化数据,默认在xml中形如:
<workbenchAdvisor/>
当然可以通过重写WorkbenchAdvisor的saveState方法,这样标签中会有相应的属性。
Tag4就是得到整个平台Workbench的WorkbenchWindow,是通过WindowManger的getWindows方法获得的。WindowManger内部维护着一个WorkbenchWindow的ArrayList。getWorkbenchWindows()方法就是获得list的一个copy。
public IWorkbenchWindow[] getWorkbenchWindows() {
	if (windowManager == null) {
		return new IWorkbenchWindow[0];
	}
	Window[] windows = windowManager.getWindows();
	IWorkbenchWindow[] dwindows = new 
IWorkbenchWindow[windows.length];
	System.arraycopy(windows, 0, dwindows, 0, windows.length);
	return dwindows;
}
然后遍历每一个WorkbenchWindow,开始执行其持久化操作,就是tag5。
最后的tag6就是保存editor区域的历史信息,默认标签<mruList/>,非关键内容,就不再说了,下面重点看一下关键实现tag5,同样是step into进去,核心类会比较复杂,不过没关系,一点点看:
public IStatus saveState(IMemento memento) {
  MultiStatus result = new MultiStatus(PlatformUI.PLUGIN_ID, IStatus.OK,
           WorkbenchMessages.WorkbenchWindow_problemsSavingWindow, null);
            
        //--------------tag1-begin---------
		// Save the window's state and bounds.
		……
		memento.putInteger(IWorkbenchConstants.TAG_X, normalBounds.x);
		memento.putInteger(IWorkbenchConstants.TAG_Y, normalBounds.y);
		memento.putInteger(IWorkbenchConstants.TAG_WIDTH, normalBounds.width);
		memento.putInteger(IWorkbenchConstants.TAG_HEIGHT, normalBounds.height);
          //--------------tag1-end---------
……
		// save the width of the perspective bar
		IMemento persBarMem = memento
				.createChild(IWorkbenchConstants.TAG_PERSPECTIVE_BAR);
		if (perspectiveSwitcher != null) {//tag3
			perspectiveSwitcher.saveState(persBarMem);
		}

		// / Save the order of the cool bar contribution items
        ICoolBarManager2 coolBarMgr = (ICoolBarManager2) 
getCoolBarManager2();
        if (coolBarMgr != null) {
        	……
		}
        //--------------tag2-end---------

		// Save each page.
		Iterator itr = pageList.iterator();
		while (itr.hasNext()) {//tag3
			WorkbenchPage page = (WorkbenchPage) itr.next();

			// Save perspective.
			IMemento pageMem = memento
					.createChild(IWorkbenchConstants.TAG_PAGE);
			pageMem.putString(IWorkbenchConstants.TAG_LABEL,  
                    page.getLabel());
			result.add(page.saveState(pageMem));//tag4
			if (page == getActiveWorkbenchPage()) {
				pageMem.putString(IWorkbenchConstants.TAG_FOCUS, "true"); 
			}
        ……		
        }
 ……
	return result;
}
其中,tag1是和整个app窗体的大小、打开时在电脑屏幕的位置、fastView等信息等(在进行我们IDE定制时,在打开一个新的工程进行恢复时,IDE会在屏幕上移动,这样用户体验不好,于是就去掉了这个部分)。在xml中形如:
<window height="704" width="1024" x="175" y="131">
<fastViewData fastViewLocation="1024"/>
接着,tag2保存了工具栏CoolBar、透视图bar等的状态信息。
然后tag3就开遍历WorkbenchWindow下的所有WorkbenchPage。(通常情况下也就一个
page)。
Tag4调用了WorkbenchPage.saveState()开始page页的保存,下面看一下该方法的关键实现部分:
public IStatus saveState(IMemento memento) {
        // Save editor manager.
        IMemento childMem = memento
                .createChild(IWorkbenchConstants.TAG_EDITORS);
        result.merge(editorMgr.saveState(childMem));//tag1
        // Save View
        childMem = memento.createChild(IWorkbenchConstants.TAG_VIEWS);
        result.merge(getViewFactory().saveState(childMem));//tag2

        // Save each perspective in opened order
        Iterator itr = perspList.iterator();
        while (itr.hasNext()) {
            Perspective persp = (Perspective) itr.next();
            IMemento gChildMem = childMem
                    .createChild(IWorkbenchConstants.TAG_PERSPECTIVE);
            result.merge(persp.saveState(gChildMem));//tag3
        }
        ……
		return result;
  }
  从代码中一目了然,WorkbenchPage的保存操作,主要分为3个部分:
     1》通过EditorManager实现所有Editor区域信息的保存;(tag1)
     2》通过ViewFactory实现所有View信息的保存;(tag2)
     3》实现透视图相关状态的保存;(tag3)
代码分析到这里算告一段落,接下来就进入了具体每个View、Editor信息的保存动作。
上文件主要进行了信息保存的分析过程,其实信息的恢复过程也是类似,就是先找到保存的状态信息文件,然后以类似上面分析的层次关系,一步步完成恢复工作的。关于恢复的具体过程,可以在插件启动处(或直接在org.eclipse.ui.internal.Workbench.restoreState()这个方法上)打上断点一步步调试(不过前提的RCP需要设置可以恢复,上文已经说过),大概过程可以列一下:
先来看一下rcp的启动过程
RCP的启动方法Application.start(IApplicationContext context) 中:
int returnCode = PlatformUI.createAndRunWorkbench(display,
                            new ApplicationWorkbenchAdvisor());
其中ApplicationWorkbenchAdvisor功能就是提供了Workbench的个性化配置和管理功能。
创建Workben
public static final int createAndRunWorkbench(final Display display,
			final WorkbenchAdvisor advisor) {
		final int[] returnCode = new int[1];
		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
			public void run() {
				……
				// create the workbench instance
				Workbench workbench = new Workbench(display, advisor);
				// run the workbench event loop
				returnCode[0] = workbench.runUI();
			}
		});
		return returnCode[0];
	}

在workbench执行eventloop(这是整个rcp的工作模式--事件监听模型)前,会先执行workbench的初始化工作init():
private boolean init() {
        ……//一系列的初始化工作
		// create workbench window manager
		windowManager = new WindowManager();
        ……
		// attempt to restore a previous workbench state
		try {
			U……
			StartupThreading.runWithoutExceptions(new StartupRunnable() {
				public void runWithException() throws Throwable {
					advisor.preStartup();
					
					if (isClosing() || !advisor.openWindows()) {
						bail[0] = true;
					}
				}});
			……
		} ……
		return true;
	}

解析来就是dvisor.openWindows(),其中调用到了
org.eclipse.ui.internal.WorkbenchConfigurer.restoreState()方法:
public IStatus restoreState() {
     return ((Workbench) getWorkbench()).restoreState();
}

于是,轰轰烈烈的恢复动作就开始了:
IStatus restoreState() {
        // Read the workbench state file.
		final File stateFile = getWorkbenchStateFile();//tag1
……
		SafeRunner.run(new SafeRunnable(x) {
			public void run() throws Exception {
				FileInputStream input = new FileInputStream(stateFile);
				BufferedReader reader = new BufferedReader(
						new InputStreamReader(input, "utf-8")); 
				IMemento memento = XMLMemento.createReadRoot(reader);//tag2
……
				// Restore the saved state
				final IStatus restoreResult = restoreState(memento);//tag3
				reader.close();
				……
			}
……
		});
}

Tag1就是获得workbench.xml所在的路径,当然,如果定制的话这个路径就需要指定为自己自定义的文件路径了(比如和工程相关的)。
Tag2就是把xml中的信息转换成XMLMemento对象,具体解析方式就是dom的xml解析技术:
  
 public static XMLMemento createReadRoot(Reader reader, String baseDir)
            throws WorkbenchException {
……
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory
                    .newInstance();
            DocumentBuilder parser = factory.newDocumentBuilder();
            InputSource source = new InputSource(reader);
            if (baseDir != null) {
				source.setSystemId(baseDir);
			}
……
            Document document = parser.parse(source);
            NodeList list = document.getChildNodes();
            for (int i = 0; i < list.getLength(); i++) {
                Node node = list.item(i);
                if (node instanceof Element) {
					return new XMLMemento(document, (Element) node);
				}
            }
        } catch (ParserConfigurationException e) {
           ……
        } 
……
    }

Tag3就是恢复的具体恢复实现方法,紧接着会调用doRestoreState方法,先获取Window对应的状态信息,创建WorkbenchWindow,并把状态信息restore进去
	private void doRestoreState(final IMemento memento, final MultiStatus status) {
	……
		// Get the child windows.
		IMemento[] children = memento
				.getChildren(IWorkbenchConstants.TAG_WINDOW);//

		createdWindows = new WorkbenchWindow[children.length];

		// Read the workbench windows.
		for (int i = 0; i < children.length; i++) {
			childMem = children[i];
			final WorkbenchWindow [] newWindow = new WorkbenchWindow[1];
			
			StartupThreading.runWithoutExceptions(new StartupRunnable() {

				public void runWithException() {
					newWindow[0] = newWorkbenchWindow();
					newWindow[0].create();	
				}});
			createdWindows[i] = newWindow[0];

			windowManager.add(newWindow[0]);
			
			status.merge(newWindow[0].restoreState(childMem, null));
				……
	}
}
下面看一下WorkbenchWindow.restoreState()方法的实现,其主要实现就是遍历其下所有的WorkbenchPage相关的IMemento模型,然后创建WorkbenchPage并通过
restoreState进行内容恢复:
public IStatus restoreState(IMemento memento,
			IPerspectiveDescriptor activeDescriptor) {
	
		// 1.Restore the window advisor state.
		// 2.Restore actionbar advisor state.
		// 3.Read window's bounds and state.
		// 4.restore the width of the perspective bar
		// 5.Restore the cool bar order 
……
		// 6.Recreate each page in the window.
		IWorkbenchPage newActivePage = null;
		IMemento[] pageArray = memento
				.getChildren(IWorkbenchConstants.TAG_PAGE);
		for (int i = 0; i < pageArray.length; i++) {
			final IMemento pageMem = pageArray[i];			
……
			// Open the perspective.
			final IAdaptable finalInput = input[0];
			final WorkbenchPage [] newPage = new WorkbenchPage[1];
			try {
				StartupThreading.runWithWorkbenchExceptions(new StartupRunnable(){

					public void runWithException(){
						newPage[0] = ((WorkbenchImplementation) Tweaklets
								.get(WorkbenchImplementation.KEY)).createWorkbenchPage(WorkbenchWindow.this, finalInput);
					}});
				
				result.add(newPage[0].restoreState(pageMem, activeDescriptor));
				……
				
			} catch (WorkbenchException e) {}
				
		}
……
		return result;
}
然后到了WorkbenchPage.restoreState()方法实现:
public IStatus restoreState(IMemento memento,
            final IPerspectiveDescriptor activeDescriptor) {
……
 // Restore editor manager.//tag1
 IMemento childMem = memento
                        .getChild(IWorkbenchConstants.TAG_EDITORS);
 result.merge(getEditorManager().restoreState(childMem));

 childMem = memento.getChild(IWorkbenchConstants.TAG_VIEWS);//tag2
 result.merge(getViewFactory().restoreState(childMem));
……
 // Restore perspectives.//tag3
 final IMemento perspMems[] = childMem
                        .getChildren(IWorkbenchConstants.TAG_PERSPECTIVE);
 final Perspective activePerspectiveArray [] = new Perspective[1];
    for (int i = 0; i < perspMems.length; i++) {
        final IMemento current = perspMems[i];       
        persp.restoreState(current)
……
    }
}

Tag1就是通过EditorManager对所有的Editor区域的状态进行恢复;Tag2就是通过ViewFactory对View进行恢复;Tag3就是Perspective透视图的恢复功能。再往下就是具体的区域的恢复功能了。

文章到这里基本是对Eclipse的持久化和恢复功能的关键过程作了比较详细的阐述了,当然可以通过查看Eclipse源码了解更多的细节东西。

对Eclipse的持久化原理了解清楚以后,定制自己的恢复功能,比如当工程打开后加载状态文件,在工程关闭后保存状态文件等,就可以很容易实现了。当然在定制的过程中还是会遇到也许问题的,比如我们定制的过程中就遇到了诸如拖动编辑器时区域消失,加载窗户时区域闪烁和重启窗口后不能恢复到原来的状态等问题,不过在进一步研究一下Eclipse源码,这些bug或问题都能一个个找到合理的解决办法(源码真是好东西,多看Eclipse源码,呵呵)。
具体的View、Editor的恢复
定制完了恢复框架,接下来要做的就是具体的View和Editor内容的恢复了。比如ProjectView视图中要恢复Project Tree的展开状态、选中状态、滚动条的位置等信息;Editor区域打开文件的具体EditorPart的文件内容(EditorInput)等信息。
接下来分别举例说明:

1.首先,设置RCP允许恢复功能

这个上文提到过,就是在WorkbenchAdvisor中设置了configurer.setSaveAndRestore(true);表示允许恢复。
重写RCP中ApplicationWorkbenchAdvisor类的initialize()方法,设置setSaveAndRestore这个配置项为true。
   
@Override
	public void initialize(IWorkbenchConfigurer configurer) {
		configurer.setSaveAndRestore(true);
		super.initialize(configurer);
	}

下面说一下几种常用窗口的恢复功能:

2.View的恢复
以org.eclipse.cdt.internal.ui.cview.CView为例,进行说明(这是cdt的开源项目的代码实现):
(1)设置要恢复的变量名
// Persistance tags.
	static final String TAG_SELECTION = "selection"; //$NON-NLS-1$
	static final String TAG_EXPANDED = "expanded"; //$NON-NLS-1$
	static final String TAG_ELEMENT = "element"; //$NON-NLS-1$
	static final String TAG_PATH = "path"; //$NON-NLS-1$
	static final String TAG_VERTICAL_POSITION = "verticalPosition"; //$NON-NLS-1$
	static final String TAG_HORIZONTAL_POSITION = "horizontalPosition"; //$NON-NLS-1$
	static final String TAG_WORKINGSET = "workingSet"; //$NON-NLS-1$
(2)重写init()方法
private  IMemento memento;//定义全局变量
     @Override
	public void init(IViewSite site, IMemento memento) throws PartInitException {
		super.init(site, memento);
		this.memento = memento;
	}
(3)View UI的恢复方法restoreState(),在View的createPartControl()中调用:
@Override
        public void createPartControl(Composite aParent) {
                ......
		
            if (memento != null) {
			   restoreState(memento);
		    }
		    memento = null;
	     }
void restoreState(IMemento memento) {
		
CoreModel factory = CoreModel.getDefault();
		
		getActionGroup().restoreFilterAndSorterState(memento);

		IMemento childMem = memento.getChild(TAG_EXPANDED);
		if (childMem != null) {
			ArrayList<ICElement> elements = new ArrayList<ICElement>();
			IMemento[] elementMem = childMem.getChildren(TAG_ELEMENT);
			for (IMemento element2 : elementMem) {
				String p = element2.getString(TAG_PATH);
				if (p != null) {
					IPath path = new Path(p);
					ICElement element = factory.create(path);
					if (element != null) {
						elements.add(element);
					}
				}
			}
			viewer.setExpandedElements(elements.toArray());
		}
		childMem = memento.getChild(TAG_SELECTION);
		if (childMem != null) {
			ArrayList<ICElement> list = new ArrayList<ICElement>();
			IMemento[] elementMem = childMem.getChildren(TAG_ELEMENT);
			for (IMemento element2 : elementMem) {
				String p = element2.getString(TAG_PATH);
				if (p != null) {
					IPath path = new Path(p);
					ICElement element = factory.create(path);
					if (element != null) {
						list.add(element);
					}
				}
			}
			viewer.setSelection(new StructuredSelection(list));
		}

		Tree tree = viewer.getTree();
		//save vertical position
		ScrollBar bar = tree.getVerticalBar();
		if (bar != null) {
			try {
				String posStr = memento.getString(TAG_VERTICAL_POSITION);
				int position;
				position = new Integer(posStr).intValue();
				bar.setSelection(position);
				position = new Integer(posStr).intValue();
				bar.setSelection(position);
			} catch (NumberFormatException e) {
			}
		}
		bar = tree.getHorizontalBar();
		if (bar != null) {
			try {
				String posStr = memento.getString(TAG_HORIZONTAL_POSITION);
				int position;
				position = new Integer(posStr).intValue();
				bar.setSelection(position);
			} catch (NumberFormatException e) {
			}
		}

(4)View UI状态的保存,重写saveState()方法:
@Override
	public void saveState(IMemento memento) {
		
if (viewer == null) {
			if (this.memento != null) { //Keep the old state;
				memento.putMemento(this.memento);
			}
			return;
		}

		//save expanded elements
		Tree tree = viewer.getTree();
		Object expandedElements[] = viewer.getExpandedElements();
		if (expandedElements.length > 0) {
			IMemento expandedMem = memento.createChild(TAG_EXPANDED);
			for (Object expandedElement : expandedElements) {
				Object o = expandedElement;
				// Do not save expanded binary files are libraries.
				if (o instanceof IParent
						&& !(o instanceof IArchiveContainer || o instanceof IBinaryContainer || o instanceof IBinary || o instanceof IArchive)) {
					IMemento elementMem = expandedMem.createChild(TAG_ELEMENT);
					ICElement e = (ICElement) o;
					IResource res = e.getResource();
					if (res != null && res.getLocation() != null) {
						elementMem.putString(TAG_PATH, res.getLocation().toOSString());
					}
				}
			}
		}
		//save selection
		Object elements[] = ((IStructuredSelection) viewer.getSelection()).toArray();
		if (elements.length > 0) {
			IMemento selectionMem = memento.createChild(TAG_SELECTION);
			for (Object element : elements) {
				if (element instanceof ICElement) {
					ICElement e = (ICElement) element;
					IResource r = e.getResource();
					if (r != null && r.getLocation() != null) {
						IMemento elementMem = selectionMem.createChild(TAG_ELEMENT);
						elementMem.putString(TAG_PATH, r.getLocation().toString());
					}
				}
			}
		}
		//save vertical position
		ScrollBar bar = tree.getVerticalBar();
		int position = bar != null ? bar.getSelection() : 0;
		memento.putString(TAG_VERTICAL_POSITION, String.valueOf(position));
		//save horizontal position
		bar = tree.getHorizontalBar();
		position = bar != null ? bar.getSelection() : 0;
		memento.putString(TAG_HORIZONTAL_POSITION, String.valueOf(position));

		getActionGroup().saveFilterAndSorterState(memento);
		//Save the working set away
		if (workingSetFilter.getWorkingSet() != null) {
			String wsname = workingSetFilter.getWorkingSet().getName();
			if (wsname != null) {
				memento.putString(TAG_WORKINGSET, wsname);
			}
		}if (viewer == null) {
			if (this.memento != null) { //Keep the old state;
				memento.putMemento(this.memento);
			}
			return;
		}

		//save expanded elements
		Tree tree = viewer.getTree();
		Object expandedElements[] = viewer.getExpandedElements();
		if (expandedElements.length > 0) {
			IMemento expandedMem = memento.createChild(TAG_EXPANDED);
			for (Object expandedElement : expandedElements) {
				Object o = expandedElement;
				// Do not save expanded binary files are libraries.
				if (o instanceof IParent
						&& !(o instanceof IArchiveContainer || o instanceof IBinaryContainer || o instanceof IBinary || o instanceof IArchive)) {
					IMemento elementMem = expandedMem.createChild(TAG_ELEMENT);
					ICElement e = (ICElement) o;
					IResource res = e.getResource();
					if (res != null && res.getLocation() != null) {
						elementMem.putString(TAG_PATH, res.getLocation().toOSString());
					}
				}
			}
		}
		//save selection
		Object elements[] = ((IStructuredSelection) viewer.getSelection()).toArray();
		if (elements.length > 0) {
			IMemento selectionMem = memento.createChild(TAG_SELECTION);
			for (Object element : elements) {
				if (element instanceof ICElement) {
					ICElement e = (ICElement) element;
					IResource r = e.getResource();
					if (r != null && r.getLocation() != null) {
						IMemento elementMem = selectionMem.createChild(TAG_ELEMENT);
						elementMem.putString(TAG_PATH, r.getLocation().toString());
					}
				}
			}
		}
		//save vertical position
		ScrollBar bar = tree.getVerticalBar();
		int position = bar != null ? bar.getSelection() : 0;
		memento.putString(TAG_VERTICAL_POSITION, String.valueOf(position));
		//save horizontal position
		bar = tree.getHorizontalBar();
		position = bar != null ? bar.getSelection() : 0;
		memento.putString(TAG_HORIZONTAL_POSITION, String.valueOf(position));

		getActionGroup().saveFilterAndSorterState(memento);
		//Save the working set away
		if (workingSetFilter.getWorkingSet() != null) {
			String wsname = workingSetFilter.getWorkingSet().getName();
			if (wsname != null) {
				memento.putString(TAG_WORKINGSET, wsname);
			}
		}
	}
业务逻辑很简单,就不做解释了。
3.Editor的恢复
例如要恢复一个ID为ID=“org.niub.test.Editor”的编辑器TestEditor
(1)扩展org.eclipse.ui.elementFactories,定义控件元素恢复工厂PersistentElementFactory
  
<extension
         point="org.eclipse.ui.elementFactories">
      <factory
            class="org.niub.test.PersistentElementFactory"
            id="org.niub.test.PersistentElementFactory">
      </factory>
   </extension>


(2)实现元素恢复工厂PersistentElementFactory
  
 public class PersistentElementFactory implements IElementFactory {
	public static final String ID = "org.niub.test.PersistentElementFactory";
    
	@Override
	public IAdaptable createElement(IMemento memento) {
		PersistentEditorInput editorInput = null;
        //当然,这个标签的名字可以任意指定,只要save和restore保存一致就行
		IMemento editorMem = memento.getChild(ID);
		if (editorMem != null) {
			int tagIndex = 
  editorMem.getInteger(PersistentEditorInput.TAG_ACTIVE_TAG_INDEX);
			int v_position = 
  editorMem.getInteger(PersistentEditorInput.TAG_VERTICAL_POSITION);
			int h_position = 
        editorMem.getInteger(PersistentEditorInput.TAG_HORIZONTAL_POSITION);
             //……可以添加对Editor的操作 

            //返回一个通过
			editorInput = new PersistentEditorInput(ID);
		}
		// 返回一个editorInput 对象
		return editorInput;
	}
   }

(3)定义Editor的EditorInput,使得能实现Editor内容的恢复:
 
public class PersistentEditorInput extends EditorInputAdapter implements IPersistableElement {	public static final String TAG_ACTIVE_TAG_INDEX = "subTagIndex";
	static final String TAG_VERTICAL_POSITION = "verticalPosition";
	static final String TAG_HORIZONTAL_POSITION = "horizontalPosition";

	private String editorId;

	public PersistentEditorInput(String editorId) {
		super("");
		this.editorId = editorId;
	}

	// ------Persistent--------------------------------------------------
	@Override
	public void saveState(IMemento memento) {
		IMemento editorMem = memento.createChild(editorId);
		if (ID.equals(editorId)) {
		     TestEditor editor = EditorUtil.getTestEditor();
		     if (editor != null) {
			editorMem.putInteger(TAG_ACTIVE_TAG_INDEX,  
  editor .getxxxSelect());
			editorMem.putInteger(TAG_VERTICAL_POSITION, 
  editor.getxxxScollerPositoin(SWT.V_SCROLL));
  		……
		     }
		} else {
          //可以添加其他类型的editor状态
		}
	}

	@Override
	public String getFactoryId() {//指定恢复元素的Factory
		return PersistentElementFactory.ID;
	}

	@Override
	public IPersistableElement getPersistable() {
		return this;
	}

	@Override
	public boolean exists() {
		// Editor Persistent需要返回true
		return true;
	}
   }

(4)指定把定制好的EditorInput对象,setInput到对于的Editor
  
IEditorPart configEditor = EditorUtil.openEditor(new PersistentEditorInput(TestEditor.ID), 
     TestEditor.ID);
   ......
分享到:
评论

相关推荐

    org.eclipse.paho.ui.app-1.0.0.zip

    标题中的"org.eclipse.paho.ui.app-1.0.0.zip"表明这是一个Eclipse Paho项目的用户界面应用程序的版本号为1.0.0的压缩包。Eclipse Paho是一个开源项目,它提供了多种编程语言的库和服务,用于在物联网(IoT)环境中...

    org.eclipse.paho.ui.app-1.0.2-win32.win32.x86_64.zip

    5. **会话持久化**:在断开连接后,能够恢复先前的订阅和消息状态,保证连接的连续性。 6. **日志记录**:提供详细的日志输出,帮助开发者追踪和诊断问题。 7. **可视化界面**:用户友好的图形界面,使得测试过程...

    eclipse黑色主题版本(plugins2)

    安装这些插件后,Eclipse不仅具备了黑色主题,还增强了其文档支持、插件开发、Java持久化、JavaScript开发、用户界面、窗口系统、项目设置以及用户引导等功能,从而提升了开发者的整体使用体验。

    eclipse可视化插件Visual Editor安装包和安装说明

    在VE中,EMF作为基础,支持UI设计时的数据绑定和持久化。 3. **GEF (Graphical Editing Framework)**:GEF是Eclipse的一个图形编辑框架,用于构建图形化的编辑器。它提供了底层的绘图和交互机制,让开发者可以专注...

    Practical Eclipse Rich Client Platform Projects 2009

    9. **持久化存储**:讲解如何使用Eclipse的模型对象持久化服务,保存和恢复用户数据和应用状态。 10. **团队协作与版本控制**:介绍如何将Eclipse RCP项目整合到版本控制系统如Git或SVN中,实现团队开发和代码管理...

    eclipse-neon-plugins14

    6. **Remote System Explorer (RSE)**:org.eclipse.rse.ui_3.3.300.201610252046.jar提供了远程系统探索功能,允许开发者在Eclipse中管理和操作远程文件系统、数据库和应用程序服务器,极大地扩展了Eclipse的适用...

    Eclipse解压04

    4. **JPT(Java Persistence Tools)**:`org.eclipse.jpt.*`文件与Java持久化工具有关,如`org.eclipse.jpt.core_2.2.1.v200908270220.jar`和`org.eclipse.jpt.ui_2.2.2.v200911250220.jar`,它们可能提供了对Java ...

    Eclipse属性页应用详解-附代码

    5. **首选项存储**:属性页中的设置通常需要持久化,Eclipse提供了`IPreferenceStore`接口,用于读写用户的偏好设置。你可以通过`Platform.getPreferencesService()`获取服务,并根据需要使用`readBoolean`、`...

    Eclipse解压03

    8. `org.eclipse.persistence.core_1.1.3.v20091002-r5404.jar`:Eclipse Persistence服务的核心库,用于支持Java持久化,如JPA(Java Persistence API)。 9. `org.eclipse.swt.win32.win32.x86_3.5.2.v3557f.jar`...

    org.eclipse.paho.ui.app-1.0.2-win32.x86_64.zip.zip

    标题中的"org.eclipse.paho.ui.app-1.0.2-win32.x86_64.zip.zip"表明这是一个Eclipse Paho MQTT客户端用户界面应用的软件包,版本号为1.0.2,适用于Windows操作系统,且是64位架构。这个软件包的核心功能是提供MQTT...

    eclipse-SDK-3.4.2的ve插件套装

    这些插件的安装对于那些希望在Eclipse 3.4.2环境中使用图形化编辑功能的开发者来说至关重要。`vefor3.4`很可能是一个包含所有这些插件的压缩包,用户需要解压并按照Eclipse的插件安装步骤进行操作,通常包括将插件...

    QQ 实战 客户端 eclipse

    3. 数据持久化:理解如何将数据持久化,即使应用关闭也能恢复状态。 六、安全与优化 1. 加密通信:为了保护用户隐私,QQ客户端的通信过程应采用加密技术,如SSL/TLS。 2. 性能调优:通过合理的设计和代码优化,...

    Eclipse RCP应用系统开发方法与实战(源代码)

    2. **第8章.rar** - 可能涉及到的是RCP应用中的数据持久化和数据库集成,包括使用JDBC、ORM工具(如Hibernate)与数据库交互,以及如何设计数据模型和事务管理。这些知识对于构建需要处理大量数据的RCP应用至关重要...

    eclipse工具5

    16. **EMF(Eclipse Modeling Framework)**:用于创建和操作模型的框架,常用于生成代码和持久化数据。 以上就是关于"Eclipse工具5"的一些核心知识点,具体的版本可能有额外的新功能和改进,但上述内容涵盖了...

    项目积累eclipse

    【标题】"项目积累eclipse"是一个以Eclipse为核心开发工具的软件项目,它整合了前端的EasyUI、后端的Dubbo框架以及MyBatis作为持久层技术。这个项目旨在通过实际操作来积累编程经验,提升开发者在业务逻辑处理和代码...

    ibator的eclipse插件

    `ibator`(IntelliJ IDEA的iBATIS Generator的Eclipse版本)是一款强大的数据库代码自动生成工具,它能够帮助开发者快速地生成Java持久层代码,包括实体类、Mapper接口及XML配置文件等,大大提高了开发效率。...

    基于eclipse平台开发手机通讯录

    在本文中,我们将深入探讨如何基于Eclipse平台开发一款手机通讯录应用,主要涉及Java编程语言和Android开发环境。Eclipse作为一个强大的集成开发环境(IDE),对于Java开发者来说,是构建各种应用程序的理想选择,...

    Eclipse RCP基础资料

    7. **持久化与首选项**:Eclipse RCP提供了内置的持久化支持,用于保存用户的配置和首选项。 8. **调试与测试**:Eclipse RCP的开发环境中集成了强大的调试工具,同时支持单元测试和集成测试。 9. **国际化与本地...

    高仿微信eclipse源码

    通过深入研究这些文件,开发者不仅可以了解Android应用开发的基本流程,还可以学习到如何设计和实现复杂的用户界面,以及如何处理网络请求、数据持久化、多线程等问题。此外,对于想要提升Android编程技能,尤其是对...

Global site tag (gtag.js) - Google Analytics