1
28 package demo.view.advanced.ports;
29
30 import y.geom.YPoint;
31 import y.io.graphml.NamespaceConstants;
32 import y.io.graphml.input.DeserializationEvent;
33 import y.io.graphml.input.DeserializationHandler;
34 import y.io.graphml.input.GraphMLParseException;
35 import y.io.graphml.output.GraphMLWriteException;
36 import y.io.graphml.output.SerializationEvent;
37 import y.io.graphml.output.SerializationHandler;
38 import y.io.graphml.output.XmlWriter;
39 import y.view.NodePort;
40 import y.view.NodeRealizer;
41 import y.view.PortLocationModel;
42 import y.view.PortLocationModelParameter;
43
44 import java.util.StringTokenizer;
45 import org.w3c.dom.NamedNodeMap;
46 import org.w3c.dom.Node;
47
48
56 public class SidePortLocationModel implements PortLocationModel {
57
63 public static final byte SIDE_TOP = 1;
70 public static final byte SIDE_LEFT = 2;
77 public static final byte SIDE_BOTTOM = 4;
84 public static final byte SIDE_RIGHT = 8;
86 private static final byte SIDE_ALL = SIDE_TOP|SIDE_LEFT|SIDE_BOTTOM|SIDE_RIGHT;
87
88
89 private final int sides;
90
91 private SidePortLocationModel( final int sides ) {
92 this.sides = sides;
93 }
94
95
110 public PortLocationModelParameter createParameter(
111 final NodeRealizer owner,
112 final YPoint location
113 ) {
114 if (owner == null) {
115 return new Parameter(this, -0.5, -0.5);
116 } else {
117 final double x = owner.getX();
118 final double w = owner.getWidth();
119 final double y = owner.getY();
120 final double h = owner.getHeight();
121
122 YPoint result = null;
123 double dist = Double.POSITIVE_INFINITY;
124 final byte[] s = {SIDE_TOP, SIDE_LEFT, SIDE_BOTTOM, SIDE_RIGHT};
125 for (int i = 0; i < s.length; ++i) {
126 if ((sides & s[i]) == s[i]) {
127 final YPoint p = calculateSideLocation(x, y, w, h, location, s[i]);
128 final double d = distSquared(p, location);
129 if (dist > d) {
130 dist = d;
131 result = p;
132 }
133 }
134 }
135
136 return createParameterImpl(x, y, w, h, result);
138 }
139 }
140
141
152 private PortLocationModelParameter createParameterImpl(
153 final double x,
154 final double y,
155 final double w,
156 final double h,
157 final YPoint location
158 ) {
159 final double rx;
160 if (w > 0) {
161 rx = (location.getX() - x - w*0.5) / w;
162 } else {
163 rx = 0;
164 }
165
166 final double ry;
167 if (h > 0) {
168 ry = (location.getY() - y - h*0.5) / h;
169 } else {
170 ry = 0;
171 }
172
173 return new Parameter(this, rx, ry);
174 }
175
176
184 public YPoint getLocation(
185 final NodePort port,
186 final PortLocationModelParameter parameter
187 ) {
188 final Parameter p = (Parameter) parameter;
189 final NodeRealizer nr = port.getRealizer();
190 return new YPoint(
191 nr.getCenterX() + p.ratioX * nr.getWidth(),
192 nr.getCenterY() + p.ratioY * nr.getHeight());
193 }
194
195
201 public int getSides() {
202 return sides;
203 }
204
205
206
214 public static SidePortLocationModel newInstance( final int sides ) {
215 if ((sides & SIDE_ALL) == 0) {
216 throw new IllegalArgumentException("Unsupported sides mask: " + sides);
217 }
218
219 return new SidePortLocationModel(sides);
220 }
221
222
237 private static YPoint calculateSideLocation(
238 final double x,
239 final double y,
240 final double w,
241 final double h,
242 final YPoint location,
243 final byte side
244 ) {
245 if (side == SIDE_TOP || side == SIDE_BOTTOM) {
246 double lx = location.getX();
247 if (lx < x) {
248 lx = x;
249 } else if (lx > x + w) {
250 lx = x + w;
251 }
252 final double ly = side == SIDE_TOP ? y : y + h;
253 return new YPoint(lx, ly);
254 } else { final double lx = side == SIDE_LEFT ? x : x + w;
256 double ly = location.getY();
257 if (ly < y) {
258 ly = y;
259 } else if (ly > y + h) {
260 ly = y + h;
261 }
262 return new YPoint(lx, ly);
263 }
264 }
265
266
272 private static double distSquared( final YPoint p1, final YPoint p2 ) {
273 final double dx = p1.getX() - p2.getX();
274 final double dy = p1.getY() - p2.getY();
275 return dx*dx + dy*dy;
276 }
277
278
279
287 static final class Parameter implements PortLocationModelParameter {
288
289 private final SidePortLocationModel model;
290
291 private final double ratioX;
292
293 private final double ratioY;
294
295
301 Parameter(
302 final SidePortLocationModel model,
303 final double ratioX,
304 final double ratioY
305 ) {
306 this.model = model;
307 this.ratioX = ratioX;
308 this.ratioY = ratioY;
309 }
310
311 public PortLocationModel getModel() {
312 return model;
313 }
314 }
315
316
317
321 public static final class Handler implements SerializationHandler, DeserializationHandler {
322 private static final String NS_NAME = "demo";
323 private static final String MODEL_NODE_NAME = "SidePortLocationModel";
324
325
326
330 public void onHandleSerialization(
331 final SerializationEvent event
332 ) throws GraphMLWriteException {
333 final Object item = event.getItem();
334 if (item instanceof Parameter) {
335 final Parameter param = (Parameter) item;
336 final XmlWriter writer = event.getWriter();
337 writer.writeStartElement(MODEL_NODE_NAME, NS_NAME);
338 writer.writeAttribute("sides", sidesToString(param));
339 writer.writeAttribute("ratioX", param.ratioX);
340 writer.writeAttribute("ratioY", param.ratioY);
341 writer.writeEndElement();
342 event.setHandled(true);
343 }
344 }
345
346 private static String sidesToString( final Parameter p ) {
347 final int sides = ((SidePortLocationModel) p.getModel()).getSides();
348 final StringBuffer sb = new StringBuffer();
349 String del = "";
350 if ((sides & SIDE_TOP) == SIDE_TOP) {
351 sb.append(del).append("SIDE_TOP");
352 del = "|";
353 }
354 if ((sides & SIDE_LEFT) == SIDE_LEFT) {
355 sb.append(del).append("SIDE_LEFT");
356 del = "|";
357 }
358 if ((sides & SIDE_BOTTOM) == SIDE_BOTTOM) {
359 sb.append(del).append("SIDE_BOTTOM");
360 del = "|";
361 }
362 if ((sides & SIDE_RIGHT) == SIDE_RIGHT) {
363 sb.append(del).append("SIDE_RIGHT");
364 del = "|";
365 }
366 return sb.toString();
367 }
368
369
375 public void onHandleDeserialization(
376 final DeserializationEvent event
377 ) throws GraphMLParseException {
378 final Node node = event.getXmlNode();
379 if (isNamespaceElement(node, NamespaceConstants.YFILES_JAVA_NS)) {
380 for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) {
381 if (isNamespaceElement(child, NS_NAME) &&
382 MODEL_NODE_NAME.equals(child.getLocalName())) {
383 final NamedNodeMap attrs = child.getAttributes();
384
385 double ratioX = 0;
386 final Node rxAttr = attrs.getNamedItem("ratioX");
387 if (rxAttr == null) {
388 throw new GraphMLParseException(
389 "Missing attribute ratioX for element " +
390 MODEL_NODE_NAME + ".");
391 } else {
392 ratioX = Double.parseDouble(rxAttr.getNodeValue());
393 }
394 double ratioY = 0;
395 final Node ryAttr = attrs.getNamedItem("ratioY");
396 if (ryAttr == null) {
397 throw new GraphMLParseException(
398 "Missing attribute ratioY for element " +
399 MODEL_NODE_NAME + ".");
400 } else {
401 ratioY = Double.parseDouble(ryAttr.getNodeValue());
402 }
403
404 int sides = 0;
405 final Node sAttr = attrs.getNamedItem("sides");
406 if (sAttr == null) {
407 throw new GraphMLParseException(
408 "Missing attribute sides for element " +
409 MODEL_NODE_NAME + ".");
410 } else {
411 sides = stringToSides(sAttr.getNodeValue().toUpperCase());
412 if ((sides & SIDE_ALL) == 0) {
413 throw new GraphMLParseException("Unsupported sides mask: " + sides);
414 }
415 }
416
417 event.setResult(new Parameter(new SidePortLocationModel(sides), ratioX, ratioY));
418 break;
419 }
420 }
421 }
422 }
423
424 private static int stringToSides( final String value ) {
425 int sides = 0;
426 for (StringTokenizer st = new StringTokenizer(value, "|"); st.hasMoreTokens();) {
427 final String token = st.nextToken().trim();
428 if ("SIDE_TOP".equals(token)) {
429 sides |= SIDE_TOP;
430 } else if ("SIDE_LEFT".equals(token)) {
431 sides |= SIDE_LEFT;
432 } else if ("SIDE_BOTTOM".equals(token)) {
433 sides |= SIDE_BOTTOM;
434 } else if ("SIDE_RIGHT".equals(token)) {
435 sides |= SIDE_RIGHT;
436 } else {
437 throw new IllegalArgumentException("Unsupported side value: " + token);
438 }
439 }
440 return sides;
441 }
442
443 private static boolean isNamespaceElement( final Node node, final String ns ) {
444 return node.getNodeType() == Node.ELEMENT_NODE &&
445 ns.equals(node.getNamespaceURI());
446 }
447 }
448 }
449