Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "VIATRA/Addon/VIATRA Viewers"

< VIATRA‎ | Addon
(Specification)
 
(17 intermediate revisions by 5 users not shown)
Line 1: Line 1:
= Using IncQuery Viewers =
+
{{caution|Old information|This page is not updated anymore; for more up-to-date details look at the language specification at https://www.eclipse.org/viatra/documentation/addons.html instead.}}
 +
= Using VIATRA Viewers =
  
The goal of the IncQuery Viewers component is to help developing model-driven user interfaces by filling and updating model viewer results with the results of model queries. The implementation relies on (and is modeled after) the [[JFace_Data_Binding|JFace Data binding]] and [[FAQ_What_kinds_of_viewers_does_JFace_provide?|JFace Viewers libraries]].
+
The goal of the VIATRA Viewers component is to help developing model-driven user interfaces by filling and updating model viewer results with the results of model queries. The implementation relies on (and is modeled after) the [[VIATRA/Transformation/DeveloperDocumentation/EventDrivenVM|Event-driven Virtual Machine]] and [[FAQ_What_kinds_of_viewers_does_JFace_provide?|JFace Viewers libraries]].
  
The IncQuery Viewers component can bind the results of queries to various JFace Viewers: JFace ListViewer and TreeViewers are currently supported, while TableViewer support is planned (see [[https://bugs.eclipse.org/bugs/show_bug.cgi?id=403325|bug 403325]] for current state). Additionally, by installing extra features for the extra update site Zest GraphViewers (based on [[GEF/GEF4|GEF4 Zest]]) are also supported.
+
The VIATRA Viewers component can bind the results of queries to various JFace Viewers: JFace ListViewer and TreeViewers are currently supported, while TableViewer support is planned (see [[https://bugs.eclipse.org/bugs/show_bug.cgi?id=403325 bug 403325]] for current state). Additionally, by installing extra features from the extra update site GraphViewers (based on [[GEF/GEF4|GEF4 Zest]]) are also supported.
  
 
To select which patterns are used to determine the model elements to bind, several annotations are used by the framework. These annotations also allow to add labels and formatting to the viewers.
 
To select which patterns are used to determine the model elements to bind, several annotations are used by the framework. These annotations also allow to add labels and formatting to the viewers.
  
 
== Using Viewer Annotations for Specifying the View Model ==
 
== Using Viewer Annotations for Specifying the View Model ==
The IncQuery Viewers component uses four different annotations to bind query results to various viewers. As the different viewers have vastly different capabilities, all annotations are designed to be ignored if an unsupported annotation (or annotation parameter) is detected. Multiple patterns can have the same annotation type, in this case the Viewers component adds all the corresponding results to the viewer.
+
The VIATRA Viewers component uses four different annotations to bind query results to various viewers. As the different viewers have vastly different capabilities, all annotations are designed to be ignored if an unsupported annotation (or annotation parameter) is detected. Multiple patterns can have the same annotation type, in this case the Viewers component adds all the corresponding results to the viewer.
  
 
# The <code>Item</code> annotation is used on patterns whose results will be the main elements to display in the viewers. ''E.g., in case of JFace Viewers the ContentProvider should return all the items.''
 
# The <code>Item</code> annotation is used on patterns whose results will be the main elements to display in the viewers. ''E.g., in case of JFace Viewers the ContentProvider should return all the items.''
Line 19: Line 20:
 
# Open the Query Explorer and Viewers Sandbox views.
 
# Open the Query Explorer and Viewers Sandbox views.
 
# Add your instance model and query definitions to the Query Explorer.
 
# Add your instance model and query definitions to the Query Explorer.
# Select the ''Initialize IncQuery Viewers'' from a query definition in the popup menu over any selected element in the main area.
+
# Select the ''Initialize VIATRA Viewers'' from a query definition in the popup menu over any selected element in the main area.
#* '''Warning''': the IncQuery Sandbox does not update itself in case of pattern changes (as opposed to the Query Explorer).
+
#* '''Warning''': the VIATRA Sandbox does not update itself in case of pattern changes (as opposed to the Query Explorer).
 
#* The ''Initialize'' command uses the actual filter settings, but the Sandbox does not update itself if the filters are updated.  
 
#* The ''Initialize'' command uses the actual filter settings, but the Sandbox does not update itself if the filters are updated.  
 
=== Programmatic Binding ===
 
=== Programmatic Binding ===
# Add a dependency to <code>org.eclipse.incquery.viewers.runtime</code> (and <code>org.eclipse.incquery.viewers.runtime.zest</code> if necessary) to your plug-in.
+
# Add a dependency to <code>org.eclipse.viatra.addon.viewers.runtime</code> (and <code>org.eclipse.viatra.addon.viewers.runtime.zest</code> if necessary) to your plug-in.
# Create a <code>ViewerModel</code> from a group of patterns.
+
# Create a <code>ViewerState</code> instance from a group of query specification. Useful (static) helper methods are  available in the <code>ViatraViewerDataModel</code> class, that require a collection of query specifications; a data filter and the required features.
# Use the corresponding bind methods from <code>IncQueryViewers</code> or <code>IncQueryGraphViewers</code> classes on a manually created <code>Viewer</code> together with the <code>ViewerModel</code> and your instance model.
+
#* Collection of query specifications: only the query specifications with the corresponding Viewers annotations will be used; other specifications will be ignored.
#* The ViewerModel instance can be re-used between different bindings.
+
#* ViewerDataFilter: it is used to filter the result of the queries by its parameters. For each pattern a separate filter can be initialized, that binds some of its parameters.
#* If the filters added to the ViewerModel are changed, the bindings will become obsolete, and have to be redone.
+
#* Required features: The ViewerStateFeature enum can be used to list the features required by the visualization. List viewers need no specific features; tree viewers require containment relations; Zest viewer requires edge relations (and possibly containment relations). Features not required will not create
 +
# Use the corresponding bind methods from <code>ViatraViewers</code> or <code>ViatraGraphViewers</code> classes on a manually created <code>Viewer</code> together with the <code>ViewerState</code> and your instance model.
 +
#* The ViewerState instance can be re-used between different viewers.
 +
#* If the filters added to the ViewerState are changed, all the viewers will become obsolete, and have to be redone.
 +
 
 +
Example
 +
<source lang="java">
 +
 
 +
state = ViatraViewerDataModel.newViewerState( //Initializing state
 +
  engine, //on a selected ViatraQueryEngine
 +
  engine.getRegisteredQuerySpecifications(), //with all query specifications loaded to the selected engine
 +
  ViewerDataFilter.UNFILTERED, //using no filters
 +
  ImmutableSet.of(ViewerStateFeature.EDGE, ViewerStateFeature.CONTAINMENT)); //but requiring all features
 +
ViatraGraphViewers.bind(graphViewer, state); //Binding viewer state to Zest graph viewer
 +
</source>
  
 
= Examples =
 
= Examples =
  
 
== UML visualization ==
 
== UML visualization ==
To illustrate the approach, a simple example was prepared in the [[http://git.eclipse.org/c/incquery/org.eclipse.incquery.examples.git/tree/papyrus-uml|examples repository of the EMF-IncQuery project]] based on UML class diagrams. The examples are relying on the notion of example classes: ''UML classes that do not have operations or properties (neither in their parent classes)''.
+
To illustrate the approach, a simple example was prepared in the [[http://git.eclipse.org/c/viatra/org.eclipse.viatra.git/tree/examples/papyrus-uml examples repository of the VIATRA project]] based on UML class diagrams. The examples are relying on the notion of example classes: ''UML classes that do not have operations or properties (neither in their parent classes)''.
  
 
To present most features of the framework, four specific patterns are used (for the entire implementations visit the git repository):
 
To present most features of the framework, four specific patterns are used (for the entire implementations visit the git repository):
Line 39: Line 54:
 
# <code>pattern superClass(sub : Class, sup : Class)</code> - for listing all direct superclass relations between classes
 
# <code>pattern superClass(sub : Class, sup : Class)</code> - for listing all direct superclass relations between classes
 
# <code>pattern transitiveSuperClass(sub : Class, sup : Class)</code> - for listing all indirect superclass relations between classes (but not the direct ones)
 
# <code>pattern transitiveSuperClass(sub : Class, sup : Class)</code> - for listing all indirect superclass relations between classes (but not the direct ones)
 +
 +
The visualizer illustrations below correspond to the [http://git.eclipse.org/c/viatra/org.eclipse.viatra.git/tree/examples/papyrus-uml/org.eclipse.viatra.examples.uml.queries/testmodels/Testmodel.uml Test model] in the repository.
  
 
=== JFace List Viewer example ===
 
=== JFace List Viewer example ===
 +
List Viewers can be used to display a collection of elements as a list. To define the list, only the <code>Item</code> annotation is used. Its <code>item</code> parameter selects a pattern parameter, representing the data to display, while its <code>label</code> parameter is used to define a label string (with the same syntax as the label features of [[VIATRA/Addons/UserDocumentation/Databinding|Data Binding]] or the labels of [[VIATRA/Addons/UserDocumentation/Validation|Validation Framework]].
  
 
<source lang="java">
 
<source lang="java">
Line 53: Line 71:
 
}
 
}
 
</source>
 
</source>
 +
[[Image:Incquery_Viewers_Demo_UML_List.png]]
 
=== Binding contents to a JFace Tree Viewer ===
 
=== Binding contents to a JFace Tree Viewer ===
 +
To support binding elements to a JFace Tree Viewer, in addition to the list of items, a set of containment relations needs also be specified using the <code>ContainsItem</code> annotation, that describes the container and contained items as pattern parameters. A containment reference is only displayed if both ends of the match result are present as Items in the viewer - otherwise it is simply ignored.
 +
 +
A unique property of the Tree Viewer support is that a single Item of the Viewers framework may appear multiple times in the tree. This happens if an element has multiple parents.
 +
 
<source lang="java">
 
<source lang="java">
 
@ContainsItem(container = sup, item = sub)
 
@ContainsItem(container = sup, item = sub)
Line 59: Line 82:
 
...
 
...
 
}
 
}
 
 
@Item(item = cl, label="Empty class $cl$")
 
@Item(item = cl, label="Empty class $cl$")
 
pattern emptyClass(cl : Class) {
 
pattern emptyClass(cl : Class) {
 
...
 
...
 
}
 
}
 
 
@Item(item = cl, label = "Class $cl$")
 
@Item(item = cl, label = "Class $cl$")
 
pattern nonEmptyClass(cl : Class) {
 
pattern nonEmptyClass(cl : Class) {
Line 70: Line 91:
 
}
 
}
 
</source>
 
</source>
 +
[[Image:Incquery_Viewers_Demo_UML_Tree.png]]
 +
 +
Additional notes
 +
 +
* In addition to basic binding support where all items appear both as root and child items as needed, it is possible to limit Items to appear only as root or only as child positions using the hierarchy parameter.
 +
* If creating a TreeViewer binding programmatically, and an item appear in multiple places, make sure you set up the <code>useHashLookup</code> property of your TreeViewer, otherwise the update of the TreeViewer would fail.
 +
 
=== Zest Graph Viewer example===
 
=== Zest Graph Viewer example===
 +
The definition of graph viewers requires a set of Items and a set of Edges. Edges are connecting two different Items; edges where the end points are not Items are not displayed. As opposed to the Tree Viewer support, a single item only appears once.
 +
 +
Additionally, based upon the formatting capabilities of Zest some basic display options are available in the form of <code>Format</code> annotation. The various parameters can be used to depict colors, line width, etc. If a selected format is not applicable for the current element, it is simply ignored.
 +
 
<source lang="java">
 
<source lang="java">
 
@Edge(source = sup, target = sub, label = "direct")
 
@Edge(source = sup, target = sub, label = "direct")
Line 77: Line 109:
 
...
 
...
 
}
 
}
 
 
@Edge(source = sup, target = sub)
 
@Edge(source = sup, target = sub)
 
pattern transitiveSuperClass(sub : Class, sup : Class) {
 
pattern transitiveSuperClass(sub : Class, sup : Class) {
 
...
 
...
 
}
 
}
 
 
@Item(item = cl, label="Empty class $cl$")
 
@Item(item = cl, label="Empty class $cl$")
 
@Format(color="#3770d7", textColor = "#ffffff")
 
@Format(color="#3770d7", textColor = "#ffffff")
Line 88: Line 118:
 
...
 
...
 
}
 
}
 
 
@Item(item = cl, label = "Class $cl$")
 
@Item(item = cl, label = "Class $cl$")
 
pattern nonEmptyClass(cl : Class) {
 
pattern nonEmptyClass(cl : Class) {
Line 94: Line 123:
 
}
 
}
 
</source>
 
</source>
 
+
[[Image:Incquery_Viewers_Demo_UML_Zest.png]]
  
 
== Ecore visualization ==
 
== Ecore visualization ==
 +
We have developed another demonstrating example in the context of the [[VIATRA/Query/UserDocumentation/HeadlessExecution|Headless Execution]] example. As the queries of the Headless Example match against .ecore models (that is, EMF metamodels), the visualization example below can be used to visualize metamodels and containment relationships in a simple way. This example is focused mainly on the 2D graph visualization supported by the GEF4 Zest framework.
  
 
=== Specification ===
 
=== Specification ===
 +
The example defines two graph node types and three graph edge types ([http://git.eclipse.org/c/viatra/org.eclipse.viatra.git/tree/examples/headless/org.eclipse.viatra.query.application.queries/src/org/eclipse/viatra/query/application/queries/headlessQueries.vql code]):
 +
* Nodes are EPackage and EClass instances
 +
* Edges are '''classes contained in a package''', '''subpackage''' relationships, and a special transitive containment relationship between packages and classes '''classes in a package hierarchy''' that enumerates all classes that are contained by a root package or some subpackage (transitively) below this root.
 +
 
<source lang="java">
 
<source lang="java">
 +
@Item(item = p, label = "P: $p.name$")
 +
@Format(color = "#791662", textColor = "#ffffff")
 +
pattern ePackage(p : EPackage) { EPackage(p); }
 +
 +
@Item(item = ec, label = "EC: $ec.name$")
 +
@Format(color = "#e8da2c")
 +
pattern eClass(ec : EClass) { EClass(ec); }
 +
 
@Edge(source = p, target = ec, label = "classIn")
 
@Edge(source = p, target = ec, label = "classIn")
 
pattern classesInPackage(p : EPackage, ec: EClass) { EPackage.eClassifiers(p,ec); }
 
pattern classesInPackage(p : EPackage, ec: EClass) { EPackage.eClassifiers(p,ec); }
Line 115: Line 157:
 
find classesInPackage(somePackage,containedClass);
 
find classesInPackage(somePackage,containedClass);
 
}
 
}
 +
</source>
  
@Item(item = p, label = "P: $p.name$")
+
=== Visualization ===
@Format(color = "#791662", textColor = "#ffffff")
+
The visualization of a simple test model ([http://git.eclipse.org/c/viatra/org.eclipse.viatra.git/tree/examples/headless/org.eclipse.viatra.query.application.queries/testmodels/Test.ecore file]) is illustrated in the figure.
pattern ePackage(p : EPackage) { EPackage(p); }
+
  
@Item(item = ec, label = "EC: $ec.name$")
+
[[Image:Headless_viewers.png|right|700px|Ecore metamodel visualization with VIATRA Viewers]]
@Format(color = "#e8da2c")
+
pattern eClass(ec : EClass) { EClass(ec); }
+
</source>
+
  
TODO
+
Observe the following details:
[[Image:EMFIncQuery/Demo/headless_viewers.png]]
+
* EPackages and EClasses are shown in purple and yellow, respectively
 +
* direct relationships (subpackage and contained packages) are shown in grey
 +
* inferred transitive containment relationships are shown in blue

Latest revision as of 09:36, 30 November 2017

Stop.png
Old information
This page is not updated anymore; for more up-to-date details look at the language specification at https://www.eclipse.org/viatra/documentation/addons.html instead.

Using VIATRA Viewers

The goal of the VIATRA Viewers component is to help developing model-driven user interfaces by filling and updating model viewer results with the results of model queries. The implementation relies on (and is modeled after) the Event-driven Virtual Machine and JFace Viewers libraries.

The VIATRA Viewers component can bind the results of queries to various JFace Viewers: JFace ListViewer and TreeViewers are currently supported, while TableViewer support is planned (see [bug 403325] for current state). Additionally, by installing extra features from the extra update site GraphViewers (based on GEF4 Zest) are also supported.

To select which patterns are used to determine the model elements to bind, several annotations are used by the framework. These annotations also allow to add labels and formatting to the viewers.

Using Viewer Annotations for Specifying the View Model

The VIATRA Viewers component uses four different annotations to bind query results to various viewers. As the different viewers have vastly different capabilities, all annotations are designed to be ignored if an unsupported annotation (or annotation parameter) is detected. Multiple patterns can have the same annotation type, in this case the Viewers component adds all the corresponding results to the viewer.

  1. The Item annotation is used on patterns whose results will be the main elements to display in the viewers. E.g., in case of JFace Viewers the ContentProvider should return all the items.
  2. The ContainsItem annotation is used on patterns whose results describe the the containment references between Items. E.g., in case of JFace TreeViewers the ContentProvider has to return these as child references.
  3. The Edge annotation is used on patterns whose results describe a generic edge reference between Items. E.g., in case of Zest GraphViewers the ContentProvider will return these results as graph edges.
  4. The Format annotation is used to define additional formatting. It is expected to be added next to Item and/or Edge annotations.

Binding Results to Viewers

Viewers Sandbox

  1. Open the Query Explorer and Viewers Sandbox views.
  2. Add your instance model and query definitions to the Query Explorer.
  3. Select the Initialize VIATRA Viewers from a query definition in the popup menu over any selected element in the main area.
    • Warning: the VIATRA Sandbox does not update itself in case of pattern changes (as opposed to the Query Explorer).
    • The Initialize command uses the actual filter settings, but the Sandbox does not update itself if the filters are updated.

Programmatic Binding

  1. Add a dependency to org.eclipse.viatra.addon.viewers.runtime (and org.eclipse.viatra.addon.viewers.runtime.zest if necessary) to your plug-in.
  2. Create a ViewerState instance from a group of query specification. Useful (static) helper methods are available in the ViatraViewerDataModel class, that require a collection of query specifications; a data filter and the required features.
    • Collection of query specifications: only the query specifications with the corresponding Viewers annotations will be used; other specifications will be ignored.
    • ViewerDataFilter: it is used to filter the result of the queries by its parameters. For each pattern a separate filter can be initialized, that binds some of its parameters.
    • Required features: The ViewerStateFeature enum can be used to list the features required by the visualization. List viewers need no specific features; tree viewers require containment relations; Zest viewer requires edge relations (and possibly containment relations). Features not required will not create
  3. Use the corresponding bind methods from ViatraViewers or ViatraGraphViewers classes on a manually created Viewer together with the ViewerState and your instance model.
    • The ViewerState instance can be re-used between different viewers.
    • If the filters added to the ViewerState are changed, all the viewers will become obsolete, and have to be redone.

Example

state = ViatraViewerDataModel.newViewerState( //Initializing state
  engine, //on a selected ViatraQueryEngine
  engine.getRegisteredQuerySpecifications(), //with all query specifications loaded to the selected engine
  ViewerDataFilter.UNFILTERED, //using no filters
  ImmutableSet.of(ViewerStateFeature.EDGE, ViewerStateFeature.CONTAINMENT)); //but requiring all features
ViatraGraphViewers.bind(graphViewer, state); //Binding viewer state to Zest graph viewer

Examples

UML visualization

To illustrate the approach, a simple example was prepared in the [examples repository of the VIATRA project] based on UML class diagrams. The examples are relying on the notion of example classes: UML classes that do not have operations or properties (neither in their parent classes).

To present most features of the framework, four specific patterns are used (for the entire implementations visit the git repository):

  1. pattern emptyClass (cl : Class) - for listing all empty classes in the model
  2. pattern nonEmptyClass(cl : Class) - for listing all classes in the model, that are not empty
  3. pattern superClass(sub : Class, sup : Class) - for listing all direct superclass relations between classes
  4. pattern transitiveSuperClass(sub : Class, sup : Class) - for listing all indirect superclass relations between classes (but not the direct ones)

The visualizer illustrations below correspond to the Test model in the repository.

JFace List Viewer example

List Viewers can be used to display a collection of elements as a list. To define the list, only the Item annotation is used. Its item parameter selects a pattern parameter, representing the data to display, while its label parameter is used to define a label string (with the same syntax as the label features of Data Binding or the labels of Validation Framework.

@Item(item = cl, label="Empty class $cl$")
pattern emptyClass(cl : Class) {
...
}
 
@Item(item = cl, label = "Class $cl$")
pattern nonEmptyClass(cl : Class) {
...
}

Incquery Viewers Demo UML List.png

Binding contents to a JFace Tree Viewer

To support binding elements to a JFace Tree Viewer, in addition to the list of items, a set of containment relations needs also be specified using the ContainsItem annotation, that describes the container and contained items as pattern parameters. A containment reference is only displayed if both ends of the match result are present as Items in the viewer - otherwise it is simply ignored.

A unique property of the Tree Viewer support is that a single Item of the Viewers framework may appear multiple times in the tree. This happens if an element has multiple parents.

@ContainsItem(container = sup, item = sub)
pattern superClass(sub : Class, sup : Class) {
...
}
@Item(item = cl, label="Empty class $cl$")
pattern emptyClass(cl : Class) {
...
}
@Item(item = cl, label = "Class $cl$")
pattern nonEmptyClass(cl : Class) {
...
}

Incquery Viewers Demo UML Tree.png

Additional notes

  • In addition to basic binding support where all items appear both as root and child items as needed, it is possible to limit Items to appear only as root or only as child positions using the hierarchy parameter.
  • If creating a TreeViewer binding programmatically, and an item appear in multiple places, make sure you set up the useHashLookup property of your TreeViewer, otherwise the update of the TreeViewer would fail.

Zest Graph Viewer example

The definition of graph viewers requires a set of Items and a set of Edges. Edges are connecting two different Items; edges where the end points are not Items are not displayed. As opposed to the Tree Viewer support, a single item only appears once.

Additionally, based upon the formatting capabilities of Zest some basic display options are available in the form of Format annotation. The various parameters can be used to depict colors, line width, etc. If a selected format is not applicable for the current element, it is simply ignored.

@Edge(source = sup, target = sub, label = "direct")
@Format(color = "#7f004b", lineWidth = 2)
pattern superClass(sub : Class, sup : Class) {
...
}
@Edge(source = sup, target = sub)
pattern transitiveSuperClass(sub : Class, sup : Class) {
...
}
@Item(item = cl, label="Empty class $cl$")
@Format(color="#3770d7", textColor = "#ffffff")
pattern emptyClass(cl : Class) {
...
}
@Item(item = cl, label = "Class $cl$")
pattern nonEmptyClass(cl : Class) {
...
}

Incquery Viewers Demo UML Zest.png

Ecore visualization

We have developed another demonstrating example in the context of the Headless Execution example. As the queries of the Headless Example match against .ecore models (that is, EMF metamodels), the visualization example below can be used to visualize metamodels and containment relationships in a simple way. This example is focused mainly on the 2D graph visualization supported by the GEF4 Zest framework.

Specification

The example defines two graph node types and three graph edge types (code):

  • Nodes are EPackage and EClass instances
  • Edges are classes contained in a package, subpackage relationships, and a special transitive containment relationship between packages and classes classes in a package hierarchy that enumerates all classes that are contained by a root package or some subpackage (transitively) below this root.
@Item(item = p, label = "P: $p.name$")
@Format(color = "#791662", textColor = "#ffffff")
pattern ePackage(p : EPackage) { EPackage(p); }
 
@Item(item = ec, label = "EC: $ec.name$")
@Format(color = "#e8da2c")
pattern eClass(ec : EClass) { EClass(ec); }
 
@Edge(source = p, target = ec, label = "classIn")
pattern classesInPackage(p : EPackage, ec: EClass) { EPackage.eClassifiers(p,ec); }
 
@Edge(source = p, target = sp, label = "sub")
pattern subPackage(p: EPackage, sp: EPackage){ EPackage.eSubpackages(p,sp); }
 
@Edge(source = rootP, target = containedClass, label = "classIn+")
@Format(color = "#0033ff")
pattern classesInPackageHierarchy(rootP: EPackage, containedClass: EClass)
{
	find classesInPackage(rootP,containedClass);
} or {
	find subPackage+(rootP,somePackage);
	find classesInPackage(somePackage,containedClass);
}

Visualization

The visualization of a simple test model (file) is illustrated in the figure.

Ecore metamodel visualization with VIATRA Viewers

Observe the following details:

  • EPackages and EClasses are shown in purple and yellow, respectively
  • direct relationships (subpackage and contained packages) are shown in grey
  • inferred transitive containment relationships are shown in blue

Back to the top