18844


Eclipseプラグイン開発でPropertyPageを作成しているのですが、その画面を開くと次のようなエラーが出ます。

[![画像の説明をここに入力](https://i.stack.imgur.com/Jy3Lt.png)](https://i.stack.imgur.com/Jy3Lt.png)

その際、スタックトレースは次のように出力されます。

[code]

    java.lang.ClassCastException: org.eclipse.jdt.internal.core.JavaProject cannot be cast to org.eclipse.core.resources.IProject
        at link.arata.mybatishelper.ProjectPropertyPage.createContents(ProjectPropertyPage.java:22)
        at org.eclipse.jface.preference.PreferencePage.createControl(PreferencePage.java:241)
        at org.eclipse.jface.preference.PreferenceDialog.createPageControl(PreferenceDialog.java:1450)
        at org.eclipse.jface.preference.PreferenceDialog$13.run(PreferenceDialog.java:1217)
        at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)
        at org.eclipse.ui.internal.JFaceUtil$1.run(JFaceUtil.java:50)
        at org.eclipse.jface.util.SafeRunnable.run(SafeRunnable.java:173)
        at org.eclipse.jface.preference.PreferenceDialog.showPage(PreferenceDialog.java:1209)
        at org.eclipse.ui.internal.dialogs.FilteredPreferenceDialog.showPage(FilteredPreferenceDialog.java:608)
        at org.eclipse.jface.preference.PreferenceDialog$9$1.run(PreferenceDialog.java:675)
        at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:70)
        at org.eclipse.jface.preference.PreferenceDialog$9.selectionChanged(PreferenceDialog.java:670)
        at org.eclipse.jface.viewers.StructuredViewer$3.run(StructuredViewer.java:877)
        at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)
        at org.eclipse.ui.internal.JFaceUtil$1.run(JFaceUtil.java:50)
        at org.eclipse.jface.util.SafeRunnable.run(SafeRunnable.java:173)
        at org.eclipse.jface.viewers.StructuredViewer.firePostSelectionChanged(StructuredViewer.java:874)
        at org.eclipse.jface.viewers.StructuredViewer.handlePostSelect(StructuredViewer.java:1243)
        at org.eclipse.jface.viewers.StructuredViewer$5.widgetSelected(StructuredViewer.java:1269)
        at org.eclipse.jface.util.OpenStrategy.firePostSelectionEvent(OpenStrategy.java:265)
        at org.eclipse.jface.util.OpenStrategy.access$5(OpenStrategy.java:259)
        at org.eclipse.jface.util.OpenStrategy$1$2.run(OpenStrategy.java:440)
        at org.eclipse.swt.widgets.RunnableLock.run(RunnableLock.java:35)
        at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:135)
        at org.eclipse.swt.widgets.Display.runAsyncMessages(Display.java:4024)
        at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3700)
        at org.eclipse.jface.window.Window.runEventLoop(Window.java:827)
        at org.eclipse.jface.window.Window.open(Window.java:803)
        at org.eclipse.ui.dialogs.PropertyDialogAction.run(PropertyDialogAction.java:157)
        at org.eclipse.jface.action.Action.runWithEvent(Action.java:473)
        at org.eclipse.jface.action.ActionContributionItem.handleWidgetSelection(ActionContributionItem.java:595)
        at org.eclipse.jface.action.ActionContributionItem.access$2(ActionContributionItem.java:511)
        at org.eclipse.jface.action.ActionContributionItem$5.handleEvent(ActionContributionItem.java:420)
        at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:84)
        at org.eclipse.swt.widgets.Display.sendEvent(Display.java:4230)
        at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1491)
        at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1514)
        at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1499)
        at org.eclipse.swt.widgets.Widget.notifyListeners(Widget.java:1299)
        at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:4072)
        at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3698)
        at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$4.run(PartRenderingEngine.java:1127)
        at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:337)
        at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1018)
        at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:156)
        at org.eclipse.ui.internal.Workbench$5.run(Workbench.java:654)
        at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:337)
        at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:598)
        at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:150)
        at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:139)
        at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196)
        at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:134)
        at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:104)
        at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:380)
        at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:235)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:497)
        at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:669)
        at org.eclipse.equinox.launcher.Main.basicRun(Main.java:608)
        at org.eclipse.equinox.launcher.Main.run(Main.java:1515)
        at org.eclipse.equinox.launcher.Main.main(Main.java:1488)
    
[/code]

plugin.xmlは次のように記述しています。

[code]

     <extension point="org.eclipse.ui.propertyPages">
      <page
       class="link.arata.mybatishelper.ProjectPropertyPage"
       id="link.arata.mybatishelper.projectPropertyPage"
       name="MyBatis Helper">
       <enabledWhen>
        <adapt type="org.eclipse.core.resources.IProject" />
       </enabledWhen>
      </page>
     </extension>
    
[/code]

ProjectPropertyPageは次のとおりです。

[code]

    package link.arata.mybatishelper;
    
    public class ProjectPropertyPage extends PropertyPage {
        private String KEY_NEW_LINE_CODE;
        private Text newLineCode;
    
        @Override
        protected Control createContents(Composite parent) {
            IProject project = (IProject) getElement();
    
            Composite composite = new Composite(parent, SWT.NONE);
            // 略
            return composite;
        }
    
        @Override
        public boolean performOk() {
            IProject project = (IProject) getElement();
            setValue(project, KEY_NEW_LINE_CODE, newLineCode.getText());
    
            return true;
        }
    
        private String getValue(IProject project, String key) {
            try {
                return project.getPersistentProperty(new QualifiedName(Activator.PLUGIN_ID, key));
            } catch (CoreException e) {
                ILog log = Activator.getDefault().getLog();
                log.log(e.getStatus());
                return null;
            }
        }
    
        private void setValue(IProject project, String key, String value) {
            try {
                project.setPersistentProperty(new QualifiedName(Activator.PLUGIN_ID, key), value);
            } catch (CoreException e) {
                ILog log = Activator.getDefault().getLog();
                log.log(e.getStatus());
            }
        }
    }
    
[/code]

例外が出ている箇所は ProjectPropertyPage#createContentsメソッドの最初の行の`IProject project =
(IProject) getElement();`の部分でキャストできなくてエラーなのは分かっています。

ただ、書籍（古いですが、Eclipseプラグイン開発
徹底攻略）やWebサイト（hishidamaさんのサイト）、また[他のプラグイン](https://github.com/seasarorg/s2daoplugin/blob/master/org.seasar.s2daoplugin/src/org/seasar/s2daoplugin/property/S2DaoPropertyPage.java#L80)などを見ても同様の記述しか無いため、どのように書けばいいのかわからない状況です。

追記：関係するかわかりませんが、MANIFEST.MFは次のとおりです。

[code]

    Manifest-Version: 1.0
    Bundle-ManifestVersion: 2
    Bundle-Name: MyBatis Helper
    Bundle-SymbolicName: link.arata.mybatishelper;singleton:=true
    Bundle-Version: 0.1.0
    Bundle-Activator: link.arata.mybatishelper.Activator
    Require-Bundle: org.eclipse.ui,
     org.eclipse.core.runtime,
     org.eclipse.ui.editors;bundle-version="3.9.0",
     org.eclipse.ui.ide;bundle-version="3.11.0",
     org.eclipse.core.resources;bundle-version="3.10.1"
    Bundle-RequiredExecutionEnvironment: JavaSE-1.6
    Bundle-ActivationPolicy: lazy
    
[/code]


CC BY-SA 3.0
2015-11-14T20:51:27.347
18826
2015-11-15T10:25:20.163
2015-11-15T05:51:15.450
2214
2214
question
eclipse
EclipseプラグインのPropertyPageで例外が出る


argusさんのコメントの通り、adapterパターンを使ってビューとモデルをつなぐところに問題がありました。質問者の方がfacebookでこちらにヘルプを求められていたのと、以前Eclipseプラグインを作っていたことがあるので、少し解説を加えさせてください。

Eclipse上の多くのモデルは`PlatformObject`を継承し、`IAdaptable`インタフェースを実装しています。この`IAdaptable`はプラグインを実装する上でとても重要なインタフェースです。

今回`getElement()`で取得できている`IJavaProject`はJavaのプロジェクトを示すJDTのモデルです。多分`PackageExplorerビュー`でJavaプロジェクトをクリックした時に取得できているのではないでしょうか？仮に`Navigatorビュー`で同じプロジェクトをクリックすると、`IProject`インタフェースのオブジェクトが取得できると思います。`PackageExplorerビュー`はJava開発用に作られたJDTプラグインのビュー、`Navigatorビュー`はワークスペース上のリソース管理のために作られたresourceプラグインのビューです。実態としてはどちらも同じものを指し示すモデルですが、ドメインが異なるため、異なるインタフェースを使っています。こういった時、よく行われるのはモデルクラスに必要なインタフェースを実装することですが、そうするとそのインタフェースに依存することになるため、プラガブルではなくなります。

その問題を解決するのが`IAdaptable`です。`IAdaptable`には今回登場した`#getAdapter(class)`というメソッドがあります。このメソッドは指定した`class`の型のオブジェクトに変換可能であればその型に変換したオブジェクトを取得する、変換できない場合はnullを返します。今回の場合は`JavaProject#getAdapter(class)`は`IProject`へ変換可能だったため、`IProject`が取得できました。

この変換可能かどうかを管理するのが`IAdapterManager`であり、変換ルールを実装するのが`IAdapterFactory`です。`IAdapterManager`は`Workbench`に紐付いており、IAdapterFactoryによる拡張をplugin.xmlに書いておくことでいろいろなドメインのモデルに変換可能にしています。  
ちらっと紹介したプラグインのモデルの基底クラスの`PlatformObject`は`IAdapterManager#getAdapter()`に処理を委譲しています。

この抽象的な部分がEclipseの拡張性の高さを生んでいますがなかなかとっつきにくさがありますね。


CC BY-SA 3.0
2015-11-15T08:40:03.313
18844
2015-11-15T09:47:44.757
2015-11-15T09:47:44.757
7290
753
18826
answer


結果的にコメント等の通り次のコードでIProjectが取得できました。説明は上記、Kondoさんのものを参照してください。

[code]

    protected IProject getProject() {
        IAdaptable element = getElement();
        if (element instanceof IProject) {
            return (IProject) element;
        }
        Object project = element.getAdapter(IProject.class);
        if (project instanceof IProject) {
            return (IProject) project;
        }
        return null;
    }
    
[/code]

また、 `getElement()` で取得できるオブジェクトは、Package Explorerビュー（Javaパースペクティブ）では
`IJavaProject` になり、Project Explorerビュー（Resourceパースペクティブ）では `IProject`
になることを確認しました。


CC BY-SA 3.0
2015-11-15T09:15:01.313
18847
2015-11-15T10:25:20.163
2015-11-15T10:25:20.163
7290
2214
18826
answer