1:
31:
32: package ;
33:
34: import ;
35: import ;
36: import ;
37: import ;
38: import ;
39: import ;
40: import ;
41: import ;
42: import ;
43: import ;
44: import ;
45: import ;
46: import ;
47: import ;
48:
49: import ;
50: import ;
51: import ;
52: import ;
53: import ;
54: import ;
55: import ;
56: import ;
57:
58:
65: public class SurveyScale implements Drawable, Serializable
66: {
67: private static final Number[] EMPTY_VALUES = new Number[0];
68:
69:
70: private int lowest;
71:
72:
73: private int highest;
74:
75:
76: private double lowerMargin = 0.10;
77:
78:
79: private double upperMargin = 0.10;
80:
81:
82: private BooleanList fillShapes;
83:
84:
85: private Number[] values;
86:
87:
88: private Number rangeLowerBound;
89:
90:
91: private Number rangeUpperBound;
92:
93:
94: private boolean drawBorder = false;
95:
96:
97: private boolean drawTickMarks;
98:
99:
100: private boolean drawScaleValues = false;
101:
102:
103: private Font scaleValueFont;
104:
105:
106: private transient Paint scaleValuePaint;
107:
108:
109: private transient Paint rangePaint;
110:
111:
112: private transient ShapeList shapes;
113:
114:
115: private transient Paint fillPaint;
116:
117:
118: private transient Stroke outlineStroke;
119:
120:
124: private transient Shape defaultShape;
125:
126:
127: private transient Paint tickMarkPaint;
128:
129: private transient Paint borderPaint;
130:
131: private int range;
132: private double lowerBound;
133: private double upperBound;
134:
135:
136: public SurveyScale()
137: {
138: this(1, 5, EMPTY_VALUES);
139: }
140:
141:
148: public SurveyScale(final int lowest, final int highest,
149: final Number[] values)
150: {
151:
152: this.lowest = lowest;
153: this.highest = highest;
154: if (values == null)
155: {
156: this.values = EMPTY_VALUES;
157: }
158: else
159: {
160: this.values = (Number[]) values.clone();
161: }
162:
163: this.drawTickMarks = true;
164: this.tickMarkPaint = Color.gray;
165:
166: this.scaleValueFont = new Font("Serif", Font.ITALIC, 10);
167: this.scaleValuePaint = Color.black;
168: this.defaultShape = new Ellipse2D.Double(-3.0, -3.0, 6.0, 6.0);
169:
170: this.rangeLowerBound = null;
171: this.rangeUpperBound = null;
172: this.rangePaint = Color.lightGray;
173:
174: this.shapes = createShapeList();
175: this.fillShapes = new BooleanList();
176: this.fillShapes.setBoolean(0, Boolean.TRUE);
177:
178: this.fillPaint = Color.black;
179: this.outlineStroke = new BasicStroke(0.5f);
180: recompute();
181: }
182:
183: public int getLowest()
184: {
185: return lowest;
186: }
187:
188: public void setLowest(final int lowest)
189: {
190: this.lowest = lowest;
191: recompute();
192: }
193:
194: public int getHighest()
195: {
196: return highest;
197: }
198:
199: public void setHighest(final int highest)
200: {
201: this.highest = highest;
202: recompute();
203: }
204:
205:
209: protected void recompute()
210: {
211: this.range = Math.max(0, this.highest - this.lowest);
212: this.lowerBound = this.lowest - (range * this.lowerMargin);
213: this.upperBound = this.highest + (range * this.upperMargin);
214: }
215:
216: protected int getRange()
217: {
218: return range;
219: }
220:
221: protected void setRange(final int range)
222: {
223: this.range = range;
224: }
225:
226: protected double getLowerBound()
227: {
228: return lowerBound;
229: }
230:
231: protected void setLowerBound(final double lowerBound)
232: {
233: this.lowerBound = lowerBound;
234: }
235:
236: protected double getUpperBound()
237: {
238: return upperBound;
239: }
240:
241: protected void setUpperBound(final double upperBound)
242: {
243: this.upperBound = upperBound;
244: }
245:
246:
252: protected ShapeList createShapeList()
253: {
254: final ShapeList shapes = new ShapeList();
255:
256: shapes.setShape(0, new Ellipse2D.Double(-3.0, -3.0, 6.0, 6.0));
257: shapes.setShape(1, ShapeUtilities.createDownTriangle(4.0f));
258: shapes.setShape(2, ShapeUtilities.createUpTriangle(4.0f));
259: shapes.setShape(3, ShapeUtilities.createDiamond(4.0f));
260: shapes.setShape(4, new Rectangle2D.Double(-4.0, -4.0, 8.0, 8.0));
261: shapes.setShape(5, new Ellipse2D.Double(-4.0, -4.0, 8.0, 8.0));
262:
263: return shapes;
264: }
265:
266:
272: public Number getRangeLowerBound()
273: {
274: return this.rangeLowerBound;
275: }
276:
277:
282: public void setRangeLowerBound(final Number bound)
283: {
284: this.rangeLowerBound = bound;
285: }
286:
287:
293: public Number getRangeUpperBound()
294: {
295: return this.rangeUpperBound;
296: }
297:
298:
303: public void setRangeUpperBound(final Number bound)
304: {
305: this.rangeUpperBound = bound;
306: }
307:
308:
314: public boolean isDrawBorder()
315: {
316: return this.drawBorder;
317: }
318:
319:
325: public void setDrawBorder(final boolean flag)
326: {
327: this.drawBorder = flag;
328: }
329:
330:
335: public boolean isDrawTickMarks()
336: {
337: return this.drawTickMarks;
338: }
339:
340:
345: public void setDrawTickMarks(final boolean flag)
346: {
347: this.drawTickMarks = flag;
348: }
349:
350:
355: public boolean isDrawScaleValues()
356: {
357: return this.drawScaleValues;
358: }
359:
360:
365: public void setDrawScaleValues(final boolean flag)
366: {
367: this.drawScaleValues = flag;
368: }
369:
370:
375: public Font getScaleValueFont()
376: {
377: return this.scaleValueFont;
378: }
379:
380:
385: public void setScaleValueFont(final Font font)
386: {
387: if (font == null)
388: {
389: throw new IllegalArgumentException("Null 'font' argument.");
390: }
391: this.scaleValueFont = font;
392: }
393:
394:
399: public Paint getScaleValuePaint()
400: {
401: return this.scaleValuePaint;
402: }
403:
404:
409: public void setScaleValuePaint(final Paint paint)
410: {
411: if (paint == null)
412: {
413: throw new IllegalArgumentException("Null 'paint' argument.");
414: }
415: this.scaleValuePaint = paint;
416: }
417:
418:
424: public Shape getShape(final int index)
425: {
426: return this.shapes.getShape(index);
427: }
428:
429:
435: public void setShape(final int index, final Shape shape)
436: {
437: this.shapes.setShape(index, shape);
438: }
439:
440:
447: public boolean isShapeFilled(final int index)
448: {
449: boolean result = false;
450: final Boolean b = this.fillShapes.getBoolean(index);
451: if (b != null)
452: {
453: result = b.booleanValue();
454: }
455: return result;
456: }
457:
458:
465: public void setShapeFilled(final int index, final boolean fill)
466: {
467: this.fillShapes.setBoolean(index, BooleanUtilities.valueOf(fill));
468: }
469:
470:
475: public Paint getRangePaint()
476: {
477: return this.rangePaint;
478: }
479:
480:
485: public void setRangePaint(final Paint paint)
486: {
487: if (paint == null)
488: {
489: throw new IllegalArgumentException("Null 'paint' argument.");
490: }
491: this.rangePaint = paint;
492: }
493:
494: public Paint getBorderPaint()
495: {
496: return borderPaint;
497: }
498:
499: public void setBorderPaint(final Paint borderPaint)
500: {
501: if (borderPaint == null)
502: {
503: throw new IllegalArgumentException("Null 'paint' argument.");
504: }
505: this.borderPaint = borderPaint;
506: }
507:
508:
514: public Shape getDefaultShape()
515: {
516: return defaultShape;
517: }
518:
519:
525: public void setDefaultShape(final Shape defaultShape)
526: {
527: if (defaultShape == null)
528: {
529: throw new NullPointerException("The default shape must not be null.");
530: }
531: this.defaultShape = defaultShape;
532: }
533:
534: public Paint getTickMarkPaint()
535: {
536: return tickMarkPaint;
537: }
538:
539: public void setTickMarkPaint(final Paint tickMarkPaint)
540: {
541: if (tickMarkPaint == null)
542: {
543: throw new NullPointerException();
544: }
545: this.tickMarkPaint = tickMarkPaint;
546: }
547:
548: public Number[] getValues()
549: {
550: return (Number[]) values.clone();
551: }
552:
553: public Paint getFillPaint()
554: {
555: return fillPaint;
556: }
557:
558: public void setFillPaint(final Paint fillPaint)
559: {
560: if (fillPaint == null)
561: {
562: throw new NullPointerException();
563: }
564: this.fillPaint = fillPaint;
565: }
566:
567: public Stroke getOutlineStroke()
568: {
569: return outlineStroke;
570: }
571:
572: public void setOutlineStroke(final Stroke outlineStroke)
573: {
574: if (outlineStroke == null)
575: {
576: throw new NullPointerException();
577: }
578: this.outlineStroke = outlineStroke;
579: }
580:
581: public double getUpperMargin()
582: {
583: return upperMargin;
584: }
585:
586: public void setUpperMargin(final double upperMargin)
587: {
588: this.upperMargin = upperMargin;
589: }
590:
591: public double getLowerMargin()
592: {
593: return lowerMargin;
594: }
595:
596: public void setLowerMargin(final double lowerMargin)
597: {
598: this.lowerMargin = lowerMargin;
599: }
600:
601:
607: public void draw(final Graphics2D g2, final Rectangle2D area)
608: {
609:
610: if (isDrawBorder())
611: {
612: drawBorder(g2, area);
613: }
614:
615: drawRangeArea(area, g2);
616:
617:
618: if (isDrawTickMarks())
619: {
620: drawTickMarks(g2, area);
621: }
622:
623:
624: if (isDrawScaleValues())
625: {
626: drawScaleValues(g2, area);
627: }
628:
629: drawValues(g2, area);
630: }
631:
632: protected void drawValues(final Graphics2D g2,
633: final Rectangle2D area)
634: {
635:
636:
637: final Number[] values = getValues();
638: if (values.length == 0)
639: {
640: return;
641: }
642:
643: final double y = area.getCenterY();
644:
645: final Stroke outlineStroke = getOutlineStroke();
646: final Shape defaultShape = getDefaultShape();
647:
648: g2.setPaint(getFillPaint());
649: for (int i = 0; i < values.length; i++)
650: {
651: final Number n = values[i];
652: if (n == null)
653: {
654: continue;
655: }
656:
657: final double v = n.doubleValue();
658: final double x = valueToJava2D(v, area);
659: Shape valueShape = getShape(i);
660: if (valueShape == null)
661: {
662: valueShape = defaultShape;
663: }
664: if (isShapeFilled(i))
665: {
666: g2.translate(x, y);
667: g2.fill(valueShape);
668: g2.translate(-x, -y);
669: }
670: else
671: {
672: g2.setStroke(outlineStroke);
673: g2.translate(x, y);
674: g2.draw(valueShape);
675: g2.translate(-x, -y);
676: }
677: }
678: }
679:
680: protected void drawScaleValues(final Graphics2D g2, final Rectangle2D area)
681: {
682: g2.setPaint(getScaleValuePaint());
683: g2.setFont(getScaleValueFont());
684:
685: final int highest = getHighest();
686: for (int i = getLowest(); i <= highest; i++)
687: {
688: final double x = valueToJava2D(i, area);
689: final double y = area.getCenterY();
690: TextUtilities.drawAlignedString(String.valueOf(i), g2, (float) x,
691: (float) y, TextAnchor.CENTER);
692: }
693: }
694:
695: protected void drawTickMarks(final Graphics2D g2, final Rectangle2D area)
696: {
697: g2.setPaint(getTickMarkPaint());
698: g2.setStroke(new BasicStroke(0.1f));
699:
700: final int highest = getHighest();
701: for (int i = getLowest(); i <= highest; i++)
702: {
703: for (int j = 0; j < 10; j++)
704: {
705: final double xx = valueToJava2D(i + j / 10.0, area);
706: final Line2D mark = new Line2D.Double(xx, area.getCenterY() - 2.0, xx,
707: area.getCenterY() + 2.0);
708: g2.draw(mark);
709: }
710: }
711: final double xx = valueToJava2D(highest, area);
712: final Line2D mark = new Line2D.Double(xx, area.getCenterY() - 2.0, xx,
713: area.getCenterY() + 2.0);
714: g2.draw(mark);
715: }
716:
717: protected void drawRangeArea(final Rectangle2D area, final Graphics2D g2)
718: {
719: final Number rangeUpperBound = getRangeUpperBound();
720: final Number rangeLowerBound = getRangeLowerBound();
721: if (rangeLowerBound == null || rangeUpperBound == null)
722: {
723: return;
724: }
725: final double x0 = valueToJava2D(rangeLowerBound.doubleValue(), area);
726: final double x1 = valueToJava2D(rangeUpperBound.doubleValue(), area);
727: final Rectangle2D rangeArea = new Rectangle2D.Double(x0, area.getY(),
728: (x1 - x0), area.getHeight());
729: g2.setPaint(getRangePaint());
730: g2.fill(rangeArea);
731: }
732:
733: protected void drawBorder(final Graphics2D g2, final Rectangle2D area)
734: {
735: g2.setStroke(getOutlineStroke());
736: g2.setPaint(getBorderPaint());
737: g2.draw(area);
738: }
739:
740:
749: private double valueToJava2D(final double value,
750: final Rectangle2D area)
751: {
752:
753: final double upperBound = getUpperBound();
754: final double lowerBound = getLowerBound();
755: return area.getMinX() + ((value - lowerBound) /
756: (upperBound - lowerBound) * area .getWidth());
757:
758: }
759:
760: private void writeObject(final ObjectOutputStream out)
761: throws IOException
762: {
763: out.defaultWriteObject();
764: final SerializerHelper helper = SerializerHelper.getInstance();
765: helper.writeObject(scaleValuePaint, out);
766: helper.writeObject(rangePaint, out);
767: helper.writeObject(fillPaint, out);
768: helper.writeObject(outlineStroke, out);
769: helper.writeObject(defaultShape, out);
770: helper.writeObject(tickMarkPaint, out);
771: helper.writeObject(borderPaint, out);
772: final int size = shapes.size();
773: out.writeInt(size);
774: for (int i = 0; i < size; i++)
775: {
776: final Shape s = shapes.getShape(i);
777: helper.writeObject(s, out);
778: }
779: }
780:
781: private void readObject(final ObjectInputStream in)
782: throws IOException, ClassNotFoundException
783: {
784: in.defaultReadObject();
785: final SerializerHelper helper = SerializerHelper.getInstance();
786: scaleValuePaint = (Paint) helper.readObject(in);
787: rangePaint = (Paint) helper.readObject(in);
788: fillPaint = (Paint) helper.readObject(in);
789: outlineStroke = (Stroke) helper.readObject(in);
790: defaultShape = (Shape) helper.readObject(in);
791: tickMarkPaint = (Paint) helper.readObject(in);
792: borderPaint = (Paint) helper.readObject(in);
793: shapes = new ShapeList();
794:
795: final int size = in.readInt();
796: for (int i = 0; i < size; i++)
797: {
798: final Shape s = (Shape) helper.readObject(in);
799: shapes.setShape(i, s);
800: }
801:
802: }
803:
804: }