001package horstmann.ch08_umleditor;
002import java.awt.Color;
003import java.awt.Dimension;
004import java.awt.Graphics2D;
005import java.awt.Shape;
006import java.awt.Stroke;
007import java.awt.geom.GeneralPath;
008import java.awt.geom.Line2D;
009import java.awt.geom.Point2D;
010import java.awt.geom.Rectangle2D;
011import java.util.ArrayList;
012
013import javax.swing.JLabel;
014
015/**
016   An edge that is composed of multiple line segments
017 */
018@SuppressWarnings("all")
019public abstract class SegmentedLineEdge extends ShapeEdge
020{
021        /**
022      Costructs an edge with no adornments.
023         */
024        public SegmentedLineEdge()
025        {
026                lineStyle = LineStyle.SOLID;
027                startArrowHead = ArrowHead.NONE;
028                endArrowHead = ArrowHead.NONE;
029                startLabel = "";
030                middleLabel = "";
031                endLabel = "";
032        }
033
034        /**
035      Sets the line style property.
036      @param newValue the new value
037         */
038        public void setLineStyle(LineStyle newValue) { lineStyle = newValue; }
039
040        /**
041      Gets the line style property.
042      @return the line style
043         */
044        public LineStyle getLineStyle() { return lineStyle; }
045
046        /**
047      Sets the start arrow head property
048      @param newValue the new value
049         */
050        public void setStartArrowHead(ArrowHead newValue) { startArrowHead = newValue; }
051
052        /**
053      Gets the start arrow head property
054      @return the start arrow head style
055         */
056        public ArrowHead getStartArrowHead() { return startArrowHead; }
057
058        /**
059      Sets the end arrow head property
060      @param newValue the new value
061         */
062        public void setEndArrowHead(ArrowHead newValue) { endArrowHead = newValue; }
063
064        /**
065      Gets the end arrow head property
066      @return the end arrow head style
067         */
068        public ArrowHead getEndArrowHead() { return endArrowHead; }
069
070        /**
071      Sets the start label property
072      @param newValue the new value
073         */
074        public void setStartLabel(String newValue) { startLabel = newValue; }
075
076        /**
077      Gets the start label property
078      @return the label at the start of the edge
079         */
080        public String getStartLabel() { return startLabel; }
081
082        /**
083      Sets the middle label property
084      @param newValue the new value
085         */
086        public void setMiddleLabel(String newValue) { middleLabel = newValue; }
087
088        /**
089      Gets the middle label property
090      @return the label at the middle of the edge
091         */
092        public String getMiddleLabel() { return middleLabel; }
093
094        /**
095      Sets the end label property
096      @param newValue the new value
097         */
098        public void setEndLabel(String newValue) { endLabel = newValue; }
099
100        /**
101      Gets the end label property
102      @return the label at the end of the edge
103         */
104        public String getEndLabel() { return endLabel; }
105
106        /**
107      Draws the edge.
108      @param g2 the graphics context
109         */
110        public void draw(Graphics2D g2)
111        {
112                ArrayList points = getPoints();
113
114                Stroke oldStroke = g2.getStroke();
115                g2.setStroke(lineStyle.getStroke());
116                g2.draw(getSegmentPath());
117                g2.setStroke(oldStroke);
118                startArrowHead.draw(g2, (Point2D)points.get(1),
119                                (Point2D)points.get(0));
120                endArrowHead.draw(g2, (Point2D)points.get(points.size() - 2),
121                                (Point2D)points.get(points.size() - 1));
122
123                drawString(g2, (Point2D)points.get(1), (Point2D)points.get(0),
124                                startArrowHead, startLabel, false);
125                drawString(g2, (Point2D)points.get(points.size() / 2 - 1),
126                                (Point2D)points.get(points.size() / 2),
127                                null, middleLabel, true);
128                drawString(g2, (Point2D)points.get(points.size() - 2),
129                                (Point2D)points.get(points.size() - 1),
130                                endArrowHead, endLabel, false);
131        }
132
133        /**
134      Draws a string.
135      @param g2 the graphics context
136      @param p an endpoint of the segment along which to
137      draw the string
138      @param q the other endpoint of the segment along which to
139      draw the string
140      @param s the string to draw
141      @param center true if the string should be centered
142      along the segment
143         */
144        private static void drawString(Graphics2D g2,
145                        Point2D p, Point2D q, ArrowHead arrow, String s, boolean center)
146        {
147                if (s == null || s.length() == 0) return;
148                label.setText("<html>" + s + "</html>");
149                label.setFont(g2.getFont());
150                Dimension d = label.getPreferredSize();
151                label.setBounds(0, 0, d.width, d.height);
152
153                Rectangle2D b = getStringBounds(g2, p, q, arrow, s, center);
154
155                Color oldColor = g2.getColor();
156                g2.setColor(g2.getBackground());
157                g2.fill(b);
158                g2.setColor(oldColor);
159
160                g2.translate(b.getX(), b.getY());
161                label.paint(g2);
162                g2.translate(-b.getX(), -b.getY());
163        }
164
165        /**
166      Computes the attachment point for drawing a string.
167      @param g2 the graphics context
168      @param p an endpoint of the segment along which to
169      draw the string
170      @param q the other endpoint of the segment along which to
171      draw the string
172      @param d the bounds of the string to draw
173      @param center true if the string should be centered
174      along the segment
175      @return the point at which to draw the string
176         */
177        private static Point2D getAttachmentPoint(Graphics2D g2,
178                        Point2D p, Point2D q, ArrowHead arrow, Dimension d, boolean center)
179        {
180                final int GAP = 3;
181                double xoff = GAP;
182                double yoff = -GAP - d.getHeight();
183                Point2D attach = q;
184                if (center)
185                {
186                        if (p.getX() > q.getX())
187                        {
188                                return getAttachmentPoint(g2, q, p, arrow, d, center);
189                        }
190                        attach = new Point2D.Double((p.getX() + q.getX()) / 2,
191                                        (p.getY() + q.getY()) / 2);
192                        if (p.getY() < q.getY())
193                                yoff =  - GAP - d.getHeight();
194                        else if (p.getY() == q.getY())
195                                xoff = -d.getWidth() / 2;
196                        else
197                                yoff = GAP;
198                }
199                else
200                {
201                        if (p.getX() < q.getX())
202                        {
203                                xoff = -GAP - d.getWidth();
204                        }
205                        if (p.getY() > q.getY())
206                        {
207                                yoff = GAP;
208                        }
209                        if (arrow != null)
210                        {
211                                Rectangle2D arrowBounds = arrow.getPath(p, q).getBounds2D();
212                                if (p.getX() < q.getX())
213                                {
214                                        xoff -= arrowBounds.getWidth();
215                                }
216                                else
217                                {
218                                        xoff += arrowBounds.getWidth();
219                                }
220                        }
221                }
222                return new Point2D.Double(attach.getX() + xoff, attach.getY() + yoff);
223        }
224
225        /**
226      Computes the extent of a string that is drawn along a line segment.
227      @param g2 the graphics context
228      @param p an endpoint of the segment along which to
229      draw the string
230      @param q the other endpoint of the segment along which to
231      draw the string
232      @param s the string to draw
233      @param center true if the string should be centered
234      along the segment
235      @return the rectangle enclosing the string
236         */
237        private static Rectangle2D getStringBounds(Graphics2D g2,
238                        Point2D p, Point2D q, ArrowHead arrow, String s, boolean center)
239        {
240                if (g2 == null) return new Rectangle2D.Double();
241                if (s == null || s.equals("")) return new Rectangle2D.Double(q.getX(), q.getY(), 0, 0);
242                label.setText("<html>" + s + "</html>");
243                label.setFont(g2.getFont());
244                Dimension d = label.getPreferredSize();
245                Point2D a = getAttachmentPoint(g2, p, q, arrow, d, center);
246                return new Rectangle2D.Double(a.getX(), a.getY(), d.getWidth(), d.getHeight());
247        }
248
249        public Rectangle2D getBounds(Graphics2D g2)
250        {
251                ArrayList points = getPoints();
252                Rectangle2D r = super.getBounds(g2);
253                r.add(getStringBounds(g2,
254                                (Point2D)points.get(1), (Point2D)points.get(0),
255                                startArrowHead, startLabel, false));
256                r.add(getStringBounds(g2,
257                                (Point2D)points.get(points.size() / 2 - 1),
258                                (Point2D)points.get(points.size() / 2),
259                                null, middleLabel, true));
260                r.add(getStringBounds(g2,
261                                (Point2D)points.get(points.size() - 2),
262                                (Point2D)points.get(points.size() - 1),
263                                endArrowHead, endLabel, false));
264                return r;
265        }
266
267        public Shape getShape()
268        {
269                GeneralPath path = getSegmentPath();
270                ArrayList points = getPoints();
271                path.append(startArrowHead.getPath((Point2D)points.get(1),
272                                (Point2D)points.get(0)), false);
273                path.append(endArrowHead.getPath((Point2D)points.get(points.size() - 2),
274                                (Point2D)points.get(points.size() - 1)), false);
275                return path;
276        }
277
278        private GeneralPath getSegmentPath()
279        {
280                ArrayList points = getPoints();
281
282                GeneralPath path = new GeneralPath();
283                Point2D p = (Point2D) points.get(points.size() - 1);
284                path.moveTo((float) p.getX(), (float) p.getY());
285                for (int i = points.size() - 2; i >= 0; i--)
286                {
287                        p = (Point2D) points.get(i);
288                        path.lineTo((float) p.getX(), (float) p.getY());
289                }
290                return path;
291        }
292
293        public Line2D getConnectionPoints()
294        {
295                ArrayList points = getPoints();
296                return new Line2D.Double((Point2D) points.get(0),
297                                (Point2D) points.get(points.size() - 1));
298        }
299
300        /**
301      Gets the corner points of this segmented line edge
302      @return an array list of Point2D objects, containing
303      the corner points
304         */
305        public abstract ArrayList getPoints();
306
307        private LineStyle lineStyle;
308        private ArrowHead startArrowHead;
309        private ArrowHead endArrowHead;
310        private String startLabel;
311        private String middleLabel;
312        private String endLabel;
313
314        private static JLabel label = new JLabel();
315}