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: }