001: /* ========================================================================
002: * JCommon : a free general purpose class library for the Java(tm) platform
003: * ========================================================================
004: *
005: * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
006: *
007: * Project Info: http://www.jfree.org/jcommon/index.html
008: *
009: * This library is free software; you can redistribute it and/or modify it
010: * under the terms of the GNU Lesser General Public License as published by
011: * the Free Software Foundation; either version 2.1 of the License, or
012: * (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful, but
015: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017: * License for more details.
018: *
019: * You should have received a copy of the GNU Lesser General Public
020: * License along with this library; if not, write to the Free Software
021: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
022: * USA.
023: *
024: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025: * in the United States and other countries.]
026: *
027: * --------------------
028: * SerialUtilities.java
029: * --------------------
030: * (C) Copyright 2000-2005, by Object Refinery Limited.
031: *
032: * Original Author: David Gilbert (for Object Refinery Limited);
033: * Contributor(s): Arik Levin;
034: *
035: * $Id: SerialUtilities.java,v 1.13 2005/11/03 09:55:27 mungady Exp $
036: *
037: * Changes
038: * -------
039: * 25-Mar-2003 : Version 1 (DG);
040: * 18-Sep-2003 : Added capability to serialize GradientPaint (DG);
041: * 26-Apr-2004 : Added read/writePoint2D() methods (DG);
042: * 22-Feb-2005 : Added support for Arc2D - see patch 1147035 by Arik Levin (DG);
043: * 29-Jul-2005 : Added support for AttributedString (DG);
044: *
045: */
046:
047: package org.jfree.io;
048:
049: import java.awt.BasicStroke;
050: import java.awt.Color;
051: import java.awt.GradientPaint;
052: import java.awt.Paint;
053: import java.awt.Shape;
054: import java.awt.Stroke;
055: import java.awt.geom.Arc2D;
056: import java.awt.geom.Ellipse2D;
057: import java.awt.geom.GeneralPath;
058: import java.awt.geom.Line2D;
059: import java.awt.geom.PathIterator;
060: import java.awt.geom.Point2D;
061: import java.awt.geom.Rectangle2D;
062: import java.io.IOException;
063: import java.io.ObjectInputStream;
064: import java.io.ObjectOutputStream;
065: import java.io.Serializable;
066: import java.text.AttributedCharacterIterator;
067: import java.text.AttributedString;
068: import java.text.CharacterIterator;
069: import java.util.HashMap;
070: import java.util.Map;
071:
072: /**
073: * A class containing useful utility methods relating to serialization.
074: *
075: * @author David Gilbert
076: */
077: public class SerialUtilities {
078:
079: /**
080: * Private constructor prevents object creation.
081: */
082: private SerialUtilities() {
083: }
084:
085: /**
086: * Returns <code>true</code> if a class implements <code>Serializable</code>
087: * and <code>false</code> otherwise.
088: *
089: * @param c the class.
090: *
091: * @return A boolean.
092: */
093: public static boolean isSerializable(final Class c) {
094: /**
095: final Class[] interfaces = c.getInterfaces();
096: for (int i = 0; i < interfaces.length; i++) {
097: if (interfaces[i].equals(Serializable.class)) {
098: return true;
099: }
100: }
101: Class cc = c.getSuperclass();
102: if (cc != null) {
103: return isSerializable(cc);
104: }
105: */
106: return (Serializable.class.isAssignableFrom(c));
107: }
108:
109: /**
110: * Reads a <code>Paint</code> object that has been serialised by the
111: * {@link SerialUtilities#writePaint(Paint, ObjectOutputStream)} method.
112: *
113: * @param stream the input stream (<code>null</code> not permitted).
114: *
115: * @return The paint object (possibly <code>null</code>).
116: *
117: * @throws IOException if there is an I/O problem.
118: * @throws ClassNotFoundException if there is a problem loading a class.
119: */
120: public static Paint readPaint(final ObjectInputStream stream)
121: throws IOException, ClassNotFoundException {
122:
123: if (stream == null) {
124: throw new IllegalArgumentException(
125: "Null 'stream' argument.");
126: }
127: Paint result = null;
128: final boolean isNull = stream.readBoolean();
129: if (!isNull) {
130: final Class c = (Class) stream.readObject();
131: if (isSerializable(c)) {
132: result = (Paint) stream.readObject();
133: } else if (c.equals(GradientPaint.class)) {
134: final float x1 = stream.readFloat();
135: final float y1 = stream.readFloat();
136: final Color c1 = (Color) stream.readObject();
137: final float x2 = stream.readFloat();
138: final float y2 = stream.readFloat();
139: final Color c2 = (Color) stream.readObject();
140: final boolean isCyclic = stream.readBoolean();
141: result = new GradientPaint(x1, y1, c1, x2, y2, c2,
142: isCyclic);
143: }
144: }
145: return result;
146:
147: }
148:
149: /**
150: * Serialises a <code>Paint</code> object.
151: *
152: * @param paint the paint object (<code>null</code> permitted).
153: * @param stream the output stream (<code>null</code> not permitted).
154: *
155: * @throws IOException if there is an I/O error.
156: */
157: public static void writePaint(final Paint paint,
158: final ObjectOutputStream stream) throws IOException {
159:
160: if (stream == null) {
161: throw new IllegalArgumentException(
162: "Null 'stream' argument.");
163: }
164: if (paint != null) {
165: stream.writeBoolean(false);
166: stream.writeObject(paint.getClass());
167: if (paint instanceof Serializable) {
168: stream.writeObject(paint);
169: } else if (paint instanceof GradientPaint) {
170: final GradientPaint gp = (GradientPaint) paint;
171: stream.writeFloat((float) gp.getPoint1().getX());
172: stream.writeFloat((float) gp.getPoint1().getY());
173: stream.writeObject(gp.getColor1());
174: stream.writeFloat((float) gp.getPoint2().getX());
175: stream.writeFloat((float) gp.getPoint2().getY());
176: stream.writeObject(gp.getColor2());
177: stream.writeBoolean(gp.isCyclic());
178: }
179: } else {
180: stream.writeBoolean(true);
181: }
182:
183: }
184:
185: /**
186: * Reads a <code>Stroke</code> object that has been serialised by the
187: * {@link SerialUtilities#writeStroke(Stroke, ObjectOutputStream)} method.
188: *
189: * @param stream the input stream (<code>null</code> not permitted).
190: *
191: * @return The stroke object (possibly <code>null</code>).
192: *
193: * @throws IOException if there is an I/O problem.
194: * @throws ClassNotFoundException if there is a problem loading a class.
195: */
196: public static Stroke readStroke(final ObjectInputStream stream)
197: throws IOException, ClassNotFoundException {
198:
199: if (stream == null) {
200: throw new IllegalArgumentException(
201: "Null 'stream' argument.");
202: }
203: Stroke result = null;
204: final boolean isNull = stream.readBoolean();
205: if (!isNull) {
206: final Class c = (Class) stream.readObject();
207: if (c.equals(BasicStroke.class)) {
208: final float width = stream.readFloat();
209: final int cap = stream.readInt();
210: final int join = stream.readInt();
211: final float miterLimit = stream.readFloat();
212: final float[] dash = (float[]) stream.readObject();
213: final float dashPhase = stream.readFloat();
214: result = new BasicStroke(width, cap, join, miterLimit,
215: dash, dashPhase);
216: } else {
217: result = (Stroke) stream.readObject();
218: }
219: }
220: return result;
221:
222: }
223:
224: /**
225: * Serialises a <code>Stroke</code> object. This code handles the
226: * <code>BasicStroke</code> class which is the only <code>Stroke</code>
227: * implementation provided by the JDK (and isn't directly
228: * <code>Serializable</code>).
229: *
230: * @param stroke the stroke object (<code>null</code> permitted).
231: * @param stream the output stream (<code>null</code> not permitted).
232: *
233: * @throws IOException if there is an I/O error.
234: */
235: public static void writeStroke(final Stroke stroke,
236: final ObjectOutputStream stream) throws IOException {
237:
238: if (stream == null) {
239: throw new IllegalArgumentException(
240: "Null 'stream' argument.");
241: }
242: if (stroke != null) {
243: stream.writeBoolean(false);
244: if (stroke instanceof BasicStroke) {
245: final BasicStroke s = (BasicStroke) stroke;
246: stream.writeObject(BasicStroke.class);
247: stream.writeFloat(s.getLineWidth());
248: stream.writeInt(s.getEndCap());
249: stream.writeInt(s.getLineJoin());
250: stream.writeFloat(s.getMiterLimit());
251: stream.writeObject(s.getDashArray());
252: stream.writeFloat(s.getDashPhase());
253: } else {
254: stream.writeObject(stroke.getClass());
255: stream.writeObject(stroke);
256: }
257: } else {
258: stream.writeBoolean(true);
259: }
260: }
261:
262: /**
263: * Reads a <code>Shape</code> object that has been serialised by the
264: * {@link #writeShape(Shape, ObjectOutputStream)} method.
265: *
266: * @param stream the input stream (<code>null</code> not permitted).
267: *
268: * @return The shape object (possibly <code>null</code>).
269: *
270: * @throws IOException if there is an I/O problem.
271: * @throws ClassNotFoundException if there is a problem loading a class.
272: */
273: public static Shape readShape(final ObjectInputStream stream)
274: throws IOException, ClassNotFoundException {
275:
276: if (stream == null) {
277: throw new IllegalArgumentException(
278: "Null 'stream' argument.");
279: }
280: Shape result = null;
281: final boolean isNull = stream.readBoolean();
282: if (!isNull) {
283: final Class c = (Class) stream.readObject();
284: if (c.equals(Line2D.class)) {
285: final double x1 = stream.readDouble();
286: final double y1 = stream.readDouble();
287: final double x2 = stream.readDouble();
288: final double y2 = stream.readDouble();
289: result = new Line2D.Double(x1, y1, x2, y2);
290: } else if (c.equals(Rectangle2D.class)) {
291: final double x = stream.readDouble();
292: final double y = stream.readDouble();
293: final double w = stream.readDouble();
294: final double h = stream.readDouble();
295: result = new Rectangle2D.Double(x, y, w, h);
296: } else if (c.equals(Ellipse2D.class)) {
297: final double x = stream.readDouble();
298: final double y = stream.readDouble();
299: final double w = stream.readDouble();
300: final double h = stream.readDouble();
301: result = new Ellipse2D.Double(x, y, w, h);
302: } else if (c.equals(Arc2D.class)) {
303: final double x = stream.readDouble();
304: final double y = stream.readDouble();
305: final double w = stream.readDouble();
306: final double h = stream.readDouble();
307: final double as = stream.readDouble(); // Angle Start
308: final double ae = stream.readDouble(); // Angle Extent
309: final int at = stream.readInt(); // Arc type
310: result = new Arc2D.Double(x, y, w, h, as, ae, at);
311: } else if (c.equals(GeneralPath.class)) {
312: final GeneralPath gp = new GeneralPath();
313: final float[] args = new float[6];
314: boolean hasNext = stream.readBoolean();
315: while (!hasNext) {
316: final int type = stream.readInt();
317: for (int i = 0; i < 6; i++) {
318: args[i] = stream.readFloat();
319: }
320: switch (type) {
321: case PathIterator.SEG_MOVETO:
322: gp.moveTo(args[0], args[1]);
323: break;
324: case PathIterator.SEG_LINETO:
325: gp.lineTo(args[0], args[1]);
326: break;
327: case PathIterator.SEG_CUBICTO:
328: gp.curveTo(args[0], args[1], args[2], args[3],
329: args[4], args[5]);
330: break;
331: case PathIterator.SEG_QUADTO:
332: gp.quadTo(args[0], args[1], args[2], args[3]);
333: break;
334: case PathIterator.SEG_CLOSE:
335: //result = gp;
336: break;
337: default:
338: throw new RuntimeException(
339: "JFreeChart - No path exists");
340: }
341: gp.setWindingRule(stream.readInt());
342: hasNext = stream.readBoolean();
343: }
344: result = gp;
345: } else {
346: result = (Shape) stream.readObject();
347: }
348: }
349: return result;
350:
351: }
352:
353: /**
354: * Serialises a <code>Shape</code> object.
355: *
356: * @param shape the shape object (<code>null</code> permitted).
357: * @param stream the output stream (<code>null</code> not permitted).
358: *
359: * @throws IOException if there is an I/O error.
360: */
361: public static void writeShape(final Shape shape,
362: final ObjectOutputStream stream) throws IOException {
363:
364: if (stream == null) {
365: throw new IllegalArgumentException(
366: "Null 'stream' argument.");
367: }
368: if (shape != null) {
369: stream.writeBoolean(false);
370: if (shape instanceof Line2D) {
371: final Line2D line = (Line2D) shape;
372: stream.writeObject(Line2D.class);
373: stream.writeDouble(line.getX1());
374: stream.writeDouble(line.getY1());
375: stream.writeDouble(line.getX2());
376: stream.writeDouble(line.getY2());
377: } else if (shape instanceof Rectangle2D) {
378: final Rectangle2D rectangle = (Rectangle2D) shape;
379: stream.writeObject(Rectangle2D.class);
380: stream.writeDouble(rectangle.getX());
381: stream.writeDouble(rectangle.getY());
382: stream.writeDouble(rectangle.getWidth());
383: stream.writeDouble(rectangle.getHeight());
384: } else if (shape instanceof Ellipse2D) {
385: final Ellipse2D ellipse = (Ellipse2D) shape;
386: stream.writeObject(Ellipse2D.class);
387: stream.writeDouble(ellipse.getX());
388: stream.writeDouble(ellipse.getY());
389: stream.writeDouble(ellipse.getWidth());
390: stream.writeDouble(ellipse.getHeight());
391: } else if (shape instanceof Arc2D) {
392: final Arc2D arc = (Arc2D) shape;
393: stream.writeObject(Arc2D.class);
394: stream.writeDouble(arc.getX());
395: stream.writeDouble(arc.getY());
396: stream.writeDouble(arc.getWidth());
397: stream.writeDouble(arc.getHeight());
398: stream.writeDouble(arc.getAngleStart());
399: stream.writeDouble(arc.getAngleExtent());
400: stream.writeInt(arc.getArcType());
401: } else if (shape instanceof GeneralPath) {
402: stream.writeObject(GeneralPath.class);
403: final PathIterator pi = shape.getPathIterator(null);
404: final float[] args = new float[6];
405: stream.writeBoolean(pi.isDone());
406: while (!pi.isDone()) {
407: final int type = pi.currentSegment(args);
408: stream.writeInt(type);
409: // TODO: could write this to only stream the values
410: // required for the segment type
411: for (int i = 0; i < 6; i++) {
412: stream.writeFloat(args[i]);
413: }
414: stream.writeInt(pi.getWindingRule());
415: pi.next();
416: stream.writeBoolean(pi.isDone());
417: }
418: } else {
419: stream.writeObject(shape.getClass());
420: stream.writeObject(shape);
421: }
422: } else {
423: stream.writeBoolean(true);
424: }
425: }
426:
427: /**
428: * Reads a <code>Point2D</code> object that has been serialised by the
429: * {@link #writePoint2D(Point2D, ObjectOutputStream)} method.
430: *
431: * @param stream the input stream (<code>null</code> not permitted).
432: *
433: * @return The point object (possibly <code>null</code>).
434: *
435: * @throws IOException if there is an I/O problem.
436: */
437: public static Point2D readPoint2D(final ObjectInputStream stream)
438: throws IOException {
439:
440: if (stream == null) {
441: throw new IllegalArgumentException(
442: "Null 'stream' argument.");
443: }
444: Point2D result = null;
445: final boolean isNull = stream.readBoolean();
446: if (!isNull) {
447: final double x = stream.readDouble();
448: final double y = stream.readDouble();
449: result = new Point2D.Double(x, y);
450: }
451: return result;
452:
453: }
454:
455: /**
456: * Serialises a <code>Point2D</code> object.
457: *
458: * @param p the point object (<code>null</code> permitted).
459: * @param stream the output stream (<code>null</code> not permitted).
460: *
461: * @throws IOException if there is an I/O error.
462: */
463: public static void writePoint2D(final Point2D p,
464: final ObjectOutputStream stream) throws IOException {
465:
466: if (stream == null) {
467: throw new IllegalArgumentException(
468: "Null 'stream' argument.");
469: }
470: if (p != null) {
471: stream.writeBoolean(false);
472: stream.writeDouble(p.getX());
473: stream.writeDouble(p.getY());
474: } else {
475: stream.writeBoolean(true);
476: }
477: }
478:
479: /**
480: * Reads a <code>AttributedString</code> object that has been serialised by
481: * the {@link SerialUtilities#writeAttributedString(AttributedString,
482: * ObjectOutputStream)} method.
483: *
484: * @param stream the input stream (<code>null</code> not permitted).
485: *
486: * @return The attributed string object (possibly <code>null</code>).
487: *
488: * @throws IOException if there is an I/O problem.
489: * @throws ClassNotFoundException if there is a problem loading a class.
490: */
491: public static AttributedString readAttributedString(
492: ObjectInputStream stream) throws IOException,
493: ClassNotFoundException {
494:
495: if (stream == null) {
496: throw new IllegalArgumentException(
497: "Null 'stream' argument.");
498: }
499: AttributedString result = null;
500: final boolean isNull = stream.readBoolean();
501: if (!isNull) {
502: // read string and attributes then create result
503: String plainStr = (String) stream.readObject();
504: result = new AttributedString(plainStr);
505: char c = stream.readChar();
506: int start = 0;
507: while (c != CharacterIterator.DONE) {
508: int limit = stream.readInt();
509: Map atts = (Map) stream.readObject();
510: result.addAttributes(atts, start, limit);
511: start = limit;
512: c = stream.readChar();
513: }
514: }
515: return result;
516: }
517:
518: /**
519: * Serialises an <code>AttributedString</code> object.
520: *
521: * @param as the attributed string object (<code>null</code> permitted).
522: * @param stream the output stream (<code>null</code> not permitted).
523: *
524: * @throws IOException if there is an I/O error.
525: */
526: public static void writeAttributedString(AttributedString as,
527: ObjectOutputStream stream) throws IOException {
528:
529: if (stream == null) {
530: throw new IllegalArgumentException(
531: "Null 'stream' argument.");
532: }
533: if (as != null) {
534: stream.writeBoolean(false);
535: AttributedCharacterIterator aci = as.getIterator();
536: // build a plain string from aci
537: // then write the string
538: StringBuffer plainStr = new StringBuffer();
539: char current = aci.first();
540: while (current != CharacterIterator.DONE) {
541: plainStr = plainStr.append(current);
542: current = aci.next();
543: }
544: stream.writeObject(plainStr.toString());
545:
546: // then write the attributes and limits for each run
547: current = aci.first();
548: int begin = aci.getBeginIndex();
549: while (current != CharacterIterator.DONE) {
550: // write the current character - when the reader sees that this
551: // is not CharacterIterator.DONE, it will know to read the
552: // run limits and attributes
553: stream.writeChar(current);
554:
555: // now write the limit, adjusted as if beginIndex is zero
556: int limit = aci.getRunLimit();
557: stream.writeInt(limit - begin);
558:
559: // now write the attribute set
560: Map atts = new HashMap(aci.getAttributes());
561: stream.writeObject(atts);
562: current = aci.setIndex(limit);
563: }
564: // write a character that signals to the reader that all runs
565: // are done...
566: stream.writeChar(CharacterIterator.DONE);
567: } else {
568: // write a flag that indicates a null
569: stream.writeBoolean(true);
570: }
571:
572: }
573:
574: }
|