1   /****************************************************************************
2    **
3    ** This file is part of yFiles-2.9. 
4    ** 
5    ** yWorks proprietary/confidential. Use is subject to license terms.
6    **
7    ** Redistribution of this file or of an unauthorized byte-code version
8    ** of this file is strictly forbidden.
9    **
10   ** Copyright (c) 2000-2011 by yWorks GmbH, Vor dem Kreuzberg 28, 
11   ** 72070 Tuebingen, Germany. All rights reserved.
12   **
13   ***************************************************************************/
14  package demo.view.realizer;
15  
16  
17  import java.awt.Color;
18  import java.awt.Graphics2D;
19  import java.awt.Stroke;
20  import java.awt.geom.GeneralPath;
21  import java.io.IOException;
22  import java.io.ObjectInputStream;
23  import java.io.ObjectOutputStream;
24  
25  import y.util.YVersion;
26  import y.view.LineType;
27  import y.view.NodeRealizer;
28  import y.view.ShapeNodeRealizer;
29  import y.io.graphml.graph2d.ShapeNodeRealizerSerializer;
30  import y.io.graphml.input.GraphMLParseContext;
31  import y.io.graphml.input.GraphMLParseException;
32  import y.io.graphml.output.XmlWriter;
33  import y.io.graphml.output.GraphMLWriteContext;
34  import org.w3c.dom.Node;
35  import org.w3c.dom.Element;
36  
37  /**
38   * This class represents a custom NodeRealizer with its own paint,
39   * copy and serialisation routines.
40   * <br>
41   * This realizer will be used in the demo
42   * {@link demo.view.realizer.StateNodeRealizerDemo}.
43   * @see <a href="http://docs.yworks.com/yfiles/doc/developers-guide/realizers.html#cls_ShapeNodeRealizer">Section Bringing Graph Elements to Life: The Realizer Concept</a> in the yFiles for Java Developer's Guide
44   * @see <a href="http://docs.yworks.com/yfiles/doc/developers-guide/mvc_controller.html#custom_edit_mode">Section User Interaction</a> in the yFiles for Java Developer's Guide
45   */
46  public class StateNodeRealizer extends ShapeNodeRealizer
47  {
48    /**
49     * State specifier constant. A node with this state will be drawn
50     * like an ordinary ShapeNodeRealizer.
51     */
52    public static final byte INITIAL_STATE     = 0;
53    
54    /**
55     * State specifier constant. A node with this state will be drawn
56     * with an additional dashed line.
57     */
58    public static final byte TRANSITION_STATE  = 1;
59  
60    /**
61     * State specifier constant. A node with this state will be drawn
62     * with an additional solid line.
63     */
64    public static final byte FINAL_STATE       = 2;
65    
66    
67    private byte state;
68    
69    /**
70     * Instantiates a new StateNodeRealizer with the given state.
71     * @see #setState(byte)
72     */
73    public StateNodeRealizer(byte state)
74    {
75      super();
76      this.state = state;
77      this.setShapeType(ELLIPSE);
78    }
79    
80    /**
81     * Instantiates a new StateNodeRealizer.
82     */
83    public StateNodeRealizer()
84    {
85      this(INITIAL_STATE);
86    }
87    
88    /**
89     * Instantiates a new StateNodeRealizer as a copy of 
90     * the given NodeRealizer.
91     */
92    public StateNodeRealizer(NodeRealizer r)
93    {
94      super(r);
95      if(r instanceof StateNodeRealizer)
96      {
97        StateNodeRealizer sr = (StateNodeRealizer)r;
98        state = sr.state;
99      }
100     else
101     {
102       state = INITIAL_STATE;
103     }
104   }
105   
106   /**
107    * Sets the state of this realizer.
108    * @param state on of {@link #INITIAL_STATE}, {@link #TRANSITION_STATE}
109    * or {@link #FINAL_STATE}.
110    */
111   public void setState(byte state)
112   {
113     this.state = state;
114   }
115   
116   /**
117    * Returns the state of this realizer.
118    * By default {@link #INITIAL_STATE} will be returned.
119    * @see #setState(byte)
120    */
121   public byte getState()
122   {
123     return this.state;
124   }
125   
126   
127   /**
128    * Paints the node on the given graphics context.
129    * Depending on the state of the realizer the node will be
130    * drawn differently
131    */
132   public void paintNode(Graphics2D gfx)
133   {
134     //first paint the shape node realizer
135     super.paintNode(gfx);
136     
137     if(getState() != INITIAL_STATE)
138     {
139       //then draw an additional state marker
140       //on top of the node.
141       Stroke oldStroke = gfx.getStroke();
142       Color oldColor   = gfx.getColor();
143       if(state == TRANSITION_STATE) {
144         gfx.setStroke(LineType.DASHED_1);
145       } else if(getState() == FINAL_STATE) {
146         gfx.setStroke(LineType.LINE_1);
147       }
148       double oldWidth  = getWidth();
149       double oldHeight = getHeight();
150       if(oldWidth > 10.0 && oldHeight > 10.0)
151       {
152         setSize(oldWidth- 10.0,oldHeight- 10.0);
153         gfx.draw(shape);
154         setSize(oldWidth,oldHeight);
155       }
156       
157       gfx.setColor(oldColor);
158       gfx.setStroke(oldStroke);
159     }
160   }
161   
162   /**
163    * Creates a copy of the given realizer that has type
164    * StateNodeRealizer. It is important to implement this method properly
165    * if the realizer should be able to act as a template for 
166    * other realizers, e.g. if it is used as default node realizer
167    * in a Graph2D.
168    * The canonical way to implement this method is to return the result
169    * of a copy constructor.
170    * @see y.view.Graph2D#setDefaultNodeRealizer(y.view.NodeRealizer)
171    * @see #StateNodeRealizer(NodeRealizer) 
172    */
173   public NodeRealizer createCopy(NodeRealizer r)
174   {
175     return new StateNodeRealizer(r);
176   }
177 
178   /**
179    * A custom shape type specifier.
180    */
181   public static final byte CUSTOM_SHAPE = -1;
182 
183   /**
184    * Demonstrates how to define custom shapes.
185    */
186   public void setShapeType(byte type)
187   {
188     if(type == CUSTOM_SHAPE)
189     {
190       updateCustomShape();
191     }
192     super.setShapeType(type);
193   }
194   
195   void updateCustomShape()
196   {
197     GeneralPath path = new GeneralPath(GeneralPath.WIND_EVEN_ODD,5);
198     float x = (float)getX();
199     float y = (float)getY();
200     float w = (float)getWidth();
201     float h = (float)getHeight();
202     path.moveTo(x, y + h);
203     path.lineTo(x, y);
204     float dx = Math.min(h*0.2f,w);
205     path.lineTo(x+w-dx,y);
206     path.lineTo(x+w,y+0.5f*h);
207     path.lineTo(x+w-dx,y+h);
208     path.closePath();
209     shape = path;      
210   }
211   
212   /**
213    * adjust custom shape when size changes. Overrides default scaling operation.
214    */
215   public void setSize(double x, double y)
216   {
217     if(getShapeType() == CUSTOM_SHAPE)
218     {
219       shape = null;
220       super.setSize(x,y);
221       updateCustomShape();
222     }
223     else
224     {
225       super.setSize(x,y);
226     }
227   }
228       
229   
230   /**
231    * Writes out this realizer in a serialized form. This method 
232    * will be used by YGFIOHandler to serialize this NodeRealizer.
233    * @deprecated Use {@link demo.view.realizer.StateNodeRealizer.StateNodeRealizerSerializer}
234    * for serialization to the {@link y.io.GraphMLIOHandler GraphML format}
235    * instead.
236    */
237   public void write(ObjectOutputStream out) throws IOException 
238   {
239     //write out a version tag. version tags help to provide future
240     //serialization compatibility when node realizer features
241     //change.    
242     out.writeByte(YVersion.VERSION_1);
243     //write out the shape node realizer features
244     super.write(out);
245     //write out the state variable
246     out.writeByte(state);
247   }
248   
249   /**
250    * Reads in the serialized form of this realizer. The realizer must have been
251    * written out before by it's {@link #write(ObjectOutputStream)} method.
252    * This method will be used by YGFIOHandler to deserialize this NodeRealizer.
253    * @deprecated Use {@link demo.view.realizer.StateNodeRealizer.StateNodeRealizerSerializer}
254    * for serialization to the {@link y.io.GraphMLIOHandler GraphML format} 
255    * instead.
256    */
257   public void read(ObjectInputStream in) throws IOException, 
258     ClassNotFoundException 
259   {
260     switch(in.readByte()) {
261     case YVersion.VERSION_1:
262       super.read(in);
263       state = in.readByte();
264       break;
265     default:
266       //trouble
267     }
268   }
269 
270   /**
271    * RealizerSerializer that can be used to serialize instances of StateNodeRealizer to and from
272    * {@link y.io.GraphMLIOHandler GraphML format}.
273    */
274   static class StateNodeRealizerSerializer extends ShapeNodeRealizerSerializer {
275     public String getName() {
276       return "StateNode";
277     }
278 
279     public String getNamespaceURI() {
280       return "demo.view.realizer";
281     }
282 
283     public Class getRealizerClass() {
284       return StateNodeRealizer.class;
285     }
286 
287     public void parse(NodeRealizer realizer, Node domNode, GraphMLParseContext context) throws GraphMLParseException {
288       super.parse(realizer, domNode, context);
289       StateNodeRealizer snr = (StateNodeRealizer) realizer;
290       String state = ((Element) domNode).getAttribute("state");
291 
292       if("initial".equals(state)) {
293         snr.setState(INITIAL_STATE);
294       }
295       else if("transition".equals(state)) {
296         snr.setState(TRANSITION_STATE);
297       }
298       else if("final".equals(state)) {
299         snr.setState(FINAL_STATE);
300       }
301     }
302 
303 
304     protected byte decodeShapeType(String s, GraphMLParseContext context) {
305       if("custom".equals(s)) {
306         return CUSTOM_SHAPE;
307       }
308       return super.decodeShapeType(s, context);
309     }
310 
311     protected String encodeShapeType(ShapeNodeRealizer snr, GraphMLWriteContext context) {
312       if(snr.getShapeType() == CUSTOM_SHAPE) {
313         return "custom";
314       }
315       return super.encodeShapeType(snr, context);
316     }
317 
318     public void writeAttributes(NodeRealizer nr, XmlWriter writer, GraphMLWriteContext context) {
319       super.writeAttributes(nr, writer, context);
320       StateNodeRealizer snr = (StateNodeRealizer) nr;
321       switch(snr.getState()) {
322         case INITIAL_STATE:
323           writer.writeAttribute("state", "initial");
324           break;
325         case TRANSITION_STATE:
326           writer.writeAttribute("state", "transition");
327           break;
328         case FINAL_STATE:
329           writer.writeAttribute("state", "final");
330           break;
331       }
332     }
333   }
334 }
335 
336