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.entityrelationship.painters;
15  import y.base.YList;
16  import y.geom.OrientedRectangle;
17  import y.geom.YDimension;
18  import y.geom.YRectangle;
19  import y.io.graphml.NamespaceConstants;
20  import y.io.graphml.input.DeserializationEvent;
21  import y.io.graphml.input.DeserializationHandler;
22  import y.io.graphml.input.GraphMLParseException;
23  import y.io.graphml.output.GraphMLWriteException;
24  import y.io.graphml.output.SerializationEvent;
25  import y.io.graphml.output.SerializationHandler;
26  import y.io.graphml.output.XmlWriter;
27  import y.layout.NodeLabelCandidate;
28  import y.layout.NodeLabelLayout;
29  import y.layout.NodeLabelModel;
30  import y.layout.NodeLayout;
31  import y.view.NodeLabel;
32  import y.view.NodeRealizer;
33  
34  /**
35   * This label model computes the size and placement information for the
36   * attributes label of an ERD entity node.
37   * The area of the attributes label starts at the separator line and fills
38   * the whole lower compartment.
39   */
40  public class ErdAttributesNodeLabelModel implements NodeLabelModel {
41  
42    /** The horizontal distance of the label to the border of the node */
43    private static final double OFFSET_X = 2;
44    /** The minimal vertical distance of the label to the separator line */
45    private static final double MIN_OFFSET_Y = 2;
46    /**
47     *  The model parameter that contains information about a specific label.
48     *  In this case it is an empty class because there is only one possible position
49     *  for the attributes label.
50     */
51    private static final ErdAttributesNodeLabelModelParameter PARAMETER =
52            new ErdAttributesNodeLabelModelParameter();
53  
54    /**
55     * Returns the default parameter of this label model.
56     * @return the default parameter
57     */
58    public Object getDefaultParameter() {
59      return PARAMETER;
60    }
61  
62    /**
63     * Returns the oriented label position and bounds encoded by the given model
64     * parameter.
65     *
66     * @param labelSize The size of the label that should be placed.
67     * @param nodeLayout The layout of the node to which the label belongs.
68     * @param param The model parameter that describes the abstract position of
69     * the label within this model. The parameter must have been generated by
70     * this model.
71     *
72     * @return the oriented label position and bounds.
73     */
74    public OrientedRectangle getLabelPlacement(
75            final YDimension labelSize,
76            final NodeLayout nodeLayout,
77            final Object param
78    ) {
79      final YRectangle tmp1 = getLabelBox(labelSize, nodeLayout);
80      return new OrientedRectangle(tmp1);
81    }
82  
83    /**
84     * Returns a list of candidate positions for the specified node and label data.
85     *
86     * @param nl The label layout for which candidates should be generated.
87     * @param nodeLayout The layout of the node to which the label belongs.
88     * @return a list of candidate positions for the specified node and label data.
89     */
90    public YList getLabelCandidates(
91            final NodeLabelLayout nl,
92            final NodeLayout nodeLayout
93    ) {
94      final YList candidates = new YList();
95      final YRectangle box = getLabelBox(nl.getBox(), nodeLayout);
96      candidates.add(new NodeLabelCandidate(
97              box.getLocation(),
98              box,
99              PARAMETER,
100             nl,
101             true));
102     return candidates;
103   }
104 
105   /**
106    * Creates a model parameter that represents the given node label context best
107    * within this model.
108    * The created model parameter represents the closest parameter representation
109    * of the given oriented label bounds that can be achieved within this model.
110    *
111    * @param labelBounds
112    * The bounds of the label for which a parameter representation is sought.
113    * @param nodeLayout
114    * The layout of the node to which the label belongs.
115    *
116    * @return  the default parameter
117    */
118   public Object createModelParameter(
119           final OrientedRectangle labelBounds,
120           final NodeLayout nodeLayout
121   ) {
122     return PARAMETER;
123   }
124 
125 
126   /**
127    * Computes the size and position of the attributes label.
128    * @param labelSize The size of the label
129    * @param nodeLayout The layout of the node
130    * @return a <code>YRectangle</code> that defines the area of the label box
131    */
132   private static YRectangle getLabelBox(
133           final YDimension labelSize,
134           final NodeLayout nodeLayout
135   ) {
136     if (nodeLayout instanceof NodeRealizer) {
137       //if ERD style is used, compute the borders of the area under the separator
138       final NodeRealizer nr = (NodeRealizer) nodeLayout;
139       if (ErdNodePainter.useErdStyle(nr)) {
140         final NodeLabel nl = nr.getLabel();
141         final double sy = ErdNodePainter.separator(nl);
142 
143         final double offsetY = Math.max(MIN_OFFSET_Y, nl.getDistance());
144 
145         final double x = nr.getX();
146 
147         final double minX = x + OFFSET_X;
148         final double minY = sy + offsetY;
149 
150         return new YRectangle(
151                 minX,
152                 minY,
153                 Math.max(labelSize.width, x + nr.getWidth() - OFFSET_X - minX),
154                 Math.max(labelSize.height, nr.getY() + nr.getHeight() - offsetY - minY));
155       }
156     }
157 
158     // if no ERD style is used return a default box
159     return getDefaultBox(labelSize, nodeLayout);
160   }
161 
162   /**
163    * Computes a default size and position for the label.
164    * @param labelSize the size of the label
165    * @param nodeLayout the layout of the node
166    * @return a <code>YRectangle</code> that defines the area of the label box
167    */
168   private static YRectangle getDefaultBox(
169           final YDimension labelSize,
170           final NodeLayout nodeLayout
171   ) {
172     final double w = labelSize.width;
173     final double h = labelSize.height;
174     return new YRectangle(
175             nodeLayout.getX() + (nodeLayout.getWidth() - w) * 0.5,
176             nodeLayout.getY() + (nodeLayout.getHeight() - h) * 0.5,
177             w,
178             h);
179   }
180 
181   /**
182    * This parameter is used to provide information for a specific label.
183    * As the attributes label always fills the whole lower compartment of the
184    * entity node, this parameter has no function. However, it is needed to proper
185    * serialization/deserialization.
186    */
187   private static final class ErdAttributesNodeLabelModelParameter {
188   }
189 
190 
191   /**
192    * This handler provides a serialization/deserialization functionality for the
193    * nodes in ERD style.
194    *
195    * It makes sure that the attributes label can be loaded/stored from/to GraphML properly.
196    */
197   public static final class Handler
198           implements SerializationHandler, DeserializationHandler {
199 
200     /** The GraphML element name for the ERD label model */
201     private static final String MODEL_NAME = "ErdAttributesNodeLabelModel";
202     /** The GraphML element name for the parameter of the ERD label model */
203     private static final String PARAM_NAME = "ErdAttributesNodeLabelModelParameter";
204 
205     /**
206      * Handles the deserialization of the ERD attributes label.
207      * @param event an event that contains all data that is needed for deserialization.
208      * @throws GraphMLParseException
209      */
210     public void onHandleDeserialization(
211             final DeserializationEvent event
212     ) throws GraphMLParseException {
213       final org.w3c.dom.Node node = event.getXmlNode();
214       if (node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE &&
215           NamespaceConstants.YFILES_JAVA_NS.equals(node.getNamespaceURI())) {
216         final String ln = node.getLocalName();
217         if (MODEL_NAME.equals(ln)) {
218           event.setResult(new ErdAttributesNodeLabelModel());
219         } else if (PARAM_NAME.equals(ln)) {
220           event.setResult(PARAMETER);
221         }
222       }
223     }
224 
225     /**
226      * Handles the serialization of the ERD attributes label.
227      * @param event an event that contains all data that is needed for serialization.
228      * @throws GraphMLWriteException
229      */
230     public void onHandleSerialization(
231             final SerializationEvent event
232     ) throws GraphMLWriteException {
233       final Object item = event.getItem();
234       if (item instanceof ErdAttributesNodeLabelModel) {
235         final XmlWriter writer = event.getWriter();
236         writer.writeStartElement(MODEL_NAME, NamespaceConstants.YFILES_JAVA_NS);
237         writer.writeEndElement();
238         event.setHandled(true);
239       } else if (item == PARAMETER) {
240         final XmlWriter writer = event.getWriter();
241         writer.writeStartElement(PARAM_NAME, NamespaceConstants.YFILES_JAVA_NS);
242         writer.writeEndElement();
243         event.setHandled(true);
244       }
245     }
246   }
247 }
248