001package horstmann.ch08_umleditor;
002import java.awt.geom.Point2D;
003import java.awt.geom.Rectangle2D;
004import java.awt.geom.RectangularShape;
005import java.io.IOException;
006import java.io.ObjectInputStream;
007import java.io.ObjectOutputStream;
008
009/**
010   A node that has a rectangular shape.
011 */
012@SuppressWarnings("serial")
013public abstract class RectangularNode implements Node
014{
015        public Object clone()
016        {
017                try
018                {
019                        RectangularNode cloned = (RectangularNode) super.clone();
020                        cloned.bounds = (Rectangle2D) bounds.clone();
021                        return cloned;
022                }
023                catch (CloneNotSupportedException exception)
024                {
025                        return null;
026                }
027        }
028
029        public void translate(double dx, double dy)
030        {
031                bounds.setFrame(bounds.getX() + dx,
032                                bounds.getY() + dy,
033                                bounds.getWidth(),
034                                bounds.getHeight());
035        }
036
037        public boolean contains(Point2D p)
038        {
039                return bounds.contains(p);
040        }
041
042        public Rectangle2D getBounds()
043        {
044                return (Rectangle2D) bounds.clone();
045        }
046
047        public void setBounds(Rectangle2D newBounds)
048        {
049                bounds = newBounds;
050        }
051
052        public Point2D getConnectionPoint(Point2D aPoint)
053        {
054                double slope = bounds.getHeight() / bounds.getWidth();
055                double x = bounds.getCenterX();
056                double y = bounds.getCenterY();
057                double ex = aPoint.getX() - x;
058                double ey = aPoint.getY() - y;
059
060                if (ex != 0 && -slope <= ey / ex && ey / ex <= slope)
061                {
062                        // intersects at left or right boundary
063                        if (ex > 0)
064                        {
065                                x = bounds.getMaxX();
066                                y += (bounds.getWidth() / 2) * ey / ex;
067                        }
068                        else
069                        {
070                                x = bounds.getX();
071                                y -= (bounds.getWidth() / 2) * ey / ex;
072                        }
073                }
074                else if (ey != 0)
075                {
076                        // intersects at top or bottom
077                        if (ey > 0)
078                        {
079                                x += (bounds.getHeight() / 2) * ex / ey;
080                                y = bounds.getMaxY();
081                        }
082                        else
083                        {
084                                x -= (bounds.getHeight() / 2) * ex / ey;
085                                y = bounds.getY();
086                        }
087                }
088                return new Point2D.Double(x, y);
089        }
090
091        private void writeObject(ObjectOutputStream out)
092                        throws IOException
093        {
094                out.defaultWriteObject();
095                writeRectangularShape(out, bounds);
096        }
097
098        /**
099      A helper method to overcome the problem that the 2D shapes
100      aren't serializable. It writes x, y, width and height
101      to the stream.
102      @param out the stream
103      @param s the shape
104         */
105        private static void writeRectangularShape(
106                        ObjectOutputStream out,
107                        RectangularShape s)
108                                        throws IOException
109        {
110                out.writeDouble(s.getX());
111                out.writeDouble(s.getY());
112                out.writeDouble(s.getWidth());
113                out.writeDouble(s.getHeight());
114        }
115
116        private void readObject(ObjectInputStream in)
117                        throws IOException, ClassNotFoundException
118        {
119                in.defaultReadObject();
120                bounds = new Rectangle2D.Double();
121                readRectangularShape(in, bounds);
122        }
123
124        /**
125      A helper method to overcome the problem that the 2D shapes
126      aren't serializable. It reads x, y, width and height
127      from the stream.
128      @param in the stream
129      @param s the shape whose frame is set from the stream values
130         */
131        private static void readRectangularShape(ObjectInputStream in,
132                        RectangularShape s)
133                                        throws IOException
134        {
135                double x = in.readDouble();
136                double y = in.readDouble();
137                double width = in.readDouble();
138                double height = in.readDouble();
139                s.setFrame(x, y, width, height);
140        }
141
142        private transient Rectangle2D bounds;
143}