Transfer of additional information between server and client
Tips & TricksSummary
Description
It is often necessary to transfer data which is not part of the default implementation between server and client, additional information to influence the layout or the visual representation of the graph and its elements, for instance.
The necessary steps differ slightly whether you want to add simple data types (like String, int, long, float, double or bool) or complex objects.
Server (yFiles Java)
Since yFiles FLEX 1.5 the integrated GraphML support of yFiles for Java 2.7 is used instead of the GraphML extension package. As some of the relevant method signatures changed both versions will be explained and marked either as 'since yFiles FLEX 1.5' for the usage of the integrated GraphML or as 'until yFiles FLEX 1.4' for the usage of the GraphML extension package.Transferring simple attributes
On the server side, a simple attribute has to be registered for reading and writing with thecom.yworks.yfiles.server.graphml.support.GraphRoundtripSupport
, using: since yFiles FLEX 1.5
GraphRoundtripSupport.addMapper(Object tag, String name, KeyType valueType, KeyScope scope)
;With the parameters:
tag
: the provider key to get the DataMap from the graph using Graph.getDataProvider(Object providerKey)name
: the name for the data used in the GraphML filevalueType
: a KeyType describing the type of the datascope
: a KeyScope describing the scope (e.g. node, edge)
until yFiles FLEX 1.4
GraphRoundtripSupport.addMapper(Object tag, String name, int graphMLType, int graphMLScope)
;With the parameters:
tag
: the provider Key to get the DataMap from the graph using Graph.getDataProvider(Object providerKey)name
: the name for the data used in the GraphML filegraphMLType
: an integer constant describing the type of the attribute, as defined in
org.graphdrawing.graphml.attr.AttributeConstants
graphMLScope
: an integer constant describing the scope (e.g. node, edge), as defined inorg.graphdrawing.graphml.GraphMLConstants
GraphRoundtripSupport grs = new GraphRoundtripSupport();
// create a mapper with the tag "edgeProviderKey"
// which will write int data for edges into attributes named "edgeIndex"
// since yFiles FLEX 1.5:
grs.addMapper("edgeProviderKey", "edgeIndex", KeyType.INT, KeyScope.EDGE);
// until yFiles FLEX 1.4:
// grs.addMapper("edgeProviderKey", "edgeIndex", AttributeConstants.TYPE_INT, GraphMLConstants.SCOPE_EDGE);
// create another mapper which is used to map data to the graph itself
// since yFiles FLEX 1.5:
grs.addMapper("graphProviderKey", "graph-data", KeyType.STRING, KeyScope.GRAPH);
// until yFiles FLEX 1.4:
//grs.addMapper("graphProviderKey", "graph-data", AttributeConstants.TYPE_STRING, GraphMLConstants.SCOPE_GRAPH);
// get the LayoutGraph created by the GraphRoundtripSupport
LayoutGraph graph = grs.createRoundtripGraph();
// get the DataProvider with the tag "edgeProviderKey"
DataMap dp = (DataMap) graph.getDataProvider("edgeProviderKey");
// fill the Map with edge related data (here as example the index of the edge)
for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
Edge edge = ec.edge();
dp.setInt(edge, edge.index());
}
// get the DataProvider with the tag "graphProviderKey"
DataMap graphDp = (DataMap) graph.getDataProvider("graphProviderKey");
// map the data to the graph
graphDp.set(graph, "I'm the graph!");
// send the graph to the client
grs.sendGraph(graph, httpServletResponse);
Transferring complex attributes
A complex attribute on the other hand has to be registered using:since yFiles FLEX 1.5
GraphRoundtripSupport.addObjectMapper(Object tag, KeyScope scope, SerializationHandler serializer, DeserializationHandler deserializer)
With the parameters:
tag
: the provider key to get the DataMap from the graph using Graph.getDataProvider(Object providerKey)scope
: a KeyScope describing the scope (e.g. node, edge)serializer
: The serializer instance that will be used for writing the complex data objects.deserializer
: The deserializer instance that will be used for reading the complex data objects.
until yFiles FLEX 1.4
GraphRoundtripSupport.addObjectMapper(Object tag, int graphMLScope, ISerializer serializer, IDeserializer deserializer)
;With the parameters:
tag
: the provider Key to get the DataMap from the graph using Graph.getDataProvider(Object providerKey)graphMLScope
: an integer constant describing the scope (e.g. node, edge), as defined inorg.graphdrawing.graphml.GraphMLConstants
serializer
: The serializer instance that will be used for writing the complex data objects.deserializer
: The deserializer instance that will be used for reading the complex data objects.
GraphRoundtripSupport grs = new GraphRoundtripSupport();
// create a mapper with the tag "nodeObjectProviderKey"
// which will write complex data of type IBPElement for nodes into attributes using the same name
// since yFiles FLEX 1.5
support.addObjectMapper("nodeObjectProviderKey", KeyScope.NODE,
new BPSerializer(), new BPDeserializer());
// until yFiles FLEX 1.4
// support.addObjectMapper("nodeObjectProviderKey", GraphMLConstants.SCOPE_NODE,
new BPSerializer(), new BPDeserializer());
// get the LayoutGraph created by the GraphRoundtripSupport
LayoutGraph graph = grs.createRoundtripGraph();
// get the DataProvider with the tag "nodeObjectProviderKey"
DataMap dp = (DataMap) graph.getDataProvider("nodeObjectProviderKey");
// fill the Map with node related data (here as example new BPActivity objects)
for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
Node node = nc.node();
dp.set(node, new BPActivity());
}
// send the graph to the client
grs.sendGraph(graph, httpServletResponse);
Server (yFiles.NET)
Transferring simple attributes
On the server side, the simple attribute has to be registered for reading and writing with theyWorks.yFiles.Graph.Web.GraphRoundtripSupport
, using
GraphRoundtripSupport.AddMapper<K, V>(object tag, string name)
orGraphRoundtripSupport.AddMapper<K, V>(object tag, string name, KeyScope scopeType, KeyType keyType)
K
corresponds to the type to store the attribute for (mostly
INode
or IEdge
).
The type argument V
corresponds to the type of the values to be stored (mostly string).
The following parameters are required:
tag
: The tag that is to be used to retrieve the mapper from the graph's mapper registryname
: The name of the attribute that is to be used for GraphML I/O
scopeType
and keyType
parameters can either be passed explicitly or they will be inferred from the type attributes K
and V
.
The following example registers a custom mapper for edge attributes, sets the mapper values and sends the graph to the client.
GraphRoundtripSupport roundtripSupport = createRoundtripSupport();
// create a mapper with the tag "edgeProviderKey"
// that will write int data for edges into attributes named "edgeIndex"
roundtripSupport.AddMapper<IEdge,string>("edgeProviderKey", "edgeIndex");
// create a mapper to map data to the graph itself
roundtripSupport.AddMapper<IGraph, string>("graphProviderKey", "graph-data");
// get the properly configured IGraph created by the GraphRoundtripSupport
IGraph graph = roundtripSupport.CreateRoundtripGraph();
// retrieve the IMapper instance and register some data
IMapperRegistry registry = graph.Lookup(typeof(IMapperRegistry)) as IMapperRegistry;
if (null != registry) {
IMapper<IEdge, string> edgeIndexMapper = registry.GetMapper<IEdge,string>("edgeProviderKey");
if( null != edgeIndexMapper ) {
int i = 0;
foreach( IEdge edge in graph.Edges ) {
edgeIndexMapper.SetValue( edge, i++.ToString() );
}
}
IMapper<IGraph, string> graphMapper = registry.GetMapper<IGraph,string>("graphProviderKey");
graphMapper.SetValue( graph, "I'm the graph!" );
}
// send the graph to the client
roundtripSupport.SendGraph(graph,context.Response);
Transferring complex attributes
A complex attribute has to be registered for reading and writing with theyWorks.yFiles.Graph.Web.GraphRoundtripSupport
using GraphRoundtripSupport.AddObjectMapper<K, V>(object tag, KeyScope scopeType, ISerializer serializer, IDeserializer deserializer )
.The type arguments
K
and V
again correspond to the type to store the attribute for resp. the type of the values to be stored.
The following parameters are required:
tag
:The tag that is to be used for the graph's mapper registry.scopeType
: The graphml scope of the data.serializer
: A serializer instance that will be used for writing the attribute objects.deserializer
: A deserializer instance that will be used for parsing the attribute objects.
GraphRoundtripSupport roundtripSupport = createRoundtripSupport();
// create a mapper with the tag "nodeObjectProviderKey"
// that will write complex data of type CustomDataObject for nodes.
roundtripSupport.AddObjectMapper<INode, CustomDataObject>("nodeObjectProviderKey", KeyScope.Node,
CustomDataObjectSerializer.instance, CustomDataObjectDeserializer.instance);
// get the properly configured IGraph created by the GraphRoundtripSupport
IGraph graph = roundtripSupport.CreateRoundtripGraph();
// retrieve the IMapper instance and register some data
IMapperRegistry registry = graph.Lookup(typeof(IMapperRegistry)) as IMapperRegistry;
if (null != registry) {
IMappe<INode, CustomDataObject> nodeDataMapper =
registry.GetMapper<INode, CustomDataObject>("nodeObjectProviderKey");
if( null != nodeDataMapper ) {
int i = 0;
foreach( INode node in graph.Nodes ) {
nodeDataMapper.SetValue( node, new CustomDataObject(++i);
}
}
}
// send the graph to the client
roundtripSupport.SendGraph(graph,context.Response);
Client
A GraphML file containing custom mapped data will look like this:
<graphml>
...
<!-- The custom GraphML attribute -->
<key id="d0" for="edge" attr.name="edgeIndex" attr.type="int"/>
<key id="d1" for="graph" attr.name="graph-data" attr.type="string"/>
<graph id="G" edgedefault="directed">
...
<!-- An edge that has a <data> element referring to the GraphML attribute "d0" -->
<edge id="e0">
...
<data key="d0">0</data> <!-- The transferred value is 0 -->
</edge>
...
<data key="d1">I'm the graph!</data> <!-- This data is mapped to the graph -->
</graph>
</graphml>
Handling simple attributes
On the client side, the simple attribute has to be registered with the RoundtripHandler,
using RoundtripHandler.addMapperAttribute(tag:Object, name:String = null,
scope:String = GraphMLConstants.SCOPE_NODE, contentType:String =
GraphMLConstants.TYPE_STRING)
. The parameters are the same as on the server side:
tag
: the provider Keyname
: the name for the data used in the GraphML filescope
: a String constant describing the scope (e.g. node, edge), as defined incom.yworks.io.graphml.GraphMLConstants
contentType
: a String constant describing the type of the attribute, as defined incom.yworks.io.graphml.GraphMLConstants
The corresponding mapper has to be registered with the graph's mapper registry before the graphml data is read into the graph instance.
The following code reads the above GraphML file:
var graph:DefaultGraph = DefaultGraph(graphCanvas.graph);
// register the mapper for the attribute to the graph
graph.mapperRegistry.addMapper( "clientEdgeProviderKey", new DictionaryMapper(true) );
graph.mapperRegistry.addMapper( "clientGraphProviderKey", new DictionaryMapper(true) );
var roundtripHandler:RoundtripHandler = new RoundtripHandler(graphCanvas);
// register the mapper for the attribute to the RoundtripHandler
roundtripHandler.addMapperAttribute(
"clientEdgeProviderKey",
"edgeIndex",
GraphMLConstants.SCOPE_EDGE,
GraphMLConstants.TYPE_INT);
// do the same for data which is mapped to the graph
roundtripHandler.addMapperAttribute(
"clientGraphProviderKey",
"graph-data",
GraphMLConstants.SCOPE_GRAPH,
GraphMLConstants.TYPE_STRING);
...
// let the roundtripHandler read the data here
...
// get the mapper
var dataMapper:IMapper = graphCanvas.graph.mapperRegistry.getMapper( "clientEdgeProviderKey" );
// read the data from the mapper
var mappedEdgeData:int = dataMapper.lookupValue(anEdge);
// the same for the graph
var graphMapper:IMapper = graphCanvas.graph.mapperRegistry.getMapper( "clientGraphProviderKey" );
var mappedGraphData:String = graphMapper.lookupValue(graphCanvas.graph);
Handling complex attributes
A complex attribute has to be registered on client side with the RoundtripHandler,
using RoundtripHandler.addObjectMapperAttribute(tag:Object, scope:String, serializer:ISerializer, deserializer:IDeserializer )
.
Again the parameters are the same as on the server side:
tag
:The attribute tag. The attribute mapper has to be registered in the graph's mapper registry with this tag. A string representation of the tag will also be used as the attribute name for the GraphML attribute.scope
: The scope type of the attribute. One of- GraphMLConstants.SCOPE_NODE
- GraphMLConstants.SCOPE_EDGE
- GraphMLConstants.SCOPE_PORT
- GraphMLConstants.SCOPE_GRAPH
- GraphMLConstants.SCOPE_GRAPHML
- GraphMLConstants.SCOPE_ALL
serializer
: The serializer instance that will be used for writing the attribute objects.deserializer
: The deserializer instance that will be used for reading the attribute objects.
The corresponding mapper has to be registered with the graph's mapper registry before the graphml data is read into the graph instance.
The following code reads a transferred complex node attribute:
var graph:DefaultGraph = DefaultGraph(graphCanvas.graph);
// register the mapper for the attribute to the graph
graph.mapperRegistry.addMapper( "nodeObjectProviderKey", new DictionaryMapper() );
var roundtripHandler:RoundtripHandler = new RoundtripHandler(graphCanvas);
// register the mapper for the attribute to the RoundtripHandler
roundtripHandler.addObjectMapperAttribute("nodeObjectProviderKey", GraphMLConstants.SCOPE_NODE,
new BPSerializer(), new BPDeserializer());
...
// let the roundtripHandler read the data here
...
// get the mapper
var dataMapper:IMapper = DefaultGraph( graphCanvas.graph ).
mapperRegistry.getMapper( "nodeObjectProviderKey" );
// read the data from the mapper
var mappedNodeData:Object = dataMapper.lookupValue(aNode);
See also yFiles FLEX Developer's guide, Chapter 3, Customizing the yFiles FLEX I/O Support.