eclipse的入口插件:
org.eclipse.platform
产品的定义,用到的图标,外观的定义都在这里,plugin.xml绑定一个application
这里没有代码,只是样式、图标。
org.eclipse.ui.ide.application
该插件是eclipse的真正入口,
主要的类有:
IDEApplication.java IDEWorkbenchAdvisor.java IDEWorkbenchWindowAdvisor.java
ResourcePerspective.java
这几个类大家应该都很属性。
不过没有ActionBarAdvisor的定义,说明eclipse中所有的菜单、工具栏,在启动的时候是为空的。
里面的菜单都是其它插件扩展得到的。
接下来进入org.eclipse.core.runtime 看看。
这里主要用到了 osgi equinox java.io java.net
Platform 对该类进行了包装 InternalPlatform
InternalPlatform 中使用了个单利模式。
eclipse 也大量运用了单利模式。
public IAdapterManager getAdapterManager() {
assertInitialized();
return AdapterManager.getDefault();
}
查看代码可知 如何名称相同的bundle 会返回第一个被加载的bundle。
public Bundle getBundle(String symbolicName) {
PackageAdmin packageAdmin = getBundleAdmin();
if (packageAdmin == null)
return null;
Bundle[] bundles = packageAdmin.getBundles(symbolicName, null);
if (bundles == null)
return null;
//Return the first bundle that is not installed or uninstalled
for (int i = 0; i < bundles.length; i++) {
if ((bundles[i].getState() & (Bundle.INSTALLED | Bundle.UNINSTALLED)) == 0) {
return bundles[i];
}
}
return null;
}
该插件也相当于eclipse的 发动机了,服务的启动、停止、文件的加载等,都是在这里处理的。
下了看一下 org.eclipse.ui 插件。
该插件有点出乎意料
只有两个类UIPlugin UIPreferenceInitializer
UIPlugin 中用到了 PrefUtil 这个类是对preferences操作的一个工具类,用着还是很方便的。
PrefUtil .getInternalPreferenceStore(); 直接得到 系统 IPreferenceStore
UIPreferenceInitializer 为eclipse 初始化一些参数,它是在这里定义的。
<extension
point="org.eclipse.core.runtime.preferences">
<initializer
class="org.eclipse.ui.internal.UIPreferenceInitializer">
</initializer>
</extension>
使用的是该org.eclipse.core.runtime.preferences扩展点,这个扩展点 在eclipse中用的还是很广的。
这个插件没有别的类了,剩下的就是一些扩展点的定义,并实现了一下扩展。
这里定义的都是系统最基础的扩展点,并初步实现了最基础的扩展。
org.eclipse.ui 插件的扩展点的定义、扩展 实现是通过那个插件实现的。
就拿org.eclipse.ui.views这个扩展点来说。
该扩展点依赖的类为 ViewPart,该类是在org.eclipse.ui.workbench 插件中定义的。
org.eclipse.ui.part包下。
查看ViewPart 源码有它实现了
protected final void checkSite(IWorkbenchPartSite site) {
super.checkSite(site);
Assert.isTrue(site instanceof IViewSite, "The site for a view must be an IViewSite");
}
这里说明 Viewpart中的 site一定是 ViewSite
public Object getAdapter(Class adapter) {
return Platform.getAdapterManager().getAdapter(this, adapter);
}
说明viewpart 是一个平台对象,可以通过adapter 扩展点进行适配。
实现了IExecutableExtension接口
说明它可以直接得到 扩展 中定义的 内容。
实现了EventManager 接口
说明可以 添加监听,这里把监听定义为
public void addPropertyListener(IPropertyListener l) {
addListenerObject(l);
}
实际上还是添加到了 EventManager 中。
IPropertyListener 监听是对Viewpart 属性更改的监听。
protected void setTitle(String title) {
title = Util.safeString(title);
//Do not send changes if they are the same
if (Util.equals(this.title, title)) {
return;
}
this.title = title;
firePropertyChange(IWorkbenchPart.PROP_TITLE);
}
protected void firePropertyChange(final int propertyId) {
Object[] array = getListeners();
for (int nX = 0; nX < array.length; nX++) {
final IPropertyListener l = (IPropertyListener) array[nX];
try {
l.propertyChanged(WorkbenchPart.this, propertyId);
} catch (RuntimeException e) {
WorkbenchPlugin.log(e);
}
}
}
EventManager 该类在eclipse中用的也是很多的,很多类都实现了该对象。
功能:实现了对监听的维护(同步的)。
IPropertyListener Viewpat属性更改的监听
public void addPropertyListener(IPropertyListener l) {
addListenerObject(l);
}
下面的这个监听是IWorkbenchPart3中定义的。可以在ViewPart绑定一些数据数据存放在Map中,Map中的数据
有变化时,就会通知监听。
public void addPartPropertyListener(IPropertyChangeListener listener) {
partChangeListeners.add(listener);
}
这样做是为了WorkbenchPart 具有更好的扩展性。
IWorkbenchPart3 官方定义
A part can provide arbitrary properties. The properties will be persisted between sessions by the part reference, and will be available from the part reference as well as the part. The properties can only be set on a part, not on the reference. The properties will be available to the IPresentablePart.
Setting a property must fire a PropertyChangeEvent.
ViewPart就先研究到这里。
接下来我们重点研究一下org.eclipse.ui.workbench
org.eclipse.ui.workbench 我们工作中使用的class基本上都在这个插件中实现的。
也算是 eclipse 的肌肉、骨骼了。
eclipse 大量使用了 Assert 类,来对参数的数据类型进行校正。
如何出现 断言的异常,可能就是 数据类型 不正确。
并且internal包下的都是对接口的实现。
基本目录:
在看NewWizardAction 源码中看到了
LegacyResourceSupport类,这个类就是加载不同插件的class的。
这里也说明了不同插件(Bundle)是通过不同的类加载器加载的。
IDialogSettings workbenchSettings = WorkbenchPlugin.getDefault()
.getDialogSettings();
通过IDialogSettings 可以对wizard的数据进行持久化到 xml,并进行状态保存。
An interface to a storage mechanism for making dialog settings persistent. The store manages a collection of key/value pairs. The keys must be strings and the values can be either, strings or array of strings. Convenience API to convert primitive types to strings is provided.
NewWizard wizard = new NewWizard();
wizard.setCategoryId(categoryId);
wizard.setWindowTitle(windowTitle);
ISelection selection = workbenchWindow.getSelectionService()
.getSelection();
IStructuredSelection selectionToPass = StructuredSelection.EMPTY;
if (selection instanceof IStructuredSelection) {
selectionToPass = (IStructuredSelection) selection;
} else {
// @issue the following is resource-specific legacy code
// Build the selection from the IFile of the editor
Class resourceClass = LegacyResourceSupport.getResourceClass();
if (resourceClass != null) {
IWorkbenchPart part = workbenchWindow.getPartService()
.getActivePart();
if (part instanceof IEditorPart) {
IEditorInput input = ((IEditorPart) part).getEditorInput();
Object resource = Util.getAdapter(input, resourceClass);
if (resource != null) {
selectionToPass = new StructuredSelection(resource);
}
}
}
}
wizard.init(workbenchWindow.getWorkbench(), selectionToPass);
IDialogSettings workbenchSettings = WorkbenchPlugin.getDefault()
.getDialogSettings();
IDialogSettings wizardSettings = workbenchSettings
.getSection("NewWizardAction"); //$NON-NLS-1$
if (wizardSettings == null) {
wizardSettings = workbenchSettings.addNewSection("NewWizardAction"); //$NON-NLS-1$
}
wizard.setDialogSettings(wizardSettings);
wizard.setForcePreviousAndNextButtons(true);
Shell parent = workbenchWindow.getShell();
WizardDialog dialog = new WizardDialog(parent, wizard);
dialog.create();
dialog.getShell().setSize(
Math.max(SIZING_WIZARD_WIDTH, dialog.getShell().getSize().x),
SIZING_WIZARD_HEIGHT);
PlatformUI.getWorkbench().getHelpSystem().setHelp(dialog.getShell(),
IWorkbenchHelpContextIds.NEW_WIZARD);
dialog.open();
这是eclipse 打开一个创建对话框的代码,可以借鉴一下。
这个插件内的很多代码大家都可以在做插件开发中借鉴一下的。
所有的IHandler 都是通过
CommandAction 来包装后执行的。
CommandAction 这里的 源码,看看还是有些启发的。
public class CommandAction extends Action {
private IHandlerService handlerService = null;
private ParameterizedCommand parameterizedCommand = null;
private ICommandListener commandListener;
protected CommandAction() {
}
/**
* Creates the action backed by a command. For commands that don't take
* parameters.
*
* @param serviceLocator
* The service locator that is closest in lifecycle to this
* action.
* @param commandIdIn
* the command id. Must not be <code>null</code>.
*/
public CommandAction(IServiceLocator serviceLocator, String commandIdIn) {
this(serviceLocator, commandIdIn, null);
}
/**
* Creates the action backed by a parameterized command. The parameterMap
* must contain only all required parameters, and may contain the optional
* parameters.
*
* @param serviceLocator
* The service locator that is closest in lifecycle to this
* action.
* @param commandIdIn
* the command id. Must not be <code>null</code>.
* @param parameterMap
* the parameter map. May be <code>null</code>.
*/
public CommandAction(IServiceLocator serviceLocator, String commandIdIn,
Map parameterMap) {
if (commandIdIn == null) {
throw new NullPointerException("commandIdIn must not be null"); //$NON-NLS-1$
}
init(serviceLocator, commandIdIn, parameterMap);
}
protected ICommandListener getCommandListener() {
if (commandListener == null) {
commandListener = new ICommandListener() {
public void commandChanged(CommandEvent commandEvent) {
if (commandEvent.isHandledChanged()
|| commandEvent.isEnabledChanged()) {
if (commandEvent.getCommand().isDefined()) {
setEnabled(commandEvent.getCommand().isEnabled());
}
}
}
};
}
return commandListener;
}
/**
* Build a command from the executable extension information.
*
* @param commandService
* to get the Command object
* @param commandId
* the command id for this action
* @param parameterMap
*/
private void createCommand(ICommandService commandService,
String commandId, Map parameterMap) {
Command cmd = commandService.getCommand(commandId);
if (!cmd.isDefined()) {
WorkbenchPlugin.log("Command " + commandId + " is undefined"); //$NON-NLS-1$//$NON-NLS-2$
return;
}
if (parameterMap == null) {
parameterizedCommand = new ParameterizedCommand(cmd, null);
return;
}
parameterizedCommand = ParameterizedCommand.generateCommand(cmd,
parameterMap);
}
public void dispose() {
// not important for command ID, maybe for command though.
handlerService = null;
if (commandListener != null) {
parameterizedCommand.getCommand().removeCommandListener(
commandListener);
commandListener = null;
}
parameterizedCommand = null;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.action.Action#runWithEvent(org.eclipse.swt.widgets.Event)
*/
public void runWithEvent(Event event) {
if (handlerService == null) {
String commandId = (parameterizedCommand == null ? "unknownCommand" //$NON-NLS-1$
: parameterizedCommand.getId());
WorkbenchPlugin.log("Cannot run " + commandId //$NON-NLS-1$
+ " before command action has been initialized"); //$NON-NLS-1$
return;
}
try {
if (parameterizedCommand != null) {
handlerService.executeCommand(parameterizedCommand, event);
}
} catch (Exception e) {
WorkbenchPlugin.log(e);
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.action.Action#run()
*/
public void run() {
// hopefully this is never called
runWithEvent(null);
}
protected void init(IServiceLocator serviceLocator, String commandIdIn,
Map parameterMap) {
if (handlerService != null) {
// already initialized
return;
}
handlerService = (IHandlerService) serviceLocator
.getService(IHandlerService.class);
ICommandService commandService = (ICommandService) serviceLocator
.getService(ICommandService.class);
ICommandImageService commandImageService = (ICommandImageService) serviceLocator
.getService(ICommandImageService.class);
createCommand(commandService, commandIdIn, parameterMap);
if (parameterizedCommand != null) {
setId(parameterizedCommand.getId());
setActionDefinitionId(parameterizedCommand.getId());
try {
setText(parameterizedCommand.getName());
} catch (NotDefinedException e) {
// if we get this far it shouldn't be a problem
}
parameterizedCommand.getCommand().addCommandListener(
getCommandListener());
parameterizedCommand.getCommand().setEnabled(
handlerService.getCurrentState());
setEnabled(parameterizedCommand.getCommand().isEnabled());
setImageDescriptor(commandImageService.getImageDescriptor(
commandIdIn, ICommandImageService.TYPE_DEFAULT));
setDisabledImageDescriptor(commandImageService.getImageDescriptor(
commandIdIn, ICommandImageService.TYPE_DISABLED));
setHoverImageDescriptor(commandImageService.getImageDescriptor(
commandIdIn, ICommandImageService.TYPE_HOVER));
}
}
protected ParameterizedCommand getParameterizedCommand() {
return parameterizedCommand;
}
public String getActionDefinitionId() {
return super.getActionDefinitionId();
}
}
还有org.eclipse.ui.internal.WorkbenchWindow
这个就是我们的rcp 窗口的实现类。
继承 jface ApplicationWindow 。
接下来我们研究一下扩展定义的信息eclipse 是在哪里调用里面的信息,并动态的调用代码
以action 为例
可以在eclipse 作为菜单、工具栏、弹出菜单 有很几个扩展点都可以实现。
看eclipse 是如何使用扩展的配置信息的。
入口肯定在
org.eclipse.ui.internal.WorkbenchWindow
private final void initializeDefaultServices() {
serviceLocator.registerService(IWorkbenchLocationService.class,
new WorkbenchLocationService(IServiceScopes.WINDOW_SCOPE,
getWorkbench(), this, null, null, null, 1));
// added back for legacy reasons
serviceLocator.registerService(IWorkbenchWindow.class, this);
final ActionCommandMappingService mappingService = new ActionCommandMappingService();
serviceLocator.registerService(IActionCommandMappingService.class,
mappingService);
final LegacyActionPersistence actionPersistence = new LegacyActionPersistence(
this);
serviceLocator.registerService(LegacyActionPersistence.class,
actionPersistence);
actionPersistence.read(); //关键是这行代码。
}
这个是初始化方法。
方法的内容:
public final void read() {
clear();
LegacyActionPersistence.super.read();
// 创建 extension registry
final IExtensionRegistry registry = Platform.getExtensionRegistry();
int actionSetCount = 0;
int editorContributionCount = 0;
int objectContributionCount = 0;
int viewContributionCount = 0;
int viewerContributionCount = 0;
final IConfigurationElement[][] indexedConfigurationElements = new IConfigurationElement[5][];
// 1 actionSets extension point.
final IConfigurationElement[] actionSetsExtensionPoint = registry
.getConfigurationElementsFor(EXTENSION_ACTION_SETS);
for (int i = 0; i < actionSetsExtensionPoint.length; i++) {
final IConfigurationElement element = actionSetsExtensionPoint[i];
final String name = element.getName();
if (TAG_ACTION_SET.equals(name)) {
addElementToIndexedArray(element, indexedConfigurationElements,
INDEX_ACTION_SETS, actionSetCount++);
}
}
// 2 editorActions extension point.
final IConfigurationElement[] editorActionsExtensionPoint = registry
.getConfigurationElementsFor(EXTENSION_EDITOR_ACTIONS);
for (int i = 0; i < editorActionsExtensionPoint.length; i++) {
final IConfigurationElement element = editorActionsExtensionPoint[i];
final String name = element.getName();
if (TAG_EDITOR_CONTRIBUTION.equals(name)) {
addElementToIndexedArray(element, indexedConfigurationElements,
INDEX_EDITOR_CONTRIBUTIONS, editorContributionCount++);
}
}
// popupMenus extension point.
final IConfigurationElement[] popupMenusExtensionPoint = registry
.getConfigurationElementsFor(EXTENSION_POPUP_MENUS);
for (int i = 0; i < popupMenusExtensionPoint.length; i++) {
final IConfigurationElement element = popupMenusExtensionPoint[i];
final String name = element.getName();
//3 TAG_OBJECT_CONTRIBUTION
if (TAG_OBJECT_CONTRIBUTION.equals(name)) {
addElementToIndexedArray(element, indexedConfigurationElements,
INDEX_OBJECT_CONTRIBUTIONS, objectContributionCount++);
//4 TAG_OBJECT_CONTRIBUTION
} else if (TAG_VIEWER_CONTRIBUTION.equals(name)) {
addElementToIndexedArray(element, indexedConfigurationElements,
INDEX_VIEWER_CONTRIBUTIONS, viewerContributionCount++);
}
}
// 4 viewActions extension point.
final IConfigurationElement[] viewActionsExtensionPoint = registry
.getConfigurationElementsFor(EXTENSION_VIEW_ACTIONS);
for (int i = 0; i < viewActionsExtensionPoint.length; i++) {
final IConfigurationElement element = viewActionsExtensionPoint[i];
final String name = element.getName();
if (TAG_VIEW_CONTRIBUTION.equals(name)) {
addElementToIndexedArray(element, indexedConfigurationElements,
INDEX_VIEW_CONTRIBUTIONS, viewContributionCount++);
}
}
readActionSets(indexedConfigurationElements[INDEX_ACTION_SETS],
actionSetCount);
readEditorContributions(
indexedConfigurationElements[INDEX_EDITOR_CONTRIBUTIONS],
editorContributionCount);
readObjectContributions(
indexedConfigurationElements[INDEX_OBJECT_CONTRIBUTIONS],
objectContributionCount);
readViewContributions(
indexedConfigurationElements[INDEX_VIEW_CONTRIBUTIONS],
viewContributionCount);
readViewerContributions(
indexedConfigurationElements[INDEX_VIEWER_CONTRIBUTIONS],
viewerContributionCount);
}
代码解释:
final IConfigurationElement[][] indexedConfigurationElements = new IConfigurationElement[5][];
这里一共有5中action的定义,所有这里的2维数组 的第一个参数为5了。
addElementToIndexedArray(element, indexedConfigurationElements,
INDEX_ACTION_SETS, actionSetCount++);
把读出来的 IConfigurationElement element 添加到对应 indexedConfigurationElements 二维数组中。
然后分别调用
readActionSets(indexedConfigurationElements[INDEX_ACTION_SETS],
actionSetCount);
readEditorContributions(
indexedConfigurationElements[INDEX_EDITOR_CONTRIBUTIONS],
editorContributionCount);
readObjectContributions(
indexedConfigurationElements[INDEX_OBJECT_CONTRIBUTIONS],
objectContributionCount);
readViewContributions(
indexedConfigurationElements[INDEX_VIEW_CONTRIBUTIONS],
viewContributionCount);
readViewerContributions(
indexedConfigurationElements[INDEX_VIEWER_CONTRIBUTIONS],
viewerContributionCount);
对不同的action进行 初始化。
进入readActionSets中。
private final void readActionSets(
final IConfigurationElement[] configurationElements,
final int configurationElementCount) {
//
// this was an even dumber fix than modifying the path
//
// stupid navigate group
// SGroup nav = menuService.getGroup(STUPID_NAVIGATE);
// if (!nav.isDefined()) {
// nav.define(new SLocation(new SBar(SBar.TYPE_MENU, null)));
// }
// stupid navigate group
final List warningsToLog = new ArrayList(1);
for (int i = 0; i < configurationElementCount; i++) {
final IConfigurationElement element = configurationElements[i];
// Read the action set identifier.
final String id = readRequired(element, ATT_ID, warningsToLog,
"Action sets need an id"); //$NON-NLS-1$
if (id == null) {
continue;
}
// Read the label.
final String label = readRequired(element, ATT_LABEL,
warningsToLog, "Actions set need a label", //$NON-NLS-1$
id);
if (label == null) {
continue;
}
// Restrict the handler to when the action set is active.
final LegacyActionSetExpression expression = new LegacyActionSetExpression(
id, window);
// Read all of the child elements.
readActionsAndMenus(element, id,
warningsToLog, expression, null);
// Define the action set.
}
logWarnings(
warningsToLog,
"Warnings while parsing the action sets from the 'org.eclipse.ui.actionSets' extension point"); //$NON-NLS-1$
}
最后找到 WorkbenchMenuService 类中的populateContributionManager 方法 具体把扩展点的信息
添加到了 具体的 menu中。
藏的好深。。。。
MenuLocationURI 这个类是对 menu [scheme]:[path]?[query] 的包装。
从定义扩展点 到 对扩展点的 调用 中间包装了很多层。 好多都是以service的形式提供的。