7

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?

Jonathan Holloway
  • 62,090
  • 32
  • 125
  • 150

4 Answers4

9

[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.

Dathan
  • 7,266
  • 3
  • 27
  • 46
Scott Stanchfield
  • 29,742
  • 9
  • 47
  • 65
  • Sure, but: This interface is not intended to be implemented by clients. – Jonathan Holloway Apr 22 '09 at 14:50
  • 1
    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 Apr 22 '09 at 19:31
  • 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. – Jonathan Holloway Apr 23 '09 at 06:46
  • 2
    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 Apr 23 '09 at 12:56
  • 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 Apr 23 '09 at 12:58
  • 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." – Jonathan Holloway Apr 24 '09 at 03:11
  • 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 Apr 24 '09 at 15:14
  • 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 Apr 24 '09 at 15:15
  • I'm trying to avoid ICompilationUnit and CompilationUnit because I want a GENERIC way of obtaining the contents of a file, the filename and 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 be a monkey file, it could be a badger file. Regardless, I want the metadata regarding that file and its' contents. – Jonathan Holloway Apr 28 '09 at 20:44
  • 2
    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 Apr 29 '09 at 12:59
  • 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 Apr 29 '09 at 13:54
4

That (decoupling Resource from JDT) was one of the goals of E4 (Eclipse 4).
The plugin list for REsources doesn't mention JDT anymore (again, Eclipse 4.x only):

  • org.eclipse.core.filesystem - An abstract, generic file system API, including an implementation of this API for the local file system. This is the API through which the resources plugin accesses an underlying file system.
  • org.eclipse.core.resources - Contains the API and implementation of the resource model
  • org.eclipse.core.resources.compatibility - A plug-in providing migration support for users opening old workspaces in Eclipse 3.1 or greater

A Demo project like e4photo don't require any JDT for accessing IResource from the selection of an IContainer.

void setSelection(@Named(IServiceConstants.ACTIVE_SELECTION)
        IResource selection) {
...
IResource[] members = input.members();
...
IResource resource = members[i];
if (resource.getType() == IResource.FILE) {
  InputStream contents = ((IFile) resource).getContents();
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
3

It looks like the JDT is already defining an IAdapterFactory for all Java elements including compilation unit (org.eclipse.jdt.internal.ui.JavaElementAdapterFactory). The adapter is defined for IResource rather than IFile so you should be able to do:

Object firstElement = treeSelection.getFirstElement();

IResource resource = (IResource)Platform.getAdapterManager().getAdapter(firstElement, IResource.class);
greg-449
  • 109,219
  • 232
  • 102
  • 145
3

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
  • 9,242
  • 2
  • 30
  • 29