views:

1236

answers:

2

I'm trying to obtain details of an Eclipse user's structured selection in the Navigator Tree view. At present I have the following which is based on the org.eclipse.ui.popMenus extension point:

public void run(IAction action) {

Shell shell = new Shell();

ISelection selection = workbenchPart.getSite().getSelectionProvider().getSelection();

if (structuredSelection instanceof org.eclipse.jface.viewers.TreeSelection) {

   org.eclipse.jface.viewers.TreeSelection treeSelection = (org.eclipse.jface.viewers.TreeSelection) structuredSelection;
   IAdaptable firstElement = (IAdaptable) treeSelection.getFirstElement();

   // Relies on an internal API, bad juju
   if (firstElement instanceof org.eclipse.jdt.internal.core.CompilationUnit) {
    org.eclipse.jdt.internal.core.CompilationUnit compilationUnit = (org.eclipse.jdt.internal.core.CompilationUnit) firstElement;         
    String editorSelection = new String(compilationUnit.getContents());
   }      
}

The problem with this is that it's currently coupled to the JDT compilation unit API, which is internal and too specific for what I want.

Ideally I want to be able to get the underlying file name, type and contents without having to rely on:

  1. An internal API
  2. The JDT Compilation Unit Code.

This would then allow me to obtain the properties of a generic file when the user right clicks on a file in the navigator view.

Can somebody provide me with any pointers on how I go about doing this please?

+1  A: 

What are you trying to achieve? Do you want to get file contents? Then you can try:

IAdaptable firstElement = (IAdaptable) treeSelection.getFirstElement();

IFile file = (IFile) firstElement.getAdapter(IFile.class);
if (file != null && file.isAccessible()) {
    // Use getContents API
    ....
}
Eugene
"Ideally I want to be able to get the underlying file name, type and contents."
Jon
Once you get IFile you can get all this information from it.
Eugene
this won't work unless an adapter has been registered to convert the selection to an IFile -- see my answer below for how to do this
Scott Stanchfield
+2  A: 

[EDIT: I added the following alternative - the original answer is father down]

First: if you select something in the Package Explorer, the selected items are all Java Model objects - you have to deal with them at some level. There are two ways you can handle this:

  1. Use the ICompilationUnit directly (see farther down)
  2. Create an Eclipe adapter factory to automate the conversion

Adapter Factory Approach

You can create an adapter factory (which can live in your main plugin or a different one) that eclipse can use to automatically convert from an ICompilationUnit to an IFile.

Note: if you create the adapter factory in a different plugin, you'll probably need to set up an early-startup for it to get the adapter factory loaded. Otherwise, you'll need to have your plugin that will work with the selection depend on the plugin that provides the adapter.

There's some great details on adapters at http://www.eclipse.org/resources/resource.php?id=407, but I'll go over an implementation for this problem here.

Dependencies

The plugin that will host the adapter needs the following dependencies

  • org.eclispe.core.resources
  • org.eclipse.jdt.core

The adapter factory class

Define the following class in your new plugin

package com.javadude.foo;

import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.IAdapterFactory; import org.eclipse.jdt.core.ICompilationUnit;

public class CompilationUnitToFileAdapter implements IAdapterFactory {
    @Override
    public Object getAdapter(Object adaptableObject, Class adapterType) {
        if (adaptableObject instanceof ICompilationUnit)
            // note: "adapting" it here just means returning the ref'd IFile
            return (IFile) ((ICompilationUnit)adaptableObject).getResource();
        return null;
    }
    @Override
    public Class[] getAdapterList() {
        return new Class[] {IFile.class};
    }
}

The extension

In the plugin that will host the adapter factory, add the following to your plugin.xml:

<extension point="org.eclipse.core.runtime.adapters">
    <factory
        adaptableType="org.eclipse.jdt.core.ICompilationUnit"
        class="com.javadude.foo.AdapterFactory1">
        <adapter type="org.eclipse.core.resources.IFile" />
    </factory>
</extension>

Using the Adapter

With the above in place, you can now write:

Object firstElement = ((ITreeSelection) selection).getFirstElement();
IFile file = (IFile) Platform.getAdapterManager().
                         getAdapter(firstElement, IFile.class);
if (file == null)
    // adapter not present; cannot use as IFile
else
    // adapter present - you can use it as an IFile

Using this approach, you can add additional adapters to convert other types to IFile and your selection code doesn't care.


Direct ICompilationUnit Approach

[EDIT: I've changing the answer, but leaving the following as reference information b/c it's the standard way to explore the content of a compilation unit that was selected in the package explorer]

This is actually the preferred way to get the contents of a file in the package explorer...

Rather than use CompilationUnit, you should use ICompilationUnit. Most of the eclipse APIs use interfaces for public consumption, and classes for internal details.

If you change your code to

if (firstElement instanceof ICompilationUnit) {
    ICompilationUnit unit = (ICompilationUnit firstElement;
    String contents = new String(unit.getContents());
}

You'll be in good shape.

To see details of examining/modifying the Java Model and source code:

(In Eclipse)
Help->
  Help Contents->
    JDT Plug-in Developer's Guide->
      Programmer's Guide->
        JDT Core

That shows how to work with the Java Model appropriately

To isolate where you reference the java model, you can create an (eclipse) adapter that will convert Java Model objects into files. Assuming such an adapter exists, you can then ask the AdapterManager to convert it to a java file for you. I'll take a peek and see if one exists.

Scott Stanchfield
Sure, but: This interface is not intended to be implemented by clients.
Jon
You're not implementing it; you're _using_ it as a handle to the implementations. This is the intended usage. Check out the doc page (and please remove the down vote!)
Scott Stanchfield
And that doesn't help me anyway as it's still tied to the JDT, I was looking to "obtain the properties of a generic file" as per the question, please read it carefully - thank you anyway.
Jon
If you're selecting something in the Package Explorer, it *is* an ICompilationUnit. You can't get around that.If you want to get the IFile from it, you can call unit.getResource(), but why convert it when not necessary.I am giving you the correct answer for the context you are in. If you do the selection in another type of viewer (non-package-explorer), you can get something else as the selection. See the JDT core guide above for the navigation structure.
Scott Stanchfield
And, ICompilationUnit is the *correct* way to access it. The API docs say clients should not *implement* it, as in "public class Foo implements ICompilationUnit".
Scott Stanchfield
Read the question! "Ideally I want to be able to get the underlying file name, type and contents without having to rely on: 1. An internal API 2. The JDT Compilation Unit Code."
Jon
The code in your question refers to CompilationUnit. This tells me that you're doing the selection from the Package Explorer. The package explorer represents java files as ICompilationUnits. ICompilationUnit *is not internal* -- it's the proper handle to use to refer to java files that are selected from the package explorer. You *cannot* refer to the selection as something else.
Scott Stanchfield
Let me ask this -- what are you trying to accomplish? Why do you want to avoid ICompilationUnit? (You're correct in wanting to avoid CompilationUnit - it *is* internal)
Scott Stanchfield
I'm trying to avoid ICompilationUnit and CompilationUnit because I want a GENERIC way of obtaining the contents of a file, the filenameand the type of file. It might not be a Java file, or any other type of language for that matter, it could be a text file, it could bea monkey file, it could be a badger file. Regardless, I want the metadata regarding that file and its' contents.
Jon
It depends on which context you're selecting things. If you select items in the Package Explorer, you have to deal with the Java model - there's no way around it. If you select things in a different view, it depends on what that view exposes.
Scott Stanchfield
I've updated my answer to talk about how to register adapters for the selection. Note that you'll need to register adapters for each type of selection object.
Scott Stanchfield