[LIB-9] Add ability to archive qrcode content
authorgotty <gotty@hedgecode.org>
Mon, 29 Apr 2019 14:40:20 +0000 (17:40 +0300)
committergotty <gotty@hedgecode.org>
Mon, 29 Apr 2019 14:40:20 +0000 (17:40 +0300)
16 files changed:
chesshog-qrcode/src/main/java/org/hedgecode/chess/ChessQRCodeApp.java [deleted file]
chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeApp.java [new file with mode: 0644]
chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeConstants.java [moved from chesshog-qrcode/src/main/java/org/hedgecode/chess/ChessQRCodeConstants.java with 90% similarity]
chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeException.java
chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeMode.java
chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeReader.java
chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeWriter.java
chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRMatrixUtils.java
chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRResult.java
chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipChessQRCodeReader.java [new file with mode: 0644]
chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipChessQRCodeWriter.java [new file with mode: 0644]
chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipContentDeflater.java [new file with mode: 0644]
chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipContentInflater.java [new file with mode: 0644]
chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipContentUtils.java [new file with mode: 0644]
chesshog-qrcode/src/main/resources/org/hedgecode/chess/qrcode/LocalStrings.properties
chesshog-qrcode/src/main/resources/org/hedgecode/chess/qrcode/LocalStrings_ru.properties

diff --git a/chesshog-qrcode/src/main/java/org/hedgecode/chess/ChessQRCodeApp.java b/chesshog-qrcode/src/main/java/org/hedgecode/chess/ChessQRCodeApp.java
deleted file mode 100644 (file)
index 59f64e3..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (c) 2018-2019. Developed by Hedgecode.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.hedgecode.chess;
-
-/**
- * Chess QR Code Main Application.
- *
- * @author Dmitry Samoshin aka gotty
- */
-public final class ChessQRCodeApp {
-
-    public static void main(String[] args) {
-
-
-    }
-
-}
\ No newline at end of file
diff --git a/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeApp.java b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeApp.java
new file mode 100644 (file)
index 0000000..fe5d50e
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2018-2019. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.qrcode;
+
+import org.hedgecode.chess.ChessHogConsole;
+
+/**
+ * Chess QR Code Main Application.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class ChessQRCodeApp {
+
+    private static final String[] QRCODE_MODULE_LOGO = {
+            "                          _                        ",
+            "        ___ ___ ___ ___ _| |___                    ",
+            "       | . |  _|  _| . | . | -_|                   ",
+            "       |_  |_| |___|___|___|___|                   ",
+            "         |_|                    _     _            ",
+            "                    _____ ___ _| |_ _| |___        ",
+            "                   |     | . | . | | | | -_|       ",
+            "                   |_|_|_|___|___|___|_|___|       ",
+            "                                                   "
+    };
+
+    public static void main(String[] args) {
+
+        ChessHogConsole.init();
+
+        for (String line : QRCODE_MODULE_LOGO) {
+            ChessHogConsole.console(line);
+        }
+
+    }
+
+}
\ No newline at end of file
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package org.hedgecode.chess;
+package org.hedgecode.chess.qrcode;
 
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
@@ -32,7 +32,7 @@ public final class ChessQRCodeConstants {
 
     private ChessQRCodeConstants() {
         throw new AssertionError(
-                "No org.hedgecode.chess.ChessQRCodeConstants instances!"
+                "No org.hedgecode.chess.qrcode.ChessQRCodeConstants instances!"
         );
     }
 
index 5c490de..9844bc2 100644 (file)
@@ -18,8 +18,6 @@ package org.hedgecode.chess.qrcode;
 
 import java.util.ResourceBundle;
 
-import org.hedgecode.chess.ChessQRCodeConstants;
-
 /**
  * Chess-specific QR Codes read/write/parse Exception.
  *
index 1c8f373..e0c1f98 100644 (file)
@@ -17,7 +17,7 @@
 package org.hedgecode.chess.qrcode;
 
 /**
- * Mode of data type stored in Chess-specific QR Codes.
+ * Mode of data type stored in chess-specific QR Codes.
  *
  * @author Dmitry Samoshin aka gotty
  */
@@ -26,18 +26,23 @@ public enum ChessQRCodeMode {
     FEN,
     TCD,
     PGN,
-    TCG;
+    TCG,
+    ZIP;
 
-    static final int CODE_LENGTH = 3;
+    public static final int CODE_LENGTH = 3;
 
-    boolean isPosition() {
+    public boolean isPosition() {
         return FEN.equals(this) || TCD.equals(this);
     }
 
-    boolean isGame() {
+    public boolean isGame() {
         return PGN.equals(this) || TCG.equals(this);
     }
 
+    public boolean isArchive() {
+        return ZIP.equals(this);
+    }
+
     public static ChessQRCodeMode byCode(String code) {
         for (ChessQRCodeMode mode : ChessQRCodeMode.values()) {
             if (mode.name().equals(code))
index a0bbbdf..b10caa4 100644 (file)
@@ -36,8 +36,6 @@ import com.google.zxing.common.HybridBinarizer;
 import com.google.zxing.qrcode.decoder.Decoder;
 import com.google.zxing.qrcode.detector.Detector;
 
-import org.hedgecode.chess.ChessQRCodeConstants;
-
 /**
  * Chess-specific QR Codes reader (image decoder).
  *
@@ -49,7 +47,7 @@ public class ChessQRCodeReader {
 
     private static ChessQRCodeReader _instance = new ChessQRCodeReader();
 
-    private ChessQRCodeReader() {
+    protected ChessQRCodeReader() {
         decoder = new Decoder();
     }
 
@@ -126,20 +124,7 @@ public class ChessQRCodeReader {
             );
         }
 
-        ChessQRCodeMode mode = ChessQRCodeMode.byCode(
-                decoderResult.getText().substring(0, ChessQRCodeMode.CODE_LENGTH)
-        );
-
-        if (mode == null) {
-            throw new ChessQRCodeException(
-                    ChessQRCodeException.Type.READ, "read.unknown.chess.mode"
-            );
-        }
-
-        return new ChessQRResult(
-                mode,
-                decoderResult.getText().substring(ChessQRCodeMode.CODE_LENGTH)
-        );
+        return createResult(decoderResult);
 /*
         ResultPoint[] points = detectorResult.getPoints();
 
@@ -176,6 +161,22 @@ public class ChessQRCodeReader {
 */
     }
 
+    protected ChessQRResult createResult(DecoderResult decoderResult) throws ChessQRCodeException {
+        String result = decoderResult.getText();
+
+        ChessQRCodeMode mode = ChessQRResult.getMode(result);
+        if (mode == null) {
+            throw new ChessQRCodeException(
+                    ChessQRCodeException.Type.READ, "read.unknown.chess.mode"
+            );
+        }
+
+        return new ChessQRResult(
+                mode,
+                ChessQRResult.getContents(result)
+        );
+    }
+
     public static ChessQRCodeReader getInstance() {
         return _instance;
     }
@@ -184,7 +185,7 @@ public class ChessQRCodeReader {
     public static void main(String[] args) {
         try {
             ChessQRResult qrCodeResult = ChessQRCodeReader.getInstance().read(
-                    new File("MyQRCode.png")
+                    new File("qrcode.png")
             );
             System.out.println("Decoded format = " + qrCodeResult.getMode());
             System.out.println("Decoded text = " + qrCodeResult.getContents());
index bc11750..cb2d532 100644 (file)
@@ -35,7 +35,6 @@ import com.google.zxing.qrcode.encoder.ByteMatrix;
 import com.google.zxing.qrcode.encoder.Encoder;
 import com.google.zxing.qrcode.encoder.QRCode;
 
-import org.hedgecode.chess.ChessQRCodeConstants;
 import org.hedgecode.chess.img.ImageFormat;
 
 /**
@@ -47,12 +46,13 @@ public class ChessQRCodeWriter {
 
     private static final int DEF_QUIET_ZONE_SIZE = 4;
     private static final int DEF_QRCODE_BIT_SIZE = 250;
+    private static final int MIN_QRCODE_BIT_SIZE = 50;
 
     private static final ErrorCorrectionLevel DEF_ERR_CORR_LEVEL = ErrorCorrectionLevel.H; // todo: Q
 
     private static ChessQRCodeWriter _instance = new ChessQRCodeWriter();
 
-    private ChessQRCodeWriter() {
+    protected ChessQRCodeWriter() {
     }
 
     public void write(ChessQRCodeMode mode, String contents, ImageFormat format, String qrCodeFilePath)
@@ -139,7 +139,7 @@ public class ChessQRCodeWriter {
         );
     }
 
-    private BitMatrix encode(
+    protected BitMatrix encode(
             ChessQRCodeMode mode,
             String contents,
             int width, int height,
@@ -158,12 +158,14 @@ public class ChessQRCodeWriter {
             );
         }
 
-        if (width < 0 || height < 0) { // todo
+        if (Math.min(width, height) < MIN_QRCODE_BIT_SIZE) { // todo: check for Version
             throw new ChessQRCodeException(
                     ChessQRCodeException.Type.WRITE, "write.qrcode.size.incorrect"
             );
         }
 
+        String qrCodeContents = mode.name().concat(contents);
+
         int quietZone = DEF_QUIET_ZONE_SIZE;
         ErrorCorrectionLevel errorCorrectionLevel = DEF_ERR_CORR_LEVEL;
 
@@ -188,12 +190,10 @@ public class ChessQRCodeWriter {
         }
         hints = newHints;
 
-        String qrCodeText = mode.name().concat(contents);
-
         QRCode qrCode;
         try {
             qrCode = Encoder.encode(
-                    qrCodeText, errorCorrectionLevel, hints
+                    qrCodeContents, errorCorrectionLevel, hints
             );
         } catch (WriterException e) {
             throw new ChessQRCodeException(
@@ -239,7 +239,7 @@ public class ChessQRCodeWriter {
                     ChessQRCodeMode.FEN,
                     "rnbqkbnr/ppp1pppp/8/3p4/4P3/8/PPPP1PPP/RNBQKBNR w KQkq d6 0 2",
                     ImageFormat.PNG,
-                    "./MyQRCode.png"
+                    "./qrcode.png"
             );
 /*
             ChessQRCodeWriter.getInstance().write(
@@ -249,7 +249,7 @@ public class ChessQRCodeWriter {
                             "11.Bf4 b5 12.a4 Bb7 13.Re1 Nd5 14.Bg3 Kc8 15.axb5 cxb5 16.Qd3 Bc6 \n" +
                             "17.Bf5 exf5 18.Rxe7 Bxe7 19.c4 1-0",
                     ImageFormat.PNG,
-                    "./MyQRCode.png"
+                    "./qrcode.png"
             );
 */
         } catch (ChessQRCodeException e) {
index 75df9f9..a0e80c5 100644 (file)
@@ -81,8 +81,8 @@ public final class ChessQRMatrixUtils {
 
 
     static ByteMatrix embedChessLogoPattern(ByteMatrix byteMatrix) {
-        if (byteMatrix == null || byteMatrix.getWidth() < CHESS_LOGO_PATTERN_LENGTH + 14) // todo: 14
-            return null;
+        if (byteMatrix == null || byteMatrix.getWidth() < CHESS_LOGO_PATTERN_LENGTH + 14)
+            return null; // todo: check for Version
 
         int xStart = (byteMatrix.getWidth() - CHESS_LOGO_PATTERN_LENGTH) / 2;
         int yStart = (byteMatrix.getHeight() - CHESS_LOGO_PATTERN_LENGTH) / 2;
index 1b8bbf7..0e2f585 100644 (file)
@@ -23,7 +23,7 @@ import org.hedgecode.chess.position.Parser;
 import org.hedgecode.chess.position.Position;
 
 /**
- * Result storage for Chess-specific QR Codes data.
+ * Result storage for chess-specific QR Codes data.
  *
  * @author Dmitry Samoshin aka gotty
  */
@@ -34,11 +34,12 @@ public class ChessQRResult {
     private Position position;
     private Game game;
 
-    ChessQRResult(ChessQRCodeMode mode, String contents) {
+    public ChessQRResult(ChessQRCodeMode mode, String contents) {
         this.mode = mode;
         this.contents = contents;
     }
 
+/*
     ChessQRResult(Position position) {
         this.position = position;
     }
@@ -46,6 +47,7 @@ public class ChessQRResult {
     ChessQRResult(Game game) {
         this.game = game;
     }
+*/
 
     public ChessQRCodeMode getMode() {
         return mode;
@@ -88,4 +90,14 @@ public class ChessQRResult {
         }
     }
 
+    public static ChessQRCodeMode getMode(String contents) {
+        return ChessQRCodeMode.byCode(
+                contents.substring(0, ChessQRCodeMode.CODE_LENGTH)
+        );
+    }
+
+    public static String getContents(String contents) {
+        return contents.substring(ChessQRCodeMode.CODE_LENGTH);
+    }
+
 }
diff --git a/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipChessQRCodeReader.java b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipChessQRCodeReader.java
new file mode 100644 (file)
index 0000000..8578a8b
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2018-2019. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.qrcode.zip;
+
+import java.io.File;
+
+import com.google.zxing.common.DecoderResult;
+
+import org.hedgecode.chess.qrcode.ChessQRCodeException;
+import org.hedgecode.chess.qrcode.ChessQRCodeMode;
+import org.hedgecode.chess.qrcode.ChessQRCodeReader;
+import org.hedgecode.chess.qrcode.ChessQRResult;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class ZipChessQRCodeReader extends ChessQRCodeReader {
+
+    private static ZipChessQRCodeReader _instance = new ZipChessQRCodeReader();
+
+    protected ZipChessQRCodeReader() {
+        super();
+    }
+
+    @Override
+    protected ChessQRResult createResult(DecoderResult decoderResult) throws ChessQRCodeException {
+        String result = decoderResult.getText();
+        ChessQRCodeMode mode = ChessQRResult.getMode(result);
+
+        if (ChessQRCodeMode.ZIP.equals(mode)) {
+            String contents = ZipContentInflater.inflate(
+                    ChessQRResult.getContents(result)
+            );
+
+            mode = ChessQRResult.getMode(contents);
+            if (mode == null) {
+                throw new ChessQRCodeException(
+                        ChessQRCodeException.Type.READ, "read.unknown.chess.mode"
+                );
+            }
+
+            return new ChessQRResult(
+                    mode,
+                    ChessQRResult.getContents(contents)
+            );
+        } else {
+            return super.createResult(decoderResult);
+        }
+    }
+
+    public static ZipChessQRCodeReader getInstance() {
+        return _instance;
+    }
+
+
+    public static void main(String[] args) {
+        try {
+            ChessQRResult qrCodeResult = ZipChessQRCodeReader.getInstance().read(
+                    new File("qrcode.png")
+            );
+            System.out.println("Decoded format = " + qrCodeResult.getMode());
+            System.out.println("Decoded text = " + qrCodeResult.getContents());
+        } catch (ChessQRCodeException e) {
+            System.out.println("Could not decode QR Code. Exception: " + e.getMessage());
+        }
+    }
+
+}
diff --git a/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipChessQRCodeWriter.java b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipChessQRCodeWriter.java
new file mode 100644 (file)
index 0000000..6b92e76
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2018-2019. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.qrcode.zip;
+
+import java.util.Map;
+
+import com.google.zxing.EncodeHintType;
+import com.google.zxing.common.BitMatrix;
+import org.hedgecode.chess.img.ImageFormat;
+import org.hedgecode.chess.qrcode.ChessQRCodeException;
+import org.hedgecode.chess.qrcode.ChessQRCodeMode;
+import org.hedgecode.chess.qrcode.ChessQRCodeWriter;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class ZipChessQRCodeWriter extends ChessQRCodeWriter {
+
+    private static ChessQRCodeWriter _instance = new ZipChessQRCodeWriter();
+
+    protected ZipChessQRCodeWriter() {
+        super();
+    }
+
+    @Override
+    protected BitMatrix encode(
+            ChessQRCodeMode mode,
+            String contents,
+            int width, int height,
+            Map<EncodeHintType,?> hints)
+            throws ChessQRCodeException
+    {
+        if (mode == null || mode.isArchive()) {
+            throw new ChessQRCodeException(
+                    ChessQRCodeException.Type.WRITE, "write.qrcode.mode.null"
+            );
+        }
+
+        if (contents == null || contents.isEmpty()) {
+            throw new ChessQRCodeException(
+                    ChessQRCodeException.Type.WRITE, "write.qrcode.contents.empty"
+            );
+        }
+
+        String zipContents = ZipContentDeflater.deflate(
+                mode.name().concat(contents)
+        );
+
+        return super.encode(
+                ChessQRCodeMode.ZIP, zipContents, width, height, hints
+        );
+    }
+
+    public static ChessQRCodeWriter getInstance() {
+        return _instance;
+    }
+
+
+    public static void main(String[] args) {
+        try {
+            ZipChessQRCodeWriter.getInstance().write(
+                    ChessQRCodeMode.PGN,
+                    "1.e4 c6 2.d4 d5 3.Nc3 dxe4 4.Nxe4 Nd7 5.Ng5 Ngf6 6.Bd3 e6 7.N1f3 h6\n" +
+                            "8.Nxe6 Qe7 9.O-O fxe6 10.Bg6+ Kd8 {Каспаров встряхнул головой} \n" +
+                            "11.Bf4 b5 12.a4 Bb7 13.Re1 Nd5 14.Bg3 Kc8 15.axb5 cxb5 16.Qd3 Bc6 \n" +
+                            "17.Bf5 exf5 18.Rxe7 Bxe7 19.c4 1-0",
+                    ImageFormat.PNG,
+                    "./qrcode.png"
+            );
+        } catch (ChessQRCodeException e) {
+            System.out.println("Could not generate QR Code, Exception: " + e.getMessage());
+        }
+    }
+
+}
diff --git a/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipContentDeflater.java b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipContentDeflater.java
new file mode 100644 (file)
index 0000000..5a2c4fe
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2018-2019. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.qrcode.zip;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.zip.Deflater;
+import java.util.zip.DeflaterOutputStream;
+
+import org.hedgecode.chess.qrcode.ChessQRCodeConstants;
+import org.hedgecode.chess.qrcode.ChessQRCodeException;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class ZipContentDeflater {
+
+    public static String deflate(String contents) throws ChessQRCodeException {
+        byte[] bytes = contents.getBytes(
+                ChessQRCodeConstants.CHARSET
+        );
+        if (bytes.length > ZipContentUtils.getMaxContentLength()) {
+            throw new ChessQRCodeException(
+                    ChessQRCodeException.Type.PARSE, "zip.max.length.error"
+            );
+        }
+        ByteArrayOutputStream bout = new ByteArrayOutputStream();
+        DeflaterOutputStream zout = new DeflaterOutputStream(
+                bout, new Deflater(Deflater.BEST_COMPRESSION)
+        );
+        try {
+            try {
+                zout.write(bytes);
+            } finally {
+                zout.close();
+                bout.close();
+            }
+        } catch (IOException e) {
+            throw new ChessQRCodeException(
+                    ChessQRCodeException.Type.PARSE, "zip.deflate.write.error", e.getMessage()
+            );
+        }
+        return ZipContentUtils.base64Encode(
+                ZipContentUtils.concat(
+                        bytes.length,
+                        bout.toByteArray()
+                )
+        );
+    }
+
+
+    public static void main(String[] args) throws ChessQRCodeException {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < 10; ++i) {
+            sb.append(i).append("bla-bla-bla").append(i);
+        }
+        String zipContents = ZipContentDeflater.deflate(sb.toString());
+        System.out.println("Zip Contents: " + zipContents);
+        System.out.println("Zip length: " + zipContents.length());
+        String contents = ZipContentInflater.inflate(zipContents);
+        System.out.println("Unzip Contents: " + contents);
+        System.out.println("Unziip length: " + contents.length());
+        System.out.println("Equals: " + sb.toString().equals(contents));
+    }
+
+}
diff --git a/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipContentInflater.java b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipContentInflater.java
new file mode 100644 (file)
index 0000000..0e44138
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2018-2019. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.qrcode.zip;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.zip.InflaterInputStream;
+
+import org.hedgecode.chess.qrcode.ChessQRCodeConstants;
+import org.hedgecode.chess.qrcode.ChessQRCodeException;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class ZipContentInflater {
+
+    public static String inflate(String zipContents) throws ChessQRCodeException {
+        byte[] zipBytes = ZipContentUtils.base64Decode(zipContents);
+        int length = ZipContentUtils.getZipLength(zipBytes);
+        if (length > ZipContentUtils.getMaxContentLength()) {
+            throw new ChessQRCodeException(
+                    ChessQRCodeException.Type.PARSE, "zip.max.length.error"
+            );
+        }
+        byte[] resultBytes = new byte[length];
+        InflaterInputStream zin = new InflaterInputStream(
+                new ByteArrayInputStream(
+                        ZipContentUtils.getZipBytes(zipBytes)
+                )
+        );
+        try {
+            try {
+                length = zin.read(resultBytes, 0, length); // todo : check length
+            } finally {
+                zin.close();
+            }
+        } catch (IOException e) {
+            throw new ChessQRCodeException(
+                    ChessQRCodeException.Type.PARSE, "zip.inflate.read.error", e.getMessage()
+            );
+        }
+        return new String(
+                resultBytes, 0, length, ChessQRCodeConstants.CHARSET
+        );
+    }
+
+}
diff --git a/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipContentUtils.java b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipContentUtils.java
new file mode 100644 (file)
index 0000000..dfd7901
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2018-2019. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.qrcode.zip;
+
+import java.util.Arrays;
+import java.util.Base64;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class ZipContentUtils {
+
+    private static final int LENGTH_BYTE_COUNT = 4;
+
+    private static final int MAX_CONTENT_LENGTH = 65536;
+
+    public static int getMaxContentLength() {
+        return MAX_CONTENT_LENGTH;
+    }
+
+    public static int getZipLength(byte[] zipBytes) {
+        return fromByteArray(
+                Arrays.copyOf(zipBytes, LENGTH_BYTE_COUNT)
+        );
+    }
+
+    public static byte[] getZipBytes(byte[] zipBytes) {
+        return Arrays.copyOfRange(
+                zipBytes, LENGTH_BYTE_COUNT, zipBytes.length
+        );
+    }
+
+    public static byte[] concat(int length, byte[] bytes) {
+        return concat(
+                toByteArray(length),
+                bytes
+        );
+    }
+
+    public static String base64Encode(byte[] bytes) {
+        return Base64.getEncoder().encodeToString(bytes);
+    }
+
+    public static byte[] base64Decode(String value) {
+        return Base64.getDecoder().decode(value);
+    }
+
+    private static byte[] toByteArray(int value) {
+        return new byte[] {
+                (byte) (value >> 24),
+                (byte) (value >> 16),
+                (byte) (value >> 8),
+                (byte) (value)
+        };
+    }
+
+    private static int fromByteArray(byte[] bytes) {
+        return bytes[0] << 24
+                | (bytes[1] & 0xFF) << 16
+                | (bytes[2] & 0xFF) << 8
+                | (bytes[3] & 0xFF);
+    }
+
+    private static byte[] concat(byte[]... byteArrays) {
+        int length = 0;
+        for (byte[] bytes : byteArrays) {
+            length += bytes.length;
+        }
+        byte[] concatBytes = new byte[length];
+        length = 0;
+        for (byte[] bytes : byteArrays) {
+            if (bytes.length == 0)
+                continue;
+            System.arraycopy(bytes, 0, concatBytes, length, bytes.length);
+            length += bytes.length;
+        }
+        return concatBytes;
+    }
+
+    private ZipContentUtils() {
+        throw new AssertionError(
+                "No org.hedgecode.chess.qrcode.zip.ZipContentUtils instances!"
+        );
+    }
+
+}
index 5c7f3a1..39911c2 100644 (file)
@@ -28,3 +28,6 @@ write.qrcode.contents.empty=Input data is empty
 write.qrcode.size.incorrect=Incorrect QR Code size specified
 write.qrcode.chess.incorrect=Specified size is not suitable for chess QR Code
 write.zxing.qrcode.error=Error encoding of QR code
+zip.max.length.error=Content length exceeds maximum size
+zip.deflate.write.error=Error deflating of content
+zip.inflate.read.error=Error inflating of content
index b396326..5036e83 100644 (file)
@@ -27,3 +27,6 @@ write.qrcode.contents.empty=\u0412\u0445\u043E\u0434\u043D\u044B\u0435 \u0434\u0
 write.qrcode.size.incorrect=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u043E \u0437\u0430\u0434\u0430\u043D \u0440\u0430\u0437\u043C\u0435\u0440 QR \u043A\u043E\u0434\u0430
 write.qrcode.chess.incorrect=\u0417\u0430\u0434\u0430\u043D\u043D\u044B\u0439 \u0440\u0430\u0437\u043C\u0435\u0440 \u043D\u0435 \u043F\u043E\u0434\u0445\u043E\u0434\u0438\u0442 \u0434\u043B\u044F \u0448\u0430\u0445\u043C\u0430\u0442\u043D\u043E\u0433\u043E QR \u043A\u043E\u0434\u0430
 write.zxing.qrcode.error=\u041E\u0448\u0438\u0431\u043A\u0430 \u043A\u043E\u0434\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F QR \u043A\u043E\u0434\u0430
+zip.max.length.error=\u0414\u043B\u0438\u043D\u0430 \u0441\u043E\u0434\u0435\u0440\u0436\u0438\u043C\u043E\u0433\u043E \u043F\u0440\u0435\u0432\u044B\u0448\u0430\u0435\u0442 \u043C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u043E \u0434\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u044B\u0439 \u0440\u0430\u0437\u043C\u0435\u0440
+zip.deflate.write.error=\u041E\u0448\u0438\u0431\u043A\u0430 \u0430\u0440\u0445\u0438\u0432\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u0441\u043E\u0434\u0435\u0440\u0436\u0438\u043C\u043E\u0433\u043E
+zip.inflate.read.error=\u041E\u0448\u0438\u0431\u043A\u0430 \u0440\u0430\u0437\u0430\u0440\u0445\u0438\u0432\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u0441\u043E\u0434\u0435\u0440\u0436\u0438\u043C\u043E\u0433\u043E