Ericson Willians Ericson Willians - 11 months ago 74
Java Question

How can I serialize a HashMap with BitMatrix objects? (QR Codes / zxing)

I have a "static class" called QRGenerator, whose purpose is to generate BitMatrix objects and to generate BufferedImage objects from them. This is the code for it:

package ericsonwrp.republica.vintage.caixa;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.EnumMap;
import java.util.Map;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;

public class QRGenerator {

private static BufferedImage image = null;
private static int size = 250;
private static BitMatrix byteMatrix = null;

public static BitMatrix generateBitMatrix(String codeText) {
try {
Map<EncodeHintType, Object> hintMap = new EnumMap<EncodeHintType, Object>(EncodeHintType.class);
hintMap.put(EncodeHintType.CHARACTER_SET, "UTF-8");
hintMap.put(EncodeHintType.MARGIN, 1);
hintMap.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);
QRCodeWriter qrCodeWriter = new QRCodeWriter();
byteMatrix = qrCodeWriter.encode(codeText, BarcodeFormat.QR_CODE, size,
size, hintMap);
} catch (WriterException e) {
e.printStackTrace();
}
return byteMatrix;
}

public static BufferedImage generateImage(BitMatrix byteMatrix) {
int width = byteMatrix.getWidth();
image = new BufferedImage(width, width,
BufferedImage.TYPE_INT_RGB);
image.createGraphics();
Graphics2D graphics = (Graphics2D) image.getGraphics();
graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, width, width);
graphics.setColor(Color.BLACK);
for (int i = 0; i < width; i++) {
for (int j = 0; j < width; j++) {
if (byteMatrix.get(i, j)) {
graphics.fillRect(i, j, 1, 1);
}
}
}
return image;
}

}


I have another class called "RepublicaVintageFile", whose purpose is to define an unique file to store the "content" of my application, and to provide a method to serialize (And read, which is irrelevant for the question) this "content". This is the code for it:

package ericsonwrp.republica.vintage.caixa;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.util.HashMap;

public class RepublicaVintageFile {

private HashMap<String, Object> content;

public RepublicaVintageFile() {
setContent(new HashMap<String, Object>());
}

public void serializeContent(String path) throws IOException {
FileOutputStream fout = new FileOutputStream(path, true);
ObjectOutput out = null;
try {
out = new ObjectOutputStream(fout);
out.writeObject(getContent());
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException ex) {
// ignore close exception
}
try {
fout.close();
} catch (IOException ex) {
// ignore close exception
}
}
}

public HashMap<String, Object> getContent() {
return content;
}

public void setContent(HashMap<String, Object> content) {
this.content = content;
}

}


The "content" of my application include BitMatrix objects, which are saved under a label inside a HashMap in a different file (
private HashMap<String, BitMatrix> qrItems;
). Since serializing the final BufferedImage gives me a
java.io.NotSerializableException
, I've tried to serialize the BitMatrix objects. But, for my disappointment, that's also impossible. This is the stacktrace:

java.io.NotSerializableException: com.google.zxing.common.BitMatrix
at java.io.ObjectOutputStream.writeObject0(Unknown Source)
at java.io.ObjectOutputStream.writeObject(Unknown Source)
at java.util.HashMap.internalWriteEntries(Unknown Source)
at java.util.HashMap.writeObject(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at java.io.ObjectStreamClass.invokeWriteObject(Unknown Source)
at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
at java.io.ObjectOutputStream.writeObject0(Unknown Source)
at java.io.ObjectOutputStream.writeObject(Unknown Source)
at java.util.HashMap.internalWriteEntries(Unknown Source)
at java.util.HashMap.writeObject(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at java.io.ObjectStreamClass.invokeWriteObject(Unknown Source)
at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
at java.io.ObjectOutputStream.writeObject0(Unknown Source)
at java.io.ObjectOutputStream.writeObject(Unknown Source)
at ericsonwrp.republica.vintage.caixa.RepublicaVintageFile.serializeContent(RepublicaVintageFile.java:26)
at ericsonwrp.republica.vintage.caixa.MainFrame.openSaveAsDialog(MainFrame.java:204)
at ericsonwrp.republica.vintage.caixa.MainFrame.access$1(MainFrame.java:177)
at ericsonwrp.republica.vintage.caixa.MainFrame$4.actionPerformed(MainFrame.java:112)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.AbstractButton.doClick(Unknown Source)
at javax.swing.plaf.basic.BasicMenuItemUI.doClick(Unknown Source)
at javax.swing.plaf.basic.BasicMenuItemUI$Handler.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$500(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)


Considering all this, I come to my question: How can I serialize a HashMap with BitMatrix objects? That's important, because I don't want to store 100 (Or more) separate .png files on a folder and have to read the images all the time in order to get their content.

Answer Source

I've managed to "save" and "load" the BitMatrix objects translating them into boolean 2-dimensional arrays.

private HashMap<String, BitMatrix> qrMatrixes;
private HashMap<String, boolean[][]> qrMatrixBooleanArrays;

As you can see, I have a HashMap of < String, BitMatrix >, with an equivalent in boolean[][] format. That's how I make such "translation" (Transferring the bits from each BitMatrix objects to the boolean[][] arrays):

for (Map.Entry<String, BitMatrix> e : getQrMatrixes().entrySet()) {
    boolean[][] b = new boolean[e.getValue().getHeight()][e.getValue().getWidth()];
    for (int j = 0; j < e.getValue().getHeight(); j++) {
        for (int k = 0; k < e.getValue().getWidth(); k++) {
            b[j][k] = e.getValue().get(j, k);
        }
    }
    qrMatrixBooleanArrays.put(e.getKey(), b);
}

Java's Serializer serializes such object (HashMap< String, boolean[][] >) with no problems, no matter how nested it is (I've imagined that it would work with primitive types, with an old-fashioned array). Here's the whole method to update the list with the QR Codes, after loading the file:

    public void updateList() {
        if (MainFrame.getCurrentFile() != null) {
            setQrMatrixes(new HashMap<String, BitMatrix>());
            setQrMatrixBooleanArrays(new HashMap<String, boolean[][]>());
            for (Map.Entry<String, boolean[][]> e : ((HashMap<String, boolean[][]>) MainFrame.getCurrentFile().getContent().get("Qr Codes")).entrySet()) {
                getQrMatrixBooleanArrays().put(e.getKey(), e.getValue());
            }
            for (Map.Entry<String, boolean[][]> e : getQrMatrixBooleanArrays().entrySet()) {
                BitMatrix b = new BitMatrix(e.getValue().length, e.getValue().length);
                for (int i = 0; i < e.getValue().length; i++) {
                    for (int j = 0; j < e.getValue()[i].length; j++) {
                        if (e.getValue()[i][j] == true) {
                            b.set(i, j);
                        }
                    }
                }
                getQrMatrixes().put(e.getKey(), b);
                qrListModel.addElement(e.getKey());
            }
        } else {
            System.out.println("No file is loaded.");
        }
    }

I'm afraid it's a bit hard to be more clear. Explore the docs for the BitMatrix object, and just copy the contents into a primitive 2d array. Good luck!