Providing Drill-Down Behavior

Often the information which is shown in a diagram has a certain abstraction level, and the further details are shown in a separate diagram. For example if a workflow connects several sub-workflows, then one overview-diagram might show the complete workflow without the inner details of the sub-workflows, and several detail-diagrams show the inner details of each sub-workflow.

In this case the user wants to have an easy possibility to navigate from the overview-diagram into the detail-diagrams and vice versa. We call this "drill-down" behavior.

Although going from overview to detail is the most typical example, there are other examples, where the user navigates to more independent diagrams.

All those use cases have one thing in common: the user wants to navigate to a diagram, which "represents" the selected business object (meaning this business object is central for the diagram).

Graphiti offers the possibility to link business objects to pictogram elements (including the diagram). The drill-down feature works in a way that it searches for all diagrams having a given business object associated. Then the user can choose from the found diagrams to which to navigate (if only one diagram is found, than this is directly opened).

This is different to a where-used functionality, where the user would search for all diagrams using a business object, no matter if the diagram really "represents" the business object.

Creating a Drill-Down Feature

In this example we want to enable the users to navigate to the diagram(s) which have the currently selected EClass associated. Therefore we have to create a drill-down feature and make it available in the feature provider.

The functionality of the AbstractDrillDownFeature is not tool-independent, so the method getDiagrams() can be implemented only by the tool efficiently. Furthermore we just overwrite the canExecute() method, so that the feature is only enabled if exactly one EClass is selected.

You can see the complete implementation of the drill-down feature here:

 

package org.eclipse.graphiti.examples.tutorial.features;
 
public class TutorialDrillDownEClassFeature extends AbstractDrillDownFeature {
 
    public TutorialDrillDownEClassFeature(IFeatureProvider fp) {
        super(fp);
    }
 
    @Override
    public String getName() {
        return "Open associated diagram";
    }
 
    @Override
    public String getDescription() {
        return "Open the diagram associated with this EClass";
    }
 
    @Override
    public boolean canExecute(ICustomContext context) {
        PictogramElement[] pes = context.getPictogramElements();
        // first check, if one EClass is selected
        if (pes != null && pes.length == 1) {
            Object bo = getBusinessObjectForPictogramElement(pes[0]);
            if (bo instanceof EClass) {
                // then forward to super-implementation, which checks if
                // this EClass is associated with other diagrams

                return super.canExecute(context);
            }
        }
        return false;
    }
 
    @Override
    protected Collection<Diagram> getDiagrams() {
       Collection<Diagram> result = Collections.emptyList();
       Resource resource = getDiagram().eResource();
       URI uri = resource.getURI();
       URI uriTrimmed = uri.trimFragment();
       if (uriTrimmed.isPlatformResource()){
           String platformString = uriTrimmed.toPlatformString(true);
           IResource fileResource = ResourcesPlugin.getWorkspace()
             .getRoot().findMember(platformString);
           if (fileResource != null){
               IProject project = fileResource.getProject();
               result = TutorialUtil.getDiagrams(project);
           }
       }
       return result;
    }
}

 

The complete implementation of class TutorialUtil can be seen here:

 

package org.eclipse.graphiti.examples.tutorial;

public class TutorialUtil {
 
    public static Collection<Diagram> getDiagrams(IProject p) {
       final List<IFile> files = getDiagramFiles(p);
       final List<Diagram> diagramList = new ArrayList<Diagram>();
       final ResourceSet rSet = new ResourceSetImpl();
       for (final IFile file : files) {
            final Diagram diagram = getDiagramFromFile(file, rSet);
            if (diagram != null) {
                diagramList.add(diagram);
            }
       }
       return diagramList;
    }
 
    private static List<IFile> getDiagramFiles(IContainer folder) {
       final List<IFile> ret = new ArrayList<IFile>();
       try {
            final IResource[] members = folder.members();
            for (final IResource resource : members) {
                 if (resource instanceof IContainer) {
                     ret.addAll(getDiagramFiles((IContainer) resource));
                 } else if (resource instanceof IFile) {
                     final IFile file = (IFile) resource;
                     if (file.getName().endsWith(".diagram")) {
                          ret.add(file);
                     }
                 }
            }
       } catch (final CoreException e) {
                e.printStackTrace();
       }
       return ret;
    }
 
    private static Diagram getDiagramFromFile(IFile file,
                                              ResourceSet resourceSet) {
       // Get the URI of the model file.
       final URI resourceURI = getFileURI(file, resourceSet);
       // Demand load the resource for this file.
       Resource resource;
       try {
            resource = resourceSet.getResource(resourceURI, true);
            if (resource != null) {
            // does resource contain a diagram as root object?
            final EList<EObject> contents = resource.getContents();
            for (final EObject object : contents) {
                 if (object instanceof Diagram) {
                     return (Diagram) object;
                 }
             }
       }
       } catch (final WrappedException e) {
                e.printStackTrace();
       }
       return null;
    }
 
    private static URI getFileURI(IFile file, ResourceSet resourceSet) {
       final String pathName = file.getFullPath().toString();
       URI resourceURI = URI.createFileURI(pathName);
       resourceURI = resourceSet.getURIConverter().normalize(resourceURI);
       return resourceURI;
    }
}
 

 

Before we test our drill-down feature, we have link EClasses to our diagrams. In our small example there is no obvious reasoning, which EClass to assign to a diagram. Because of that we create a new custom feature, where the user himself can assign selected EClasses to the current diagram.

You can see the complete implementation of this custom feature here:

 

package org.eclipse.graphiti.examples.tutorial.features;
 
public class TutorialAssociateDiagramEClassFeature extends
    AbstractCustomFeature {
 
    public TutorialAssociateDiagramEClassFeature(IFeatureProvider fp) {
        super(fp);
    }
 
    @Override
    public String getName() {
        return "Associate diagram";
    }
 
    @Override
    public String getDescription() {
        return "Associate the diagram with this EClass";
    }
 
    @Override
    public boolean canExecute(ICustomContext context) {
        boolean ret = false;
        PictogramElement[] pes = context.getPictogramElements();
        if (pes != null && pes.length >= 1) {
            ret = true;
            for (PictogramElement pe : pes) {
                Object bo = getBusinessObjectForPictogramElement(pe);
                if (! (bo instanceof EClass)) {
                    ret = false;
                }               
            }
        }
        return ret;
    }
 
    public void execute(ICustomContext context) {
        PictogramElement[] pes = context.getPictogramElements();
        EClass eClasses[] = new EClass[pes.length];
        for (int i = 0; i < eClasses.length; i++) {
            eClasses[i] =
                (EClass) getBusinessObjectForPictogramElement(pes[i]);
        }
       
        // associate selected EClass with diagram
        link(getDiagram(), eClasses);
    }
 
}

 

Finally the feature provider has to deliver our newly created custom features (overwrite the method getCustomFeatures).

This implementation can be seen here:

 

@Override
public ICustomFeature[] getCustomFeatures(ICustomContext context) {
       return new ICustomFeature[]
            { new TutorialRenameEClassFeature(this),
              new TutorialDrillDownEClassFeature(this),
              new TutorialAssociateDiagramEClassFeature(this)};
}

 

Test: Navigate to Diagram Associated with a EClass

Now start the editor and test this new drill-down feature:

  1. create or open two diagrams
  2. in the first diagram create a new EClass
  3. use "copy & paste" to copy this EClass to the second diagram (the underlying business object is remains the same!)
  4. open the context menu on the EClass in the second diagram; choose "Associate diagram"
  5. save both diagrams and close the second diagram
  6. open the context menu on the EClass in the first diagram; choose "Open associated diagram"
  7. now the second diagram is opened again