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  import java.awt.Color;
17  import java.awt.EventQueue;
18  import java.awt.Font;
19  import java.awt.Graphics2D;
20  import java.awt.Shape;
21  import java.io.IOException;
22  import java.io.ObjectInputStream;
23  import java.io.ObjectOutputStream;
24  
25  import javax.swing.JFrame;
26  import javax.swing.JRootPane;
27  
28  import demo.view.DemoDefaults;
29  import y.util.D;
30  import y.util.YVersion;
31  import y.view.EditMode;
32  import y.view.Graph2DView;
33  import y.view.NodeLabel;
34  import y.view.NodeRealizer;
35  import y.view.ShapeNodeRealizer;
36  import y.view.SmartNodeLabelModel;
37  import y.view.YLabel;
38  
39  /**
40   * NodeRealizer implementation that represents a UML Class Node.
41   * This node realizer displays the following properties of a class
42   * in UML notation:
43   * <ul>
44   *  <li>class name</li>
45   *  <li>stereotype property</li>
46   *  <li>constraint property</li>
47   *  <li>attribute list</li>
48   *  <li>method list</li>
49   * </ul>
50   * Executing this class will display a sample instance of this realizer.
51   * @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
52   */
53  public class UMLClassNodeRealizer extends ShapeNodeRealizer
54  {
55    private NodeLabel aLabel; //attributeLabel
56    private NodeLabel mLabel; //methodLabel
57    private boolean clipContent;
58    private boolean omitDetails;
59    private String stereotype = "";
60    private String constraint = "";
61  
62    /**
63     * Instantiates a new UMLNodeRealizer.
64     */ 
65    public UMLClassNodeRealizer()
66    {
67      init();
68    }
69    
70    void init()
71    {
72      setShapeType(RECT_3D);
73  
74      SmartNodeLabelModel model = new SmartNodeLabelModel();
75      getLabel().setLabelModel(model);
76      getLabel().setModelParameter(model.createDiscreteModelParameter(SmartNodeLabelModel.POSITION_TOP));
77      
78      getLabel().setFontSize(13);
79      getLabel().setFontStyle(Font.BOLD);
80          
81      aLabel = new NodeLabel();
82      aLabel.bindRealizer(this);
83      aLabel.setAlignment(YLabel.ALIGN_LEFT);
84      aLabel.setLabelModel(new SmartNodeLabelModel());
85      
86      mLabel = new NodeLabel();
87      mLabel.bindRealizer(this);
88      mLabel.setAlignment(YLabel.ALIGN_LEFT);
89      mLabel.setLabelModel(new SmartNodeLabelModel());
90      
91      clipContent = true;
92      omitDetails = false;
93    }
94    
95    /**
96     * Instantiates a new UMLNodeRealizer as a copy of a given
97     * realizer.
98     */ 
99    public UMLClassNodeRealizer(NodeRealizer r)
100   {
101     super(r);
102     if(r instanceof UMLClassNodeRealizer)
103     {
104       UMLClassNodeRealizer cnr = (UMLClassNodeRealizer)r;
105       aLabel = (NodeLabel)cnr.aLabel.clone();
106       aLabel.bindRealizer(this);
107       mLabel = (NodeLabel)cnr.mLabel.clone();
108       mLabel.bindRealizer(this);
109       constraint = cnr.constraint;
110       stereotype = cnr.stereotype;
111       clipContent = cnr.clipContent;
112       omitDetails = cnr.omitDetails;  
113     }
114     else {
115       init();
116     }
117   }
118   
119   /**
120    * Returns a UMLNodERealizer that is a copy of the given 
121    * realizer.
122    */ 
123   public NodeRealizer createCopy(NodeRealizer r)
124   {
125     return new UMLClassNodeRealizer(r);
126   }
127     
128   
129   //////////////////////////////////////////////////////////////////////////////
130   // SETTER & GETTER ///////////////////////////////////////////////////////////
131   //////////////////////////////////////////////////////////////////////////////
132   
133   
134   /**
135    * Set the class name to be displayed by this realizer.
136    */
137   public void setClassName(String name)
138   {
139     setLabelText(name);
140   }
141   
142   /**
143    * Returns the class name to be displayed by this realizer.
144    */
145   public String getClassName()
146   {
147     return getLabelText();
148   }
149   
150   
151   /**
152    * Sets the constraint property of this realizer.
153    */
154   public void setConstraint(String constraint)
155   {
156     this.constraint = constraint;
157   }
158 
159   
160   /**
161    * Sets the stereotype property of this realizer.
162    */
163   public void setStereotype(String stereotype)
164   {
165     this.stereotype = stereotype;
166   }
167   
168   
169   /**
170    * Returns the constraint property of this realizer.
171    */
172   public String getConstraint()
173   {
174     return constraint;
175   }
176 
177   /**
178    * Returns the stereotype property of this realizer.
179    */
180   public String getStereotype()
181   {
182     return stereotype;
183   }
184   
185   
186   /**
187    * Returns the node label that represents all added
188    * method strings.
189    */
190   public NodeLabel getMethodLabel()
191   {
192     return mLabel;
193   }
194   
195   /**
196    * Returns the node label that represents all added
197    * attribute strings.
198    */
199   public NodeLabel getAttributeLabel()
200   {
201     return aLabel;
202   }
203   
204   /**
205    * Returns whether or not the display of the labels should be
206    * clipped with the bounding box of the realizer.
207    */
208   public boolean getClipContent()
209   {
210     return clipContent;
211   }
212  
213   /**
214    * Sets whether or not the display of the labels should be
215    * clipped with the bounding box of the realizer.
216    */
217   public void setClipContent(boolean clipping)
218   {
219     clipContent = clipping;
220   }
221   
222  
223   /**
224    * Set whether or not this realizer should omit details when being displayed.
225    */
226   public void setOmitDetails(boolean b)
227   {
228     omitDetails = b;
229   }
230   
231   
232   /**
233    * Returns whether or not this realizer should omit details when being displayed.
234    */ 
235   public boolean getOmitDetails()
236   {
237     return omitDetails;
238   }
239   
240   private void addToLabel(NodeLabel l, String s)
241   {
242     if(l.getText().length() > 0) {
243       l.setText(l.getText() + '\n' + s);
244     } else {
245       l.setText(s);
246     }
247   }
248   
249   
250   /**
251    * Adds a class method label to this realizer.
252    */ 
253   public void addMethod(String method)
254   {
255     addToLabel(mLabel,method);
256   }
257   
258   /**
259    * Adds a class attribute label to this realizer.
260    */ 
261   public void addAttribute(String attr)
262   {
263     addToLabel(aLabel,attr);
264   }
265   
266   /**
267    * Set the size of this realizer automatically. This method will adapt the size
268    * of this realizer so that the labels defined for it will fit within its
269    * bounding box.
270    */
271   public void fitContent()
272   {
273     double height = 3.0;
274     double width = getLabel().getWidth() + 10.0;
275     
276     if(stereotype.length() > 0)
277     {
278       NodeLabel l = new NodeLabel();
279       l.setText("<<" + getStereotype() + ">>");
280       l.setLabelModel(new SmartNodeLabelModel());
281       l.bindRealizer( this );
282       height += l.getHeight() + 5.0;
283       width = Math.max(l.getWidth()+ 10.0, width);
284     }
285     
286     height += getLabel().getHeight() + 3.0;
287 
288     if(constraint.length() > 0)
289     {
290       NodeLabel l = new NodeLabel();
291       l.setText("{" + getConstraint() + "}");
292       l.setLabelModel(new SmartNodeLabelModel());
293       height += l.getHeight() + 5.0;
294       width = Math.max(l.getWidth() + 10.0, width);
295     }
296     
297     if(!omitDetails && !(aLabel.getText().length() == 0 && mLabel.getText().length() == 0))
298     {
299       height += 3.0;
300       height += aLabel.getHeight() + 3.0;
301       width = Math.max(aLabel.getWidth() + 10.0,width);
302       height += 3.0;
303       height += mLabel.getHeight() + 3.0;
304       width = Math.max(mLabel.getWidth() + 10.0,width);
305     }
306     
307     setSize(width, height);
308   }
309   
310   
311   //////////////////////////////////////////////////////////////////////////////
312   // GRAPHICS  /////////////////////////////////////////////////////////////////
313   //////////////////////////////////////////////////////////////////////////////
314   
315   /**
316    * Paint the labels associated with this realizer.
317    */
318   public void paintText(Graphics2D gfx)
319   {    
320     Shape oldClip = null;
321     if(clipContent)
322     {
323       oldClip = gfx.getClip();
324       gfx.clip(getBoundingBox());//Rect((int)x,(int)y,(int)width,(int)height);
325     }
326     
327     double yoff = 3.0;
328 
329     if(stereotype.length() > 0)
330     {
331       NodeLabel l = new NodeLabel();
332       l.setText("<<" + getStereotype() + ">>");
333       SmartNodeLabelModel labelModel = new SmartNodeLabelModel();
334       l.setLabelModel(labelModel);
335       // Create a specific model parameter which aligns the node's and the label's centers horizontally
336       // (nodeRatioX and labelRatioX are 0) and the upper sides of node and label vertically. (nodeRatioY
337       // and labelRatioY are -0.5). In y-direction an offset is added (yoff) to to move the label to a
338       // position with some distance to the border of the node.
339       l.setModelParameter(labelModel.createSpecificModelParameter(0, -0.5, 0, -0.5, 0, yoff, 0, -1));
340       l.bindRealizer( this );
341       l.paint(gfx);
342       yoff += l.getHeight() + 5.0;
343     }
344     
345     NodeLabel label = getLabel();
346     final SmartNodeLabelModel labelModel = new SmartNodeLabelModel();
347     // Create a specific model parameter which aligns the node's and the label's centers horizontally
348     // (nodeRatioX and labelRatioX are 0) and the upper sides of node and label vertically. (nodeRatioY
349     // and labelRatioY are -0.5). In y-direction an offset is added (yoff) to to move the label to a
350     // position with some distance to the border of the node or to the previous label respectively.
351     label.setModelParameter(labelModel.createSpecificModelParameter(0, -0.5, 0, -0.5, 0, yoff, 0, -1));
352     label.paint(gfx);
353     yoff += label.getHeight() + 3.0;
354 
355     if(constraint.length() > 0)
356     {
357       NodeLabel l = new NodeLabel();
358       l.setText("{" + getConstraint() + "}");
359       SmartNodeLabelModel lModel = new SmartNodeLabelModel();
360       l.setLabelModel(lModel);
361       // Create a specific model parameter which aligns the node's and the label's right sides horizontally
362       // (nodeRatioX and labelRatioX are 0.5) and the upper sides of node and label vertically. (nodeRatioY
363       // and labelRatioY are -0.5). In y-direction an offset is added (yoff) to to move the label to a
364       // position with some distance to the border of the node or to the previous label respectively. In
365       // x-direction the offset (-0.5) is set so keep a distance to the border of the node.
366       l.setModelParameter(lModel.createSpecificModelParameter(0.5, -0.5, 0.5, -0.5, -5.0, yoff, 0, -1));
367       l.bindRealizer( this );
368       l.paint(gfx);
369       yoff += l.getHeight() + 5.0;
370     }
371     
372     if(!omitDetails && !(aLabel.getText().length() == 0 && mLabel.getText().length() == 0))
373     {
374       gfx.setColor(getLineColor());
375       gfx.drawLine((int)x+1,(int)(y+yoff),(int)(x+width-1.0),(int)(y+yoff));
376       yoff += 3.0;
377       final SmartNodeLabelModel aLabelModel = new SmartNodeLabelModel();
378       // Create a specific model parameter which aligns the node's and the label's left sides horizontally
379       // (nodeRatioX and labelRatioX are -0.5) and the upper sides of node and label vertically. (nodeRatioY
380       // and labelRatioY are -0.5). In y-direction an offset is added (yoff) to to move the label to a
381       // position with some distance to the border of the node or to the previous label respectively. In
382       // x-direction the offset (3.0) is set so keep a distance to the border of the node.
383       aLabel.setModelParameter(aLabelModel.createSpecificModelParameter(-0.5, -0.5, -0.5, -0.5, 3.0, yoff, 0, -1));
384       aLabel.paint(gfx);
385       yoff += aLabel.getHeight() + 3.0;
386       gfx.drawLine((int)x+1,(int)(y+yoff),(int)(x+width-1.0),(int)(y+yoff));
387       yoff += 3.0;
388       final SmartNodeLabelModel mLabelModel = new SmartNodeLabelModel();
389       mLabel.setModelParameter(mLabelModel.createSpecificModelParameter(-0.5, -0.5, -0.5, -0.5, 3.0, yoff, 0, -1));
390       mLabel.paint(gfx);
391     }
392     
393     if(clipContent)
394     {
395       gfx.setClip(oldClip);
396     }
397   }
398   
399   //////////////////////////////////////////////////////////////////////////////
400   // SERIALIZATION /////////////////////////////////////////////////////////////
401   //////////////////////////////////////////////////////////////////////////////
402   
403   /**
404    * Serialization routine that allows this realizer to be written out
405    * in YGF graph format.
406    * @deprecated Use a custom {@link y.io.graphml.graph2d.NodeRealizerSerializer}
407    * for serialization to the {@link y.io.GraphMLIOHandler GraphML format}
408    * instead.
409    */
410   public void write(ObjectOutputStream out) throws IOException 
411   {
412     out.writeByte(YVersion.VERSION_1);
413     super.write(out);
414     aLabel.write( out );
415     mLabel.write( out );
416     out.writeBoolean(clipContent);
417     out.writeBoolean(omitDetails);
418     out.writeObject(getStereotype());
419     out.writeObject(getConstraint());
420   }
421   
422   
423   /**
424    * Deserialization routine that allows this realizer to be read in
425    * from YGF graph format.
426    * @deprecated Use a custom {@link y.io.graphml.graph2d.NodeRealizerSerializer}
427    * for serialization to the {@link y.io.GraphMLIOHandler GraphML format}
428    * instead.
429    */
430   public void read(ObjectInputStream in) throws IOException, ClassNotFoundException 
431   {
432     switch(in.readByte()) {
433     case YVersion.VERSION_1:
434       super.read(in);
435       init();
436       aLabel.read(in);
437       mLabel.read(in);
438       clipContent = in.readBoolean();
439       omitDetails = in.readBoolean();
440       stereotype = (String)in.readObject();
441       constraint = (String)in.readObject();
442       break;
443     default:
444       D.fatal("Unsupported Format");
445     }
446   }
447 
448 
449 
450   public static void addContentTo( final JRootPane rootPane )
451   {
452     final UMLClassNodeRealizer r = new UMLClassNodeRealizer();
453     r.setClassName("com.mycompany.MyClass");
454     r.setConstraint("abstract");
455     r.setStereotype("factory");
456     r.addAttribute("-graph");
457     r.addAttribute("-id");
458     r.addMethod("+setGraph(Graph)");
459     r.addMethod("+getGraph():Graph");
460     r.addMethod("+setID(int)");
461     r.addMethod("+getID():int");
462     r.fitContent();
463     r.setFillColor(new Color(255, 153, 0));
464     final Graph2DView view = new Graph2DView();
465     view.setFitContentOnResize(true);
466     view.getGraph2D().setDefaultNodeRealizer(r.createCopy());
467     view.getGraph2D().createNode();
468     view.addViewMode(new EditMode());
469 
470     rootPane.setContentPane(view);
471   }
472 
473   /**
474    * Launcher method. Execute this class to see a sample instantiation of
475    * this node realizer in action.
476    */
477   public static void main(String[] args)
478   {
479     EventQueue.invokeLater(new Runnable() {
480       public void run() {
481         DemoDefaults.initLnF();
482         final JFrame frame = new JFrame();
483         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
484         addContentTo(frame.getRootPane());
485         frame.pack();
486         frame.setLocationRelativeTo(null);
487         frame.setVisible(true);
488       }
489     });
490   }
491 }
492 
493