Ericson Willians Ericson Willians - 2 months ago 8
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

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!

Comments