PolyGlot.IOHandler.java Source code

Java tutorial

Introduction

Here is the source code for PolyGlot.IOHandler.java

Source

/*
 * Copyright (c) 2014-2015, Draque Thompson, draquemail@gmail.com
 * All rights reserved.
 *
 * Licensed under: Creative Commons Attribution-NonCommercial 4.0 International Public License
 *  See LICENSE.TXT included with this code to read the full license agreement.
    
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package PolyGlot;

import java.awt.Component;
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.util.Iterator;
import org.apache.commons.io.FileUtils;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import javax.imageio.ImageIO;
import javax.swing.JFileChooser;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.poi.util.IOUtils;
import org.w3c.dom.Document;

/**
 * This class handles file IO for PolyGlot
 *
 * @author draque
 */
public class IOHandler {

    /**
     * Gets the dictionary File from the filename of the pgd, whether it's raw
     * XML or an archive
     *
     * @param _filename the filename of the actual file
     * @return the File object of the dictionary
     * @throws java.io.IOException
     */
    public static InputStream getDictFile(String _filename) throws IOException {
        InputStream rawFile = new FileInputStream(_filename);

        if (isFileZipArchive(_filename)) {
            ZipFile zipFile = new ZipFile(_filename);

            ZipEntry xmlEntry = zipFile.getEntry(PGTUtil.dictFileName);

            return zipFile.getInputStream(xmlEntry);
        }

        return rawFile;
    }

    /**
     * Opens and returns image from URL given (can be file path)
     * @param filePath path of image
     * @return BufferedImage
     * @throws IOException in IO 
     */
    public static BufferedImage getImage(String filePath) throws IOException {
        return ImageIO.read(new File(filePath));
    }

    /**
     * Queries user for image file, and returns it
     * @param parent the parent window from which this is called
     * @return the image chosen by the user, null if canceled
     * @throws IOException If the image cannot be opened for some reason
     */
    public static BufferedImage openImageFile(Component parent) throws IOException {
        JFileChooser chooser = new JFileChooser();
        chooser.setDialogTitle("Open Images");
        FileNameExtensionFilter filter = new FileNameExtensionFilter("Image Files", "gif", "jpg", "jpeg", "bmp",
                "png", "wbmp");
        chooser.setFileFilter(filter);
        String fileName;
        chooser.setCurrentDirectory(new File("."));

        if (chooser.showOpenDialog(parent) == JFileChooser.APPROVE_OPTION) {
            fileName = chooser.getSelectedFile().getAbsolutePath();
        } else {
            return null;
        }

        return getImage(fileName);
    }

    /**
     * Gets font from save file if possible, null otherwise
     *
     * @param _path The path of the PGD file
     * @return a Font object if the PGD file is both a zip archive and contains
     * a font
     * @throws java.io.IOException
     * @throws java.awt.FontFormatException
     */
    public static Font getFontFrom(String _path) throws IOException, FontFormatException {
        Font ret = null;

        if (isFileZipArchive(_path)) {
            ZipFile zipFile = new ZipFile(_path);

            ZipEntry fontEntry = zipFile.getEntry(PGTUtil.fontFileName);

            if (fontEntry != null) {
                final File tempFile = File.createTempFile("stream2file", ".tmp");
                tempFile.deleteOnExit();

                FileOutputStream out = new FileOutputStream(tempFile);
                IOUtils.copy(zipFile.getInputStream(fontEntry), out);

                try {
                    ret = Font.createFont(Font.TRUETYPE_FONT, tempFile);
                } catch (FontFormatException e) {
                    throw new FontFormatException(
                            "Could not load language font. Possible incompatible font: " + e.getMessage());
                } catch (IOException e) {
                    throw new FontFormatException("Could not load language font. I/O exception: " + e.getMessage());
                }
            }
        }

        return ret;
    }

    /**
     * Tests whether or not a file is a zip archive
     *
     * @param _fileName the file to test
     * @return true is passed file is a zip archive
     * @throws java.io.FileNotFoundException
     */
    public static boolean isFileZipArchive(String _fileName) throws FileNotFoundException, IOException {
        File file = new File(_fileName);

        // ignore directories and files too small to possibly be archives
        if (file.isDirectory() || file.length() < 4) {
            return false;
        }

        DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
        int test = in.readInt();
        in.close();
        return test == 0x504b0304;
    }

    public static void writeFile(String _fileName, Document doc, DictCore core)
            throws IOException, TransformerException {
        final String tempFileName = "xxTEMPPGTFILExx";
        String directoryPath;

        File finalFile = new File(_fileName);

        directoryPath = finalFile.getParentFile().getAbsolutePath();

        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        StringWriter writer = new StringWriter();
        transformer.transform(new DOMSource(doc), new StreamResult(writer));

        StringBuilder sb = new StringBuilder();

        sb.append(writer.getBuffer().toString());

        // save file to temp location initially.
        final File f = new File(directoryPath, tempFileName);
        final ZipOutputStream out;

        if (System.getProperty("java.version").startsWith("1.6")) {
            out = new ZipOutputStream(new FileOutputStream(f));
        } else {
            out = new ZipOutputStream(new FileOutputStream(f), Charset.forName("ISO-8859-1"));
        }

        ZipEntry e = new ZipEntry(PGTUtil.dictFileName);
        out.putNextEntry(e);

        byte[] data = sb.toString().getBytes("UTF-8");
        out.write(data, 0, data.length);

        out.closeEntry();

        // embed font in PGD archive if applicable
        File fontFile = IOHandler.getFontFile(core.getPropertiesManager().getFontCon());

        if (fontFile != null) {
            byte[] buffer = new byte[1024];
            FileInputStream fis = new FileInputStream(fontFile);
            out.putNextEntry(new ZipEntry(PGTUtil.fontFileName));
            int length;

            while ((length = fis.read(buffer)) > 0) {
                out.write(buffer, 0, length);
            }

            out.closeEntry();
            fis.close();
        }

        // write all logograph images to file
        Iterator<LogoNode> it = core.getLogoCollection().getAllLogos().iterator();
        if (it.hasNext()) {
            out.putNextEntry(new ZipEntry(PGTUtil.logoGraphSavePath));

            while (it.hasNext()) {
                LogoNode curNode = it.next();
                out.putNextEntry(new ZipEntry(PGTUtil.logoGraphSavePath + curNode.getId().toString() + ".png"));

                ImageIO.write(curNode.getLogoGraph(), "png", out);

                out.closeEntry();
            }
        }

        out.finish();
        out.close();

        // attempt to open file in dummy core. On success, copy file to end
        // destination, on fail, delete file, and inform user by bubbling error
        try {
            DictCore test = new DictCore();
            test.readFile(f.getAbsolutePath());

        } catch (Exception ex) {
            f.delete();

            throw new IOException(ex);
        }

        try {
            if (System.getProperty("java.version").startsWith("1.6")) {
                if (finalFile.exists()) {
                    finalFile.delete();
                }
                FileUtils.copyFile(f, finalFile);
            } else {
                java.nio.file.Files.copy(f.toPath(), finalFile.toPath(),
                        java.nio.file.StandardCopyOption.REPLACE_EXISTING);
            }
            f.delete();
        } catch (IOException ex) {
            f.delete();
            throw new IOException("Unable to save file: " + ex.getMessage());
        }
    }

    /**
     * gets the file of the current conlang font from the user's system
     *
     * @param font the font to find a file for
     * @return the font's file if found, null otherwise
     */
    public static File getFontFile(Font font) {
        File ret = null;

        if (font == null) {
            return ret;
        }

        if (System.getProperty("os.name").startsWith("Mac")) {
            ret = IOHandler.getFontFromLocation("/Library/Fonts/", font);

            if (ret == null) {
                ret = IOHandler.getFontFromLocation("/System/Library/Fonts/", font);
            }

            if (ret == null) {
                ret = IOHandler.getFontFromLocation(System.getProperty("user.home") + "/Library/Fonts/", font);
            }
        } else if (System.getProperty("os.name").startsWith("Windows")) {
            ret = getFontFromLocation(System.getenv("WINDIR") + "\\Fonts", font);
        } else {
            // TODO: figure out how to make this work in Linux, maybe?
        }

        // TODO: Inform user if font cannot be found (mostly a mac issue...)
        return ret;
    }

    /**
     * Returns a font's file based on the font and a path
     *
     * @param path path to check for a font
     * @param font font to check for
     * @return the font's file, null otherwise
     */
    private static File getFontFromLocation(String path, Font font) {
        File folder = new File(path);
        File[] listOfFiles = folder.listFiles();
        File ret = null;

        for (File listOfFile : listOfFiles) {
            if (listOfFile.isFile()) {
                File fontFile = loadFont(listOfFile.getPath(), font);

                if (fontFile != null) {
                    ret = fontFile;
                    break;
                }
            }
        }

        return ret;
    }

    /**
     * compares testfont to loaded file. returns file if it represents the font
     *
     * @param path full path of file to test
     * @param testFont font to test against
     * @return file if path leads to passed font, null otherwise
     */
    private static File loadFont(String path, Font testFont) {
        File fontFile = new File(path);
        File ret = null;

        // unrecgnized types won't be loaded
        if (path.toLowerCase().endsWith(".ttf") || path.toLowerCase().endsWith(".otf")
                || path.toLowerCase().endsWith(".ttc") || path.toLowerCase().endsWith(".ttc")
                || path.toLowerCase().endsWith(".dfont")) {
            try {
                Font f = Font.createFont(Font.TRUETYPE_FONT, fontFile);

                // if names match, set ret to return file
                if (f.getName().equals(testFont.getName()) || f.getName().equals(testFont.getName() + " Regular")) {
                    ret = fontFile;
                }

            } catch (FontFormatException e) {
                // null detected and message bubbled to user elsewhere
                ret = null;
            } catch (IOException e) {
                // null detected and message bubbled to user elsewhere
                ret = null;
            }
        }

        return ret;
    }

    /**
     * loads all images into their logographs from archive
     * @param logoCollection logocollection from dictionary core
     * @param fileName name/path of archive
     * @throws java.lang.Exception
     */
    public static void loadImages(LogoCollection logoCollection, String fileName) throws Exception {
        if (!isFileZipArchive(fileName)) {
            return;
        }

        Iterator<LogoNode> it = logoCollection.getAllLogos().iterator();
        ZipFile zipFile = new ZipFile(fileName);

        while (it.hasNext()) {
            LogoNode curNode = it.next();
            ZipEntry imgEntry = zipFile.getEntry(PGTUtil.logoGraphSavePath + curNode.getId().toString() + ".png");

            BufferedImage img = ImageIO.read(zipFile.getInputStream(imgEntry));
            curNode.setLogoGraph(img);
        }
    }
}