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