1   package demo.yext.graphml;
2   
3   import java.awt.Color;
4   import java.awt.Graphics2D;
5   import java.awt.Shape;
6   import java.awt.event.ActionEvent;
7   import java.awt.event.ActionListener;
8   import java.awt.geom.AffineTransform;
9   import java.awt.geom.Ellipse2D;
10  import java.awt.geom.GeneralPath;
11  import java.awt.geom.RectangularShape;
12  import java.awt.geom.RoundRectangle2D;
13  import java.util.HashMap;
14  import java.util.Map;
15  import javax.swing.JComboBox;
16  import javax.swing.JToolBar;
17  import javax.swing.JMenu;
18  
19  import org.graphdrawing.graphml.reader.GraphMLParseContext;
20  import org.graphdrawing.graphml.writer.GraphMLWriteContext;
21  import org.graphdrawing.graphml.writer.XmlWriter;
22  import org.graphdrawing.graphml.GraphMLConstants;
23  import org.w3c.dom.NamedNodeMap;
24  import org.w3c.dom.Node;
25  
26  import y.view.AbstractCustomHotSpotPainter;
27  import y.view.AbstractCustomNodePainter;
28  import y.view.GenericNodeRealizer;
29  import y.view.NodeRealizer;
30  import y.view.SimpleUserDataHandler;
31  import yext.graphml.graph2D.GenericNodeRealizerSerializer;
32  
33  /**
34   * This class demonstrates how to create customized node realizers 
35   * of type {@link GenericNodeRealizer} and how to read and write 
36   * these customized types in GraphML format. 
37   */
38  public class GenericNodeRealizerDemo extends GraphMLDemo
39  {
40    
41    /** Create the demo */
42    public GenericNodeRealizerDemo()
43    {
44      super();
45      getGraphMLIOHandler().getRealizerSerializerManager().addNodeRealizerSerializer(new MyGenericNodeRealizerSerializer());
46      
47      // create a default NodeRealizer
48      GenericNodeRealizer cnr = new GenericNodeRealizer();
49      
50      // get the factory to register our own styles
51      GenericNodeRealizer.Factory factory = GenericNodeRealizer.getFactory();
52      
53      
54      // use a map to pass in our implementations
55      Map implementationsMap = new HashMap();
56      // create custom implementations for ...
57      
58      // the painter and contains test
59      CustomPainter painter = new CustomPainter(new Ellipse2D.Double());
60      // register the painter
61      implementationsMap.put(GenericNodeRealizer.Painter.class, painter);
62      // and the contains test
63      implementationsMap.put(GenericNodeRealizer.ContainsTest.class, painter);
64      
65      // create a custom hotspot painter and hot spot hit test
66      CustomHotSpotPainter chsp = new CustomHotSpotPainter(165, new Ellipse2D.Double(), null);
67      // register the painter
68      implementationsMap.put(GenericNodeRealizer.HotSpotPainter.class, chsp);
69      // and the hit test
70      implementationsMap.put(GenericNodeRealizer.HotSpotHitTest.class, chsp);
71  
72      // a simple default implementation that can deal with cloneable and serializable userdata....
73      implementationsMap.put(GenericNodeRealizer.UserDataHandler.class, new SimpleUserDataHandler(SimpleUserDataHandler.REFERENCE_ON_FAILURE));
74      
75      // finally add the configuration to the factory
76      factory.addConfiguration("type1", implementationsMap);
77      
78      // do the same with two different styles...
79      painter = new CustomPainter(new RoundRectangle2D.Double(50,50,50,50,15, 15));
80      implementationsMap.put(GenericNodeRealizer.Painter.class, painter);
81      implementationsMap.put(GenericNodeRealizer.ContainsTest.class, painter);
82      chsp = new CustomHotSpotPainter(255, new Ellipse2D.Double(), Color.red);
83      implementationsMap.put(GenericNodeRealizer.HotSpotPainter.class, chsp);
84      implementationsMap.put(GenericNodeRealizer.HotSpotHitTest.class, chsp);
85      factory.addConfiguration("type2", implementationsMap);
86      
87      GeneralPath gp = new GeneralPath();
88      gp.moveTo(1.0f, 0.5f);
89      gp.lineTo(0.0f, 1.0f);
90      gp.quadTo(0.0f, 0.5f, 0.3f, 0.5f);
91      gp.quadTo(0.0f, 0.5f, 0.0f, 0.0f);
92      gp.closePath();
93      
94      PolygonPainter pp = new PolygonPainter(gp);
95      implementationsMap.put(GenericNodeRealizer.Painter.class, pp);
96      implementationsMap.put(GenericNodeRealizer.ContainsTest.class, pp);
97      factory.addConfiguration("type3", implementationsMap);
98      
99      // initialize the default noderealizer to the type we just registered...
100     cnr.setConfiguration("type1");
101     cnr.setUserData(new UserData(1));
102     // set the realizer...
103     view.getGraph2D().setDefaultNodeRealizer(cnr);
104   }
105   
106   /** Create a toolbar to switch the default node realizer type */
107   protected JToolBar createToolBar()
108   {
109     JToolBar retValue;
110     
111     retValue = super.createToolBar();
112     final JComboBox cb = new JComboBox(new Object[]{"type1", "type2", "type3"});
113     retValue.add(cb);
114     cb.addActionListener(new ActionListener()
115     {
116       public void actionPerformed(ActionEvent ae)
117       {
118         GenericNodeRealizer gnr = (GenericNodeRealizer)view.getGraph2D().getDefaultNodeRealizer();
119         gnr.setConfiguration(cb.getSelectedItem().toString());
120         gnr.setUserData(new UserData(cb.getSelectedIndex()+1));
121       }
122     });
123     return retValue;
124   }
125 
126   protected JMenu createSampleGraphMenu() {
127     return null;
128   }
129 
130 
131   /**
132    * A custom HotSpotPainter implementation
133    */
134   static final class CustomHotSpotPainter extends AbstractCustomHotSpotPainter
135   {
136     
137     private RectangularShape shape;
138     private Color color;
139     
140     CustomHotSpotPainter(int mask, RectangularShape shape, Color color)
141     {
142       super(mask);
143       this.shape = shape;
144       this.color = color;
145     }
146     
147     protected void initGraphics(NodeRealizer context, Graphics2D g)
148     {
149       super.initGraphics(context, g);
150       if (color == null)
151       {
152         Color fc = context.getFillColor();
153         if (fc != null)
154         {
155           g.setColor(fc);
156         }
157       }
158       else
159       {
160         g.setColor(color);
161       }
162     }
163     
164     
165     protected void paint(byte hotSpot, double centerX, double centerY, Graphics2D graphics)
166     {
167       shape.setFrame(centerX - 2, centerY - 2, 5, 5);
168       graphics.fill(shape);
169     }
170     
171     protected boolean isHit(byte hotSpot, double centerX, double centerY, double testX, double testY)
172     {
173       return Math.abs(testX - centerX) < 3 && Math.abs(testY - centerY) < 3;
174     }
175     
176   }
177   
178   /**
179    * A custom Painter and ContainsTest implementation.
180    * This one works with any kind of RectangularShape
181    */
182   static final class CustomPainter extends AbstractCustomNodePainter implements GenericNodeRealizer.ContainsTest
183   {
184     RectangularShape shape;
185     
186     CustomPainter(RectangularShape shape)
187     {
188       this.shape = shape;
189     }
190     
191     /** Override default fill color */
192     protected Color getFillColor(NodeRealizer context, boolean selected)
193     {
194       if (selected)
195       {
196         return Color.red;
197       }
198       else
199       {
200         return super.getFillColor(context, selected);
201       }
202     }
203     
204     protected void paintNode(NodeRealizer context, Graphics2D graphics, boolean sloppy)
205     {
206       shape.setFrame(context.getX(), context.getY(), context.getWidth(), context.getHeight());
207       if (initializeFill(context, graphics))
208       {
209         graphics.fill(shape);
210       }
211       if (initializeLine(context, graphics))
212       {
213         graphics.draw(shape);
214       }
215       UserData data = (UserData)((GenericNodeRealizer)context).getUserData();
216       graphics.setColor(Color.black);
217       graphics.drawString("data=" + data.value,
218                           (float)context.getX(),
219                           (float)(context.getY() - 1));
220     }
221     
222     public boolean contains(NodeRealizer context, double x, double y)
223     {
224       shape.setFrame(context.getX(), context.getY(), context.getWidth(), context.getHeight());
225       return shape.contains(x, y);
226     }
227   }
228   
229   /**
230    * Another custom Painter and ContainsTest implementation.
231    * This one works with any kind of GeneralPath
232    */
233   static final class PolygonPainter extends AbstractCustomNodePainter implements GenericNodeRealizer.ContainsTest
234   {
235     GeneralPath path;
236     AffineTransform aft;
237     PolygonPainter(GeneralPath path)
238     {
239       this.path = path;
240       this.aft = AffineTransform.getScaleInstance(1.0d, 1.0d);
241     }
242     
243     protected void paintNode(NodeRealizer context, Graphics2D graphics, boolean sloppy)
244     {
245       aft.setToIdentity();
246       aft.translate(context.getX(), context.getY());
247       aft.scale(context.getWidth(), context.getHeight());
248       Shape shape = path.createTransformedShape(aft);
249       if (initializeFill(context, graphics))
250       {
251         graphics.fill(shape);
252       }
253       if (initializeLine(context, graphics))
254       {
255         graphics.draw(shape);
256       }
257       UserData data = (UserData)((GenericNodeRealizer)context).getUserData();
258       graphics.setColor(Color.black);
259       graphics.drawString("data=" + data.value,
260                           (float)context.getX(),
261                           (float)(context.getY() + context.getHeight() + 8));
262     }
263     
264     /** Override default fill color to be the same as the unselected fill color */
265     protected Color getFillColor(NodeRealizer context, boolean selected)
266     {
267       return super.getFillColor(context, false);
268     }
269     
270     public boolean contains(NodeRealizer context, double x, double y)
271     {
272       return path.contains((x - context.getX()) / context.getWidth(), (y - context.getY()) / context.getHeight());
273     }
274   }
275   
276   /**
277    * The type for the user data that is associated with GenericNodeRealizer.
278    */
279   static class UserData {
280     int value;
281     UserData(int value) {
282       this.value = value;
283     }
284   }
285   
286   /**
287    * Serializer for instances of {@link y.view.GenericNodeRealizer}.
288    * Demonstrates how the serialization of user data can be customized. 
289    */
290   static class MyGenericNodeRealizerSerializer extends GenericNodeRealizerSerializer
291   {
292     /**
293      * Writes the userdata section of this realizer and assigns the user data object. 
294      */
295     protected void writeUserData(GenericNodeRealizer gnr, XmlWriter writer, GraphMLWriteContext context)
296     {
297       if (gnr.getUserData() instanceof UserData)
298       {
299         UserData data = (UserData)gnr.getUserData();
300         writer.writeStartElement("UserData", GraphMLConstants.YWORKS_EXT_NS_URI)
301             .writeAttribute("class", data.getClass().getName())
302             .writeAttribute("value", String.valueOf( data.value ) )
303             .writeEndElement();
304       }
305       else {
306         super.writeUserData(gnr, writer, context);
307       }
308     }
309     
310     /**
311      * Parses the userdata section of this realizer and assigns the user data object. 
312      */
313     protected void parseUserData(GenericNodeRealizer gnr,Node domNode, GraphMLParseContext context) 
314     {
315       String name = domNode.getLocalName();
316       if (!"UserData".equals(name)) {
317         return;
318       }
319 
320       NamedNodeMap nm = domNode.getAttributes();
321       Node a;
322       a = nm.getNamedItem("class");
323       if(UserData.class.getName().equals(a.getNodeValue())) {
324         a = nm.getNamedItem("value");
325         gnr.setUserData(new UserData(Integer.parseInt(a.getNodeValue())));
326       }
327       else {
328         super.parseUserData(gnr, domNode, context);
329       }
330     }
331   }
332   
333   
334   /**
335    * Launcher method. Execute this class to see sample instantiations of
336    * the CustomNodeRealizer in action.
337    */
338   public static void main(String[] args)
339   {
340     initLnF();
341     final GenericNodeRealizerDemo demo = new GenericNodeRealizerDemo();
342     demo.start();
343   }
344 }
345