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}