From 6ca2f6628d3598b31d0f88e3e0cbb3f79e2c1135 Mon Sep 17 00:00:00 2001
From: Piotr Gawron <piotr.gawron@uni.lu>
Date: Fri, 10 May 2019 15:56:41 +0200
Subject: [PATCH] glyphs are added to the project in a zip parser

---
 .../converter/ComplexZipConverter.java        |   4 +-
 .../lcsb/mapviewer/converter/GlyphParser.java |  60 ++++++++++++++++++
 .../mapviewer/converter/InvalidGlyphFile.java |  48 ++++++++++++++
 .../mapviewer/converter/ProjectFactory.java   |   7 +-
 .../converter/zip/GlyphZipEntryFile.java      |  42 ++++++++++++
 .../converter/zip/ZipEntryFileFactory.java    |  10 +++
 .../converter/ProjectFactoryTest.java         |  35 ++++++++++
 converter/testFiles/complex_with_glyphs.zip   | Bin 0 -> 7179 bytes
 .../java/lcsb/mapviewer/model/Project.java    |   5 ++
 .../model/cache/UploadedFileEntry.java        |   2 +-
 10 files changed, 210 insertions(+), 3 deletions(-)
 create mode 100644 converter/src/main/java/lcsb/mapviewer/converter/GlyphParser.java
 create mode 100644 converter/src/main/java/lcsb/mapviewer/converter/InvalidGlyphFile.java
 create mode 100644 converter/src/main/java/lcsb/mapviewer/converter/zip/GlyphZipEntryFile.java
 create mode 100644 converter/testFiles/complex_with_glyphs.zip

diff --git a/converter/src/main/java/lcsb/mapviewer/converter/ComplexZipConverter.java b/converter/src/main/java/lcsb/mapviewer/converter/ComplexZipConverter.java
index 666db27bc6..9c734eeac9 100644
--- a/converter/src/main/java/lcsb/mapviewer/converter/ComplexZipConverter.java
+++ b/converter/src/main/java/lcsb/mapviewer/converter/ComplexZipConverter.java
@@ -21,6 +21,7 @@ import org.apache.log4j.Logger;
 import lcsb.mapviewer.common.exception.InvalidArgumentException;
 import lcsb.mapviewer.common.exception.InvalidClassException;
 import lcsb.mapviewer.common.exception.NotImplementedException;
+import lcsb.mapviewer.converter.zip.GlyphZipEntryFile;
 import lcsb.mapviewer.converter.zip.ImageZipEntryFile;
 import lcsb.mapviewer.converter.zip.LayoutZipEntryFile;
 import lcsb.mapviewer.converter.zip.ModelZipEntryFile;
@@ -122,7 +123,8 @@ public class ComplexZipConverter {
             continue;
           } else if (zef instanceof ImageZipEntryFile) {
             continue;
-            // imageEntries.add((ImageZipEntryFile) zef);
+          } else if (zef instanceof GlyphZipEntryFile) {
+            continue;
           } else if (!isIgnoredFile(entry.getName())) {
             throw new NotImplementedException("Unknwon entry type: " + zef.getClass());
           }
diff --git a/converter/src/main/java/lcsb/mapviewer/converter/GlyphParser.java b/converter/src/main/java/lcsb/mapviewer/converter/GlyphParser.java
new file mode 100644
index 0000000000..1e7d480fbb
--- /dev/null
+++ b/converter/src/main/java/lcsb/mapviewer/converter/GlyphParser.java
@@ -0,0 +1,60 @@
+package lcsb.mapviewer.converter;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.ZipFile;
+
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.log4j.Logger;
+
+import lcsb.mapviewer.converter.zip.GlyphZipEntryFile;
+import lcsb.mapviewer.model.cache.UploadedFileEntry;
+import lcsb.mapviewer.model.map.layout.graphics.Glyph;
+
+/**
+ * Parser used to extract data about {@link Glyph} from zip file.
+ * 
+ * @author Piotr Gawron
+ * 
+ */
+public class GlyphParser {
+
+  /**
+   * Default class logger.
+   */
+  @SuppressWarnings("unused")
+  private final Logger logger = Logger.getLogger(GlyphParser.class);
+
+  /**
+   * Method that parse zip entry and creates a {@link Glyph} from it.
+   *
+   * @param entry
+   *          zip entry to parse
+   * @param zipFile
+   *          original file where zip entry comes from
+   * @throws IOException
+   *           thrown when there is a problem with zip file
+   * @throws InvalidGlyphFile
+   *           thrown when the zip file contains invalid data
+   */
+  public Glyph parseGlyph(GlyphZipEntryFile entry, ZipFile zipFile) throws InvalidGlyphFile, IOException {
+
+    String filename = FilenameUtils.getName(entry.getFilename());
+    if (filename.toLowerCase().endsWith("png")) {
+      InputStream is = zipFile.getInputStream(zipFile.getEntry(entry.getFilename()));
+
+      UploadedFileEntry file = new UploadedFileEntry();
+      file.setFileContent(IOUtils.toByteArray(is));
+      file.setOriginalFileName(entry.getFilename());
+      file.setLength(file.getFileContent().length);
+
+      Glyph result = new Glyph();
+      result.setFile(file);
+      return result;
+    } else {
+      throw new InvalidGlyphFile("Unknown file in overview images zip archive: " + filename);
+    }
+  }
+
+}
diff --git a/converter/src/main/java/lcsb/mapviewer/converter/InvalidGlyphFile.java b/converter/src/main/java/lcsb/mapviewer/converter/InvalidGlyphFile.java
new file mode 100644
index 0000000000..d126dc1928
--- /dev/null
+++ b/converter/src/main/java/lcsb/mapviewer/converter/InvalidGlyphFile.java
@@ -0,0 +1,48 @@
+package lcsb.mapviewer.converter;
+
+/**
+ * Exception thrown when the zip file containing data about
+ * {@link lcsb.mapviewer.model.map.layout.graphics.Glyph} is invalid.
+ * 
+ * @author Piotr Gawron
+ * 
+ */
+public class InvalidGlyphFile extends InvalidInputDataExecption {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 1L;
+
+  /**
+   * Default constructor.
+   * 
+   * @param e
+   *          parent exception (reason)
+   */
+  public InvalidGlyphFile(Exception e) {
+    super(e);
+  }
+
+  /**
+   * Default constructor.
+   * 
+   * @param message
+   *          exception message
+   * @param e
+   *          parent exception (reason)
+   */
+  public InvalidGlyphFile(String message, Exception e) {
+    super(message, e);
+  }
+
+  /**
+   * Default constructor.
+   * 
+   * @param message
+   *          exception message
+   */
+  public InvalidGlyphFile(String message) {
+    super(message);
+  }
+}
diff --git a/converter/src/main/java/lcsb/mapviewer/converter/ProjectFactory.java b/converter/src/main/java/lcsb/mapviewer/converter/ProjectFactory.java
index 2f193101b4..db770c3ed3 100644
--- a/converter/src/main/java/lcsb/mapviewer/converter/ProjectFactory.java
+++ b/converter/src/main/java/lcsb/mapviewer/converter/ProjectFactory.java
@@ -12,6 +12,7 @@ import java.util.zip.ZipFile;
 import org.apache.log4j.Logger;
 
 import lcsb.mapviewer.common.exception.InvalidArgumentException;
+import lcsb.mapviewer.converter.zip.GlyphZipEntryFile;
 import lcsb.mapviewer.converter.zip.ImageZipEntryFile;
 import lcsb.mapviewer.converter.zip.LayoutZipEntryFile;
 import lcsb.mapviewer.converter.zip.ZipEntryFile;
@@ -21,13 +22,15 @@ import lcsb.mapviewer.model.map.model.Model;
 public class ProjectFactory {
 
   /**
-   * Deafult class clogger.
+   * Default class logger.
    */
   @SuppressWarnings("unused")
   private Logger logger = Logger.getLogger(ProjectFactory.class);
 
   private ComplexZipConverter converter;
 
+  private GlyphParser glyphParser = new GlyphParser();
+
   public ProjectFactory(ComplexZipConverter converter) {
     this.converter = converter;
   }
@@ -59,6 +62,8 @@ public class ProjectFactory {
           ZipEntryFile zef = params.getEntry(entry.getName());
           if (zef instanceof ImageZipEntryFile) {
             imageEntries.add((ImageZipEntryFile) zef);
+          } else if (zef instanceof GlyphZipEntryFile) {
+            project.addGlyph(glyphParser.parseGlyph((GlyphZipEntryFile) zef, zipFile));
           } else if (zef instanceof LayoutZipEntryFile) {
             project.addLayout(
                 converter.layoutZipEntryFileToLayout(params, zipFile, entry, (LayoutZipEntryFile) zef, overlayOrder++));
diff --git a/converter/src/main/java/lcsb/mapviewer/converter/zip/GlyphZipEntryFile.java b/converter/src/main/java/lcsb/mapviewer/converter/zip/GlyphZipEntryFile.java
new file mode 100644
index 0000000000..db9e153626
--- /dev/null
+++ b/converter/src/main/java/lcsb/mapviewer/converter/zip/GlyphZipEntryFile.java
@@ -0,0 +1,42 @@
+package lcsb.mapviewer.converter.zip;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+/**
+ * Structure used to describe a file in a zip archive with single entry about
+ * {@link lcsb.mapviewer.model.map.layout.graphics.Glyph}.
+ * 
+ * @author Piotr Gawron
+ * 
+ */
+public class GlyphZipEntryFile extends ZipEntryFile implements Serializable {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 1L;
+
+  /**
+   * Default constructor.
+   */
+  public GlyphZipEntryFile() {
+
+  }
+
+  /**
+   * Default constructor.
+   * 
+   * @param filename
+   *          {@link ZipEntryFile#filename}
+   * @param inputStream
+   *          input stream with the data for this entry.
+   * @see #baos
+   * @throws IOException
+   *           thrown when there is a problem with accessing input stream
+   */
+  public GlyphZipEntryFile(String filename) {
+    super(filename);
+  }
+
+}
diff --git a/converter/src/main/java/lcsb/mapviewer/converter/zip/ZipEntryFileFactory.java b/converter/src/main/java/lcsb/mapviewer/converter/zip/ZipEntryFileFactory.java
index 1dc84388e4..e647bedc78 100644
--- a/converter/src/main/java/lcsb/mapviewer/converter/zip/ZipEntryFileFactory.java
+++ b/converter/src/main/java/lcsb/mapviewer/converter/zip/ZipEntryFileFactory.java
@@ -38,6 +38,13 @@ public class ZipEntryFileFactory {
    */
   private static final String IMAGES_DIRECTORY = "images/";
 
+  /**
+   * Directory in a zip file where information about
+   * {@link lcsb.mapviewer.model.map.layout.graphics.Glyph} is stored. These
+   * entries should be by default transformed into {@link GlyphZipEntryFile}.
+   */
+  private static final String GLYPHS_DIRECTORY = "glyphs/";
+
   /**
    * Directory in a zip file where information about
    * {@link lcsb.mapviewer.model.map.layout.Layout Layout} is stored. These
@@ -128,6 +135,9 @@ public class ZipEntryFileFactory {
       } else if (directory.equals(IMAGES_DIRECTORY)) {
         ImageZipEntryFile result = new ImageZipEntryFile(entry.getName());
         return result;
+      } else if (directory.equals(GLYPHS_DIRECTORY)) {
+        GlyphZipEntryFile result = new GlyphZipEntryFile(entry.getName());
+        return result;
       } else if (directory.equals(LAYOUT_DIRECTORY)) {
         LayoutZipEntryFile result = createLayoutZipEntryFile(entry.getName(), zipFile.getInputStream(entry));
         return result;
diff --git a/converter/src/test/java/lcsb/mapviewer/converter/ProjectFactoryTest.java b/converter/src/test/java/lcsb/mapviewer/converter/ProjectFactoryTest.java
index 0ab238b07f..9c3be560f4 100644
--- a/converter/src/test/java/lcsb/mapviewer/converter/ProjectFactoryTest.java
+++ b/converter/src/test/java/lcsb/mapviewer/converter/ProjectFactoryTest.java
@@ -10,12 +10,14 @@ import java.util.List;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
+import org.apache.log4j.Logger;
 import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.Test;
 
 import lcsb.mapviewer.converter.ComplexZipConverterTest.MockConverter;
+import lcsb.mapviewer.converter.zip.ModelZipEntryFile;
 import lcsb.mapviewer.converter.zip.ZipEntryFileFactory;
 import lcsb.mapviewer.model.Project;
 import lcsb.mapviewer.model.map.OverviewImage;
@@ -24,6 +26,7 @@ import lcsb.mapviewer.model.map.OverviewModelLink;
 import lcsb.mapviewer.model.map.model.ModelData;
 
 public class ProjectFactoryTest {
+  Logger logger = Logger.getLogger(ProjectFactoryTest.class);
 
   @AfterClass
   public static void tearDownAfterClass() throws Exception {
@@ -126,4 +129,36 @@ public class ProjectFactoryTest {
 
   }
 
+  @Test
+  public void testParseGlyphs() throws Exception {
+    try {
+      ComplexZipConverter converter = new ComplexZipConverter(MockConverter.class);
+      ProjectFactory projectFactory = new ProjectFactory(converter);
+
+      ComplexZipConverterParams params = new ComplexZipConverterParams();
+      params.zipFile(new ZipFile("testFiles/complex_with_glyphs.zip"));
+      params.entry(new ModelZipEntryFile("main.xml", "main", true, false, null));
+
+      ZipFile zipFile = new ZipFile("testFiles/complex_with_glyphs.zip");
+
+      ZipEntryFileFactory factory = new ZipEntryFileFactory();
+      Enumeration<? extends ZipEntry> entries = zipFile.entries();
+      while (entries.hasMoreElements()) {
+        ZipEntry entry = entries.nextElement();
+        if (!entry.isDirectory()) {
+          params.entry(factory.createZipEntryFile(entry, zipFile));
+        }
+      }
+
+      Project project = projectFactory.create(params);
+      assertNotNull(project);
+      assertEquals(1, project.getGlyphs().size());
+      
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+
+  }
+
 }
diff --git a/converter/testFiles/complex_with_glyphs.zip b/converter/testFiles/complex_with_glyphs.zip
new file mode 100644
index 0000000000000000000000000000000000000000..05b2874b50ffbc86ff8e0e75bd6cef5d159cd939
GIT binary patch
literal 7179
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

literal 0
HcmV?d00001

diff --git a/model/src/main/java/lcsb/mapviewer/model/Project.java b/model/src/main/java/lcsb/mapviewer/model/Project.java
index 25f565fa55..9991f192e9 100644
--- a/model/src/main/java/lcsb/mapviewer/model/Project.java
+++ b/model/src/main/java/lcsb/mapviewer/model/Project.java
@@ -698,4 +698,9 @@ public class Project implements Serializable {
     this.glyphs = glyphs;
   }
 
+  public void addGlyph(Glyph parseGlyph) {
+    this.glyphs.add(parseGlyph);
+    parseGlyph.setProject(this);
+  }
+
 }
diff --git a/model/src/main/java/lcsb/mapviewer/model/cache/UploadedFileEntry.java b/model/src/main/java/lcsb/mapviewer/model/cache/UploadedFileEntry.java
index 05d024494f..6d36a152d0 100644
--- a/model/src/main/java/lcsb/mapviewer/model/cache/UploadedFileEntry.java
+++ b/model/src/main/java/lcsb/mapviewer/model/cache/UploadedFileEntry.java
@@ -43,7 +43,7 @@ public class UploadedFileEntry extends FileEntry implements Serializable {
    * Constructor that copies data from the parameter.
    * 
    * @param original
-   *          original object from which data will bbe copied
+   *          original object from which data will be copied
    */
   public UploadedFileEntry(UploadedFileEntry original) {
     super(original);
-- 
GitLab