Virtual containers in tree viewers using EMF.Edit

Maybe you already stumbled upon the problem to display model instances with hundreds or even thousands of child elements in a tree based viewer. Displaying all that children as direct successors of their parent (shown in the figure on the left) is confusing and slows down the UI.

An alternative approach is to hook in virtual folders, that contain a subset of the parent’s child elements instead. This approach is shown in the figure on the right and might be familiar to you since it’s also used in the Eclipse ‘Variables’ view.

If you use EMF.Edit based item providers to display your domain objects read on!

The technique shown here is a slightly modified version of the technique explained in chapter 19.2.3 Adding Non-Model Intermediary View Objects of the standard Eclipse Modeling Framework book.

The source code below is used to display the right tree viewer in the illustration above. It’s usual EMF.Edit programming. The only difference is the usage of a PartitionedContentProvider instead of an AdapterFactoryContentProvider.

You find the implementation of PartitionedContentProvider here. It’s packaged into a plugin but feel free to copy that single class into your own project. Overwrite the method getVirtualFolderSize() to limit the maximum number of container’s direct children. If the number exceeds this limit, virtual folders are hooked in instead. If the virtual folder size is -1 virtual folders are not created.


final AdapterFactory adapterFactory = new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE);
domain = new AdapterFactoryEditingDomain(adapterFactory, new BasicCommandStack());

viewer = new TreeViewer(parent, SWT.FLAT | SWT.MULTI);

viewer.addDragSupport(DND.DROP_MOVE, new Transfer[] { LocalTransfer.getInstance() }, new ViewerDragAdapter(viewer));
viewer.addDropSupport(DND.DROP_MOVE, new Transfer[] { LocalTransfer.getInstance() }, new EditingDomainViewerDropAdapter(domain, viewer));
viewer.setLabelProvider(new AdapterFactoryLabelProvider(adapterFactory));

// we limit the maximal child elements for containers to 100
viewer.setContentProvider(new PartitionedContentProvider(adapterFactory) {

       @Override
       protected int getVirtualFolderSize(Object folder) {
          return folder instanceof Container ? 100 : DEFAULT_FOLDER_SIZE;
       }
});


Adding Maven artifacts to your target platform

Don’t get me wrong I really like Maven. It’s a great and technically mature build system. Nevertheless I try to avoid it in all my RCP and OSGi projects because all that POM declarations are redundant and unnecessary in some way. Inter bundle dependencies are expressed by means of OSGi manifests and the build process provided by the Eclipse environment respectively the headless PDE build is more than adequate. Obviously this is just a matter of taste and if you use Tycho or other IDEs with the better Maven support, maybe you will not agree.

Anyway there are many existing OSGi bundles (e.g. Jetty, Vaadin, log4j, Knopflerfish) available in Maven Central and manually downloading them together with all their dependencies is a cumbersome task. Also Maven dependencies without proper OSGi metadata information can be transformed via the maven-bundle-plugin if required.

So during my experiments with the embedded XULRunner, I tried to find a mechanism that automatically adds bundles from Maven repositories to my active target platform. If you are not familiar with the concept of Eclipse target platforms read this tutorial first.

This is my best solution so far. If you have better ideas please let me know!

Create a simple project named target_platform in your workspace. In the new project create a sub folder named maven_libs. Later this sub folder is the location of all jar files fetched by Maven. Select New->Other->Target Definition and add the maven_libs folder to the list of required locations.

The path ${project_loc:/target_platform}/maven_libs works fine even if the target_platform project is outside the current workspace. Of course you can still add further locations like environment variables, folders or p2 repositories.

Create the following pom.xml definition that lists all required dependencies in your target platform project and configure the outputDirectory accordingly.


<project>
    <modelVersion>4.0.0</modelVersion>
    <name>Target Platform Project</name>
  
    <groupId>com.javahacks.sample</groupId>
    <artifactId>target_platform</artifactId>
    <version>1.0</version>  
    <packaging>pom</packaging>
	
	<properties>
		<outputDirectory>maven_libs</outputDirectory>
	</properties>

	
    <dependencies>
        
		<!--Configure dependencies here -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>       
        </dependency>
               
    </dependencies>
  
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.8</version>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>initialize</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>                            
                            <overWriteReleases>false</overWriteReleases>
                            <overWriteSnapshots>false</overWriteSnapshots>
                            <overWriteIfNewer>true</overWriteIfNewer>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
  
           
        </plugins>
  
    </build>
    
</project>

Now run mvn install from within Eclipse or on the command line inside the ‘target_platform’ location to fetch all libraries configured in the POM configuration.

Reopen the target definition and select Set as Target Platform. If you run Maven outside your IDE refresh the target platform project first.

This approach is also useful for the POM dependency resolution explained here. Now you have the same bundle resolution at the build and development time and running the Maven installation to setup the target platform on your build server (without Tycho) is no problem.