| <html> |
| |
| <head> |
| <meta content="text/html; charset=utf-8" http-equiv="Content-Type"> |
| <title>Providing drill-down behavior</title> |
| <link href="../book.css" rel="Stylesheet" type="text/css"> |
| <link href="../code.css" rel="Stylesheet" type="text/css"> |
| </head> |
| |
| <body> |
| |
| <h1>Providing Drill-Down Behavior</h1> |
| <p>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.</p> |
| <p>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.</p> |
| <p>Although going from overview to detail is the most typical example, there are |
| other examples, where the user navigates to more independent diagrams. </p> |
| <p>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). </p> |
| <p>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).</p> |
| <p>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.</p> |
| <h2>Creating a Drill-Down Feature</h2> |
| <p>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.</p> |
| <p>A drill-down feature is a <a href="custom-feature.htm">custom feature</a> which |
| can easily be implemented by extending the base class |
| <a href="../../../javadoc/org/eclipse/graphiti/ui/features/AbstractDrillDownFeature.html"> |
| AbstractDrillDownFeature</a>.</p> |
| <p>The functionality of the <em>AbstractDrillDownFeature</em> is not tool-independent, |
| so the method <i>getDiagrams()</i> can be implemented only by the tool efficiently. |
| Furthermore we just overwrite the <em>canExecute()</em> method, so that the feature |
| is only enabled if exactly one EClass is selected.</p> |
| <p>You can see the complete implementation of the drill-down feature here:</p> |
| <!-- Begin code ------------------------------------------------------------------------------- --> |
| <p> </p> |
| <div class="literallayout"> |
| <div class="incode"> |
| <p class="code"><span class="keyword">package </span>org.eclipse.graphiti.examples.tutorial.features;<br> <br> |
| <span class="keyword">public class</span> TutorialDrillDownEClassFeature |
| <span class="keyword">extends </span>AbstractDrillDownFeature {<br> <br> |
| <span class="keyword">public </span>TutorialDrillDownEClassFeature(IFeatureProvider |
| fp) {<br> |
| <span class="keyword">super</span>(fp);<br> }<br> <br> |
| @Override<br> <span class="keyword">public </span>String |
| getName() {<br> |
| <span class="keyword">return </span><span class="string">"Open associated |
| diagram"</span>;<br> }<br> <br> |
| @Override<br> <span class="keyword">public </span>String |
| getDescription() {<br> |
| <span class="keyword">return </span><span class="string">"Open the diagram |
| associated with this EClass"</span>;<br> }<br> <br> |
| @Override<br> <span class="keyword">public boolean</span> |
| canExecute(ICustomContext context) {<br> |
| PictogramElement[] pes = context.getPictogramElements();<br> |
| <span class="comment">// first check, if one EClass is selected</span><br> |
| <span class="keyword">if</span> (pes != <span class="keyword">null |
| </span>&& pes.<span class="string"><em>length</em></span> == 1) {<br> |
| Object bo = getBusinessObjectForPictogramElement(pes[0]);<br> |
| <span class="keyword">if</span> (bo <span class="keyword">instanceof</span> |
| EClass) {<br> |
| <span class="comment">// then forward to super-implementation, which checks |
| if<br> |
| // this EClass is associated with other diagrams</span><br> |
| <span class="keyword">return super</span>.canExecute(context);<br> |
| }<br> }<br> |
| <span class="keyword">return false</span>;<br> }<br> <br> |
| @Override<br> <span class="keyword">protected </span>Collection<Diagram> |
| getDiagrams() {<br> Collection<Diagram> |
| result = Collections.emptyList();<br> |
| Resource resource = getDiagram().eResource();<br> |
| URI uri = resource.getURI();<br> URI |
| uriTrimmed = uri.trimFragment();<br> |
| <span class="keyword">if</span> (uriTrimmed.isPlatformResource()){<br> |
| String platformString = uriTrimmed.toPlatformString(<span class="keyword">true</span>);<br> |
| IResource fileResource = ResourcesPlugin.getWorkspace()<br> |
| .getRoot().findMember(platformString);<br> |
| <span class="keyword">if</span> (fileResource != <span class="keyword">null</span>){<br> |
| IProject project = fileResource.getProject();<br> |
| result = TutorialUtil.getDiagrams(project);<br> |
| }<br> }<br> |
| <span class="keyword">return</span> result;<br> }<br>}<br> |
| </p> |
| </div> |
| </div> |
| <p> </p> |
| <!-- End code ------------------------------------------------------------------------------- --> |
| <p>The complete implementation of class <em>TutorialUtil</em> can be seen here:</p> |
| <!-- Begin code ------------------------------------------------------------------------------- --> |
| <p> </p> |
| <div class="literallayout"> |
| <div class="incode"> |
| <p class="code"><span class="keyword">package </span>org.eclipse.graphiti.examples.tutorial;<br> |
| <br><span class="keyword">public class</span> TutorialUtil {<br> <br> |
| <span class="keyword">public static</span> Collection<Diagram> getDiagrams(IProject |
| p) {<br> <span class="keyword">final</span> |
| List<IFile> files = getDiagramFiles(p);<br> |
| <span class="keyword">final</span> List<Diagram> diagramList = |
| <span class="keyword">new </span>ArrayListDiagram();<br> |
| <span class="keyword">final</span> ResourceSet rSet = |
| <span class="keyword">new</span> ResourceSetImpl();<br> |
| <span class="keyword">for</span> (<span class="keyword">final</span> IFile |
| file : files) {<br> |
| <span class="keyword">final</span> Diagram diagram = getDiagramFromFile(file, |
| rSet);<br> |
| if (diagram != <span class="keyword">null</span>) {<br> |
| diagramList.add(diagram);<br> |
| }<br> }<br> |
| <span class="keyword">return</span> diagramList;<br> }<br> <br> |
| <span class="keyword">private static</span> List<IFile> getDiagramFiles(IContainer |
| folder) {<br> <span class="keyword"> |
| final</span> List<IFile> ret = <span class="keyword">new</span> ArrayList<IFile>();<br> |
| <span class="keyword">try</span> {<br> |
| final IResource[] members = folder.members();<br> |
| <span class="keyword">for</span> (<span class="keyword">final</span> IResource |
| resource : members) {<br> |
| <span class="keyword">if</span> (resource <span class="keyword">instanceof</span> |
| IContainer) {<br> |
| ret.addAll(getDiagramFiles((IContainer) resource));<br> |
| } <span class="keyword">else if</span> (resource <span class="keyword">instanceof</span> |
| IFile) {<br> |
| <span class="keyword">final</span> IFile file = (IFile) resource;<br> |
| <span class="keyword">if</span> (file.getName().endsWith(<span class="string">".diagram"</span>)) |
| {<br> |
| ret.add(file);<br> |
| }<br> |
| }<br> |
| }<br> } <span class="keyword">catch</span> |
| (<span class="keyword">final</span> CoreException e) {<br> |
| e.printStackTrace();<br> }<br> |
| <span class="keyword"> return</span> ret;<br> }<br> <br> |
| <span class="keyword"> private static</span> Diagram getDiagramFromFile(IFile |
| file, <br> |
| ResourceSet resourceSet) {<br> |
| <span class="comment">// Get the URI of the model file.</span><br> |
| <span class="keyword">final</span> URI resourceURI = getFileURI(file, resourceSet);<br> |
| <span class="comment">// Demand load the resource for this file.</span><br> |
| Resource resource;<br> |
| <span class="keyword">try</span> {<br> |
| resource = resourceSet.getResource(resourceURI, <span class="keyword">true</span>);<br> |
| <span class="keyword">if</span> (resource != <span class="keyword">null</span>) |
| {<br> |
| <span class="comment"> // does resource contain a diagram as root object?</span><br> |
| <span class="keyword">final</span> EList<EObject> contents = resource.getContents();<br> |
| <span class="keyword">for</span> (<span class="keyword">final</span> EObject |
| object : contents) {<br> |
| <span class="keyword">if</span> (object <span class="keyword">instanceof</span> |
| Diagram) {<br> |
| <span class="keyword">return</span> (Diagram) object;<br> |
| }<br> |
| }<br> }<br> |
| } <span class="keyword">catch</span> (<span class="keyword">final</span> |
| WrappedException e) {<br> |
| e.printStackTrace();<br> }<br> |
| <span class="keyword">return null</span>;<br> }<br> <br> |
| <span class="keyword">private static</span> URI getFileURI(IFile file, ResourceSet |
| resourceSet) {<br> <span class="keyword"> final</span> |
| String pathName = file.getFullPath().toString();<br> |
| URI resourceURI = URI.createFileURI(pathName);<br> |
| resourceURI = resourceSet.getURIConverter().normalize(resourceURI);<br> |
| <span class="keyword">return</span> resourceURI;<br> }<br> |
| }<br> </p> |
| </div> |
| </div> |
| <p> </p> |
| <!-- End code ------------------------------------------------------------------------------- --> |
| <p>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. </p> |
| <p>You can see the complete implementation of this custom feature here:</p> |
| <!-- Begin code ------------------------------------------------------------------------------- --> |
| <p> </p> |
| <div class="literallayout"> |
| <div class="incode"> |
| <p class="code"><span class="keyword">package</span> org.eclipse.graphiti.examples.tutorial.features;<br> <br> |
| <span class="keyword">public class</span> TutorialAssociateDiagramEClassFeature |
| <span class="keyword">extends</span><br> AbstractCustomFeature |
| {<br> <br> <span class="keyword">public </span>TutorialAssociateDiagramEClassFeature(IFeatureProvider |
| fp) {<br> |
| <span class="keyword">super</span>(fp);<br> }<br> <br> |
| @Override<br> <span class="keyword">public </span>String |
| getName() {<br> |
| <span class="keyword">return </span><span class="string">"Associate diagram"</span>;<br> |
| }<br> <br> @Override<br> |
| <span class="keyword">public </span>String getDescription() {<br> |
| <span class="keyword">return </span><span class="string">"Associate the |
| diagram with this EClass"</span>;<br> }<br> <br> |
| @Override<br> <span class="keyword">public boolean</span> |
| canExecute(ICustomContext context) {<br> |
| <span class="keyword">boolean</span> ret = <span class="keyword">false</span>;<br> |
| PictogramElement[] pes = context.getPictogramElements();<br> |
| <span class="keyword">if</span> (pes != <span class="keyword">null |
| </span>&& pes.<span class="string"><em>length</em> </span>>= 1) {<br> |
| ret = <span class="keyword">true</span>;<br> |
| <span class="keyword">for</span> (PictogramElement pe : pes) {<br> |
| Object bo = getBusinessObjectForPictogramElement(pe);<br> |
| <span class="keyword">if</span> (! (bo <span class="keyword">instanceof</span> |
| EClass)) {<br> |
| ret = <span class="keyword">false</span>;<br> |
| } |
| <br> }<br> |
| }<br> <span class="keyword">return</span> |
| ret;<br> }<br> <br> |
| <span class="keyword"> public</span> <span class="keyword">void</span> |
| execute(ICustomContext context) {<br> |
| PictogramElement[] pes = context.getPictogramElements();<br> |
| EClass eClasses[] = <span class="keyword">new</span> EClass[pes.<span class="string"><em>length</em></span>];<br> |
| <span class="keyword"> for</span> (int i = 0; i < eClasses.<span class="string"><em>length</em></span>; |
| i++) {<br> |
| eClasses[i] =<br> |
| (EClass) getBusinessObjectForPictogramElement(pes[i]);<br> |
| }<br> <br> |
| <span class="comment">// associate selected EClass with diagram</span><br> |
| link(getDiagram(), eClasses);<br> }<br> <br>}<br> |
| </p> |
| </div> |
| </div> |
| <p> </p> |
| <!-- End code ------------------------------------------------------------------------------- --> |
| <p>Finally the feature provider has to deliver our newly created custom features |
| (overwrite the method |
| <a href="../../../javadoc/org/eclipse/graphiti/features/IFeatureProvider.html#getCustomFeatures(org.eclipse.graphiti.features.context.ICustomContext)"> |
| getCustomFeatures</a>). </p> |
| <p>This implementation can be seen here:</p> |
| <!-- Begin code ------------------------------------------------------------------------------- --> |
| <p> </p> |
| <div class="literallayout"> |
| <div class="incode"> |
| <p class="code">@Override<br><span class="keyword">public </span>ICustomFeature[] |
| getCustomFeatures(ICustomContext context) {<br> |
| <span class="keyword">return new</span> ICustomFeature[] <br> |
| { <span class="keyword">new</span> TutorialRenameEClassFeature(this),<br> |
| <span class="keyword">new</span> TutorialDrillDownEClassFeature(this),<br> |
| <span class="keyword">new</span> TutorialAssociateDiagramEClassFeature(this)};<br> |
| }<br></p> |
| </div> |
| </div> |
| <p> </p> |
| <!-- End code ------------------------------------------------------------------------------- --> |
| <h2>Test: Navigate to Diagram Associated with a EClass</h2> |
| <p>Now start the editor and test this new drill-down feature:</p> |
| <ol> |
| <li>create or open two diagrams</li> |
| <li>in the first diagram create a new EClass</li> |
| <li>use "copy & paste" to copy this EClass to the second diagram (the underlying |
| business object is remains the same!)</li> |
| <li>open the context menu on the EClass in the second diagram; choose "Associate |
| diagram"</li> |
| <li>save both diagrams and close the second diagram</li> |
| <li>open the context menu on the EClass in the first diagram; choose "Open associated |
| diagram"</li> |
| <li>now the second diagram is opened again</li> |
| </ol> |
| |
| </body> |
| |
| </html> |