As explained in Chapter 6, Using yFiles FLEX with a yFiles Server, the yFiles FLEX client uses a different concept for defining the visual properties of graph items than yFiles Java. Therefore, the yFiles FLEX server API contains a framework for reading and writing the yFiles FLEX GraphML format along with compatibility classes that model the client's styles, labels, and label models. The server API that provides this functionality is contained in package com.yworks.yfiles.server.graphml.flexio and its sub-packages.
Please see the section called “Using the yFiles FLEX Native GraphML Extension with a yFiles for Java Server” for the required configuration to use the yFiles FLEX format compatibility layer on the server.
In order to read, manipulate, and write custom styles on the server, three classes are required:
In the following, examples for all of these three classes are provided. The example implementations are taken from the custom style demo application that comes with yFiles FLEX. The custom style has fill, stroke, and "percentage" properties, all of which influence the way the style is rendered on the client. The following GraphML serialization format is used for the custom style:
<y:CustomStyle> <y:Percentage value="0.8" /> <y:Pen .. /> <y:Brush.. /> </y:CustomStyle>
A data object for a custom style is a very simple data class that provides getters and setters for all properties of the custom style. Instances of the data object class are created by the corresponding deserializer implementation.
Example 7.1. Custom style data object
/** * A simple data object that holds the state information of * the custom style. */ public class CustomStyle implements INodeStyle { private IStroke stroke; private IFill fill; private double percentage; public IStroke getStroke() { return stroke; } public void setStroke(IStroke stroke) { this.stroke = stroke; } public IFill getFill() { return fill; } public void setFill(IFill fill) { this.fill = fill; } public double getPercentage() { return percentage; } public void setPercentage(double percentage) { this.percentage = percentage; } }
A custom deserializer should extend AbstractDeserializer and override deserializeNode. It's important not to override deserialize instead, because this would prevent reference sharing from working correctly. The deserializer creates an instance of the corresponding data object and sets its properties by reading the GraphML data.
Example 7.2. Custom style deserializer
// Simple deserializer implementation that reads the custom style // and creates a corresponding CustomStyle data object that can be used to // manipulate the style properties. // // Because this deserializer extends AbstractDeserializer, we don't need // to implement canHandle. // The default implementation will return true, if // the element name and namespace of an xml element match // the values returned by getElementName and getXmlNamespaceURI. // // The framework already contains deserializers for fill and strokes instances. // Therefore, we can just delegate the deserialization of the fill and stroke // using FlexIOTools.deserialize public class CustomStyleDeserializer extends AbstractDeserializer { public Object deserializeNode(Node xmlNode, GraphMLParseContext context) throws GraphMLParseException { // Create a new instance of the data object that will keep the state // of the deserialized custom style CustomStyle style = new CustomStyle(); // Deserialize the fill using // FlexIOTools.deserialize, which will delegate the deserialization to // an DeserializationHandler that can deserialize fills. Node fillNode = XmlSupport.getChildNode(xmlNode, Constants.Y_BRUSH, NamespaceConstants.YFILES_JAVA_NS); IFill fill = (IFill) FlexIOTools.deserialize(context, fillNode); if (null != fill) { style.setFill(fill); } // Deserialize the stroke using // FlexIOTools.deserialize, which will delegate the deserialization to // an DeserializationHandler that can deserialize strokes Node strokeNode = XmlSupport.getChildNode(xmlNode, Constants.Y_PEN, NamespaceConstants.YFILES_JAVA_NS); IStroke stroke = (IStroke) FlexIOTools.deserialize(context, strokeNode); if (null != stroke) { style.setStroke(stroke); } // Read the percentage attribute Node percentageNode = XmlSupport.getChildNode(xmlNode, "Percentage", NamespaceConstants.YFILES_JAVA_NS); if (null != percentageNode) { String value = XmlSupport.getAttributeValue(percentageNode, "value"); if (null != value) { style.setPercentage(Double.parseDouble(value)); } } return style; } public String getElementName(GraphMLParseContext context) { return "CustomStyle"; } public String getXmlNamespaceURI(GraphMLParseContext context) { return NamespaceConstants.YFILES_JAVA_NS; } }
All SerializationHandler and DeserializationHandler instances have to be registered with the GraphMLHandler that is used for the GraphML (de)serialization process. Custom serializers and deserializers can also be added to the GraphRoundtripSupport instance that is used for client-server communication. Each time the graph is read or written with the roundtrip support, all serializers and deserializers that have been added using addSerializer(y.io.graphml.output.SerializationHandler) and addDeserializer(y.io.graphml.input.DeserializationHandler) are registered with the GraphMLHandler in addRegisteredHandlers(y.io.graphml.GraphMLHandler).
Example 7.3. Registering custom serializer and deserializer instances
// Register a serializer and a deserializer // for a custom style with the // this.support = new GraphRoundtripSupport(); support.addSerializer(new CustomStyleSerializer()); support.addDeserializer(new CustomStyleDeserializer());
Similar to the Flex side, serializing complex objects can be faciliated using the ReflectionBasedSerializer. This serializer uses reflection to serialize objects of different class types.
The ReflectionBasedSerializer handles all classes which are in a package which is registered in the static SymbolicPackageNameRegistry together with a namespace URI.
Example 7.4. Adding a package-namespace pair to the SymbolicPackageNameRegistry
SymbolicPackageNameRegistry.add( "com.yworks.yfiles.server.graphml.demo.support", "http://www.yworks.com/demo/style");
The namespace URI which will be used for the elements representing the serialized object in the GraphML.
The ReflectionBasedSerializer serializes all public read- and writable properties, i.e. properties which have public getter and setter methods which are named according to the JavaBeans conventions. It will not serialize public variables. Table 7.1, “Properties in Java, Flex and GraphML” shows how to handle a property with the identifier "property".
Table 7.1. Properties in Java, Flex and GraphML
Property type | Flex | GraphML | Java |
---|---|---|---|
String, numeric | property | Attribute: property="value" | getProperty()/setProperty() |
boolean | property | Attribute: property="true|false" | isProperty()/setProperty() |
Complex | property | Child element: <Class.property>[value]</Class.property> | getProperty()/setProperty() |
The corresponding deserializer is the ReflectionBasedDeserializer. It handles all XML elements which are defined in a namespace whose URI is stored in the SymbolicPackageNameRegistry.
The ReflectionBasedSerializer and ReflectionBasedDeserializer are registered by default. The serializer and deserializer will only be invoked if no other serializer / deserializer which can handle the current object is found.
Example 7.5, “Class to be serialized with the ReflectionBasedSerializer” shows the Java class Person which corresponds to the Flex class Person shown as example in the section called “Reflection Based Serialization”.
Example 7.5. Class to be serialized with the ReflectionBasedSerializer
package com.yworks.yfiles.server.graphml.demo.support; import com.yworks.yfiles.server.graphml.flexio.compat.Stroke; public class Person { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } private boolean vip; public boolean isVip() { return vip; } public void setVip(boolean vip) { this.vip = vip; } private Stroke pen; public Stroke getPen() { return pen; } public void setPen(Stroke pen) { this.pen = pen; } }
The ReflectionBasedSerializer generates the GraphML which is shown in Example 7.6, “GraphML generated by ReflectionBasedSerializer” .
Class StyledLayoutGraph extends LayoutGraph to provide methods for working with yFiles FLEX styles and labels on the server. GraphRoundtripSupport's createRoundtripGraph() method will always return a StyledLayoutGraph instance.
When the graph is read using a default GraphRoundtripSupport instance, the style and label data is parsed into data providers that are registered with the graph. Using StyledLayoutGraph, styles and labels can be accessed without having to deal with the underlying data providers. The methods provided by FlexIOTools for accessing styles and labels are listed below:
INodeStyle getStyle(Node node) |
|
Description | Get a node's style |
void setStyle(Node node, INodeStyle style) |
|
Description | Set the style for a node instance |
IEdgeStyle getStyle(Edge edge) |
|
Description | Get the style of an edge |
void setStyle(Edge edge,IEdgeStyle style) |
|
Description | Set the style for an edge instance |
void addLabel(Node node, Label label) |
|
Description | Add a label to a node |
void addLabel(Edge edge, Label label) |
|
Description | Add a label to an edge |
List getLabels(Node node) |
|
Description | Get all labels that belong to a node |
List getLabels(Edge edge) |
|
Description | Get all labels that belong to an edge |
Please see the section called “Working with Labels on the Server” for specifics on working with labels on the server.
User tags which are accessed using the ITagOwner interface at the client can be set/retrieved via convenience methods offered by StyledLayoutGraph:
Object getUserTag(Object item) |
|
Description | Get the user tag of the provided node, edge or label. |
static void setUserTag(Object item, Object tag) |
|
Description | Set the user tag of the provided node, edge or label. |
The implementation of custom serializers and deserializers is facilitated by the convenience methods serialize and deserialize of class FlexIOTools:
static void serialize(GraphMLWriteContext context, Object subject, XmlWriter writer) |
|
Description | Try to serialize the given instance using the context and silently ignores GraphMLWriteExceptions. |
static boolean trySerialize(Object item, GraphMLWriteContext context) |
|
Description | Tries to serialize the given item using the context and returns if the serialization was successful. |
static boolean canBeSerialized(Object item, GraphMLWriteContext context) |
|
Description | Tests and returns if the given item can be serialized using the context, i.e. an appropriate serialization handler can be found and used. |
static Object deserialize(GraphMLParseContext context, Node xmlNode) |
|
Description | Tries to delegate deserialization to the context and silently ignores GraphMLParseExceptions. |
static String getARGBString(Color c) |
|
Description | Returns the ARGB string (e.g. #FFCC00FF) of a java.awt.Color instance. |
static Color parseNamedOrARGBColor(String colorStr) |
|
Description | Parse the given argb string (e.g. #FFCC00FF) or named color (e.g. "Lime") into a java.awt.Color. |
Copyright ©2007-2015, yWorks GmbH. All rights reserved. |