The Shape
interface is implemented by objects of Java 2D (Arc2D
, Area
, CubicCurve2D
, Ellipse2D
, GeneralPath
etc..).
Some of the concrete objects are marked as Serializable
and can be stored and restored using object serialization, but others like Area
do not implement the interface and throw errors.
But since we are constantly warned that such naive serialization is not necessarily stable across Java implementations or versions, I'd prefer to use some form of serialization that is.
That leads us to storing/restoring from XML using XMLEncoder
and XMLDecoder
, but that is capable of handling even less of the Java 2D Shape
objects.
Some results for both can be seen below. We start with 6 shapes, and attempt to store/restore them via object serialization and standard XML serialization.
How would we store all Shape
objects correctly via XML?
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.beans.*;
import java.io.*;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.border.TitledBorder;
public class Serialize2D {
private JPanel ui;
Serialize2D() {
initUI();
}
public void initUI() {
if (ui != null) {
return;
}
ui = new JPanel(new GridLayout(0, 1));
int[] xpoints = {205, 295, 205, 295};
int[] ypoints = {5, 25, 25, 45};
Polygon polygon = new Polygon(xpoints, ypoints, xpoints.length);
ArrayList<Shape> shapes = new ArrayList<Shape>();
int w = 45;
shapes.add(new Rectangle2D.Double(5, 5, 90, 40));
shapes.add(new Ellipse2D.Double(105, 5, 90, 40));
shapes.add(polygon);
shapes.add(new GeneralPath(new Rectangle2D.Double(5, 55, 90, 40)));
shapes.add(new Path2D.Double(new Rectangle2D.Double(105, 55, 90, 40)));
shapes.add(new Area(new Rectangle2D.Double(205, 55, 90, 40)));
addTitledLabelToPanel(shapes, "Original Shapes");
addTitledLabelToPanel(
serializeToFromObject(shapes), "Serialize via Object");
addTitledLabelToPanel(
serializeToFromXML(shapes), "Serialize via XML");
}
public JComponent getUI() {
return ui;
}
public ArrayList<Shape> serializeToFromObject(ArrayList<Shape> shapes) {
ArrayList<Shape> shps = new ArrayList<Shape>();
try {
ObjectOutputStream oos = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
for (Shape shape : shapes) {
try {
oos.writeObject(shape);
} catch (Exception ex) {
System.err.println(ex.toString());
}
}
oos.flush();
oos.close();
System.out.println("length Obj: " + baos.toByteArray().length);
ByteArrayInputStream bais = new ByteArrayInputStream(
baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
Object o = null;
try {
o = ois.readObject();
} catch (NotSerializableException ex) {
System.err.println(ex.getMessage());
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
}
while (o != null) {
shps.add((Shape) o);
try {
o = ois.readObject();
} catch (NotSerializableException ex) {
System.err.println(ex.getMessage());
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
}
}
return shps;
} catch (IOException ex) {
ex.printStackTrace();
}
return shps;
}
public ArrayList<Shape> serializeToFromXML(ArrayList<Shape> shapes) {
ArrayList<Shape> shps = new ArrayList<Shape>();
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
XMLEncoder xmle = new XMLEncoder(baos);
for (Shape shape : shapes) {
xmle.writeObject(shape);
}
xmle.flush();
xmle.close();
System.out.println("length XML: " + baos.toByteArray().length);
ByteArrayInputStream bais
= new ByteArrayInputStream(baos.toByteArray());
XMLDecoder xmld = new XMLDecoder(bais);
Shape shape = (Shape) xmld.readObject();
while (shape != null) {
shps.add(shape);
try {
shape = (Shape) xmld.readObject();
} catch (ArrayIndexOutOfBoundsException aioobe) {
// we've read last object
shape = null;
}
}
xmld.close();
} catch (Exception ex) {
ex.printStackTrace();
}
return shps;
}
private final static String getType(Object o) {
String s = o.getClass().getName();
String[] parts = s.split("\\.");
s = parts[parts.length - 1].split("\\$")[0];
return s;
}
public static void drawShapesToImage(
ArrayList<Shape> shapes, BufferedImage image) {
Graphics2D g = image.createGraphics();
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.WHITE);
g.fillRect(0, 0, image.getWidth(), image.getHeight());
for (Shape shape : shapes) {
String s = getType(shape);
g.setColor(Color.GREEN);
g.fill(shape);
g.setColor(Color.BLACK);
g.draw(shape);
Rectangle r = shape.getBounds();
int x = r.x + 5;
int y = r.y + 16;
if (r.width * r.height != 0) {
g.drawString(s, x, y);
}
}
g.dispose();
}
private void addTitledLabelToPanel(ArrayList<Shape> shapes, String title) {
int w = 300;
int h = 100;
BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
drawShapesToImage(shapes, bi);
JLabel l = new JLabel(new ImageIcon(bi));
l.setBorder(new TitledBorder(title));
ui.add(l);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
Serialize2D ss = new Serialize2D();
JOptionPane.showMessageDialog(null, ss.getUI());
}
};
SwingUtilities.invokeLater(r);
}
}