org.jivesoftware.sparkimpl.plugin.emoticons.EmoticonManager.java Source code

Java tutorial

Introduction

Here is the source code for org.jivesoftware.sparkimpl.plugin.emoticons.EmoticonManager.java

Source

/**
 * $RCSfile: ,v $ $Revision: $ $Date: $
 *
 * Copyright (C) 2004-2011 Jive Software. All rights reserved.
 *
 * 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.jivesoftware.sparkimpl.plugin.emoticons;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipFile;
import javax.swing.ImageIcon;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.jivesoftware.Spark;
import org.jivesoftware.spark.util.URLFileSystem;
import org.jivesoftware.spark.util.log.Log;
import org.jivesoftware.sparkimpl.settings.local.LocalPreferences;
import org.jivesoftware.sparkimpl.settings.local.SettingsManager;
import org.xml.sax.SAXException;

/**
 * Responsible for the handling of all Emoticon packs. Using the
 * EmoticonManager, you can specify any defined Emoticon Pack, retrieve any
 * emoticon based on its text equivalant, and retrieve its associated image url.
 *
 * @author Derek DeMoro
 */
public class EmoticonManager {

    private static EmoticonManager singleton;
    private static final Object LOCK = new Object();

    private Map<String, Collection<Emoticon>> emoticonMap = new HashMap<>();
    private Map<String, ImageIcon> imageMap = new HashMap<>();

    /**
     * The root emoticon directory.
     */
    public static File EMOTICON_DIRECTORY;

    /**
     * Returns the singleton instance of <CODE>EmoticonManager</CODE>, creating
     * it if necessary.
     * <p/>
     *
     * @return the singleton instance of <Code>EmoticonManager</CODE>
     */
    public static EmoticonManager getInstance() {
        // Synchronize on LOCK to ensure that we don't end up creating
        // two singletons.
        synchronized (LOCK) {
            if (null == singleton) {
                EmoticonManager controller = new EmoticonManager();
                singleton = controller;
                return controller;
            }
        }
        return singleton;
    }

    /**
     * Initialize the EmoticonManager
     */
    private EmoticonManager() {
        EMOTICON_DIRECTORY = new File(Spark.getBinDirectory().getParent(), "xtra/emoticons").getAbsoluteFile();

        File[] files = null;
        files = EMOTICON_DIRECTORY.listFiles();

        // If files in this directory, copy this files into the Spark User Home
        // Directory
        if (files != null) {
            // Copy over to allow for non-admins to extract.
            copyFiles();

            final LocalPreferences pref = SettingsManager.getLocalPreferences();
            String emoticonPack = pref.getEmoticonPack();

            try {
                addEmoticonPack(emoticonPack);
            } catch (Exception e) {
                Log.error(e);
            }
        }
    }

    /**
     * Copy the files directly over to an accepted permissions directory.
     */
    private void copyFiles() {
        // Current Plugin directory
        File newEmoticonDir = new File(Spark.getLogDirectory().getParentFile(), "xtra/emoticons").getAbsoluteFile();
        newEmoticonDir.mkdirs();

        File[] files = EMOTICON_DIRECTORY.listFiles();
        for (File file : files) {
            if (file.isFile()) {
                try {
                    // Copy over
                    File newFile = new File(newEmoticonDir, file.getName());

                    // Check timestamps
                    long installerFile = file.lastModified();
                    long copiedFile = newFile.lastModified();
                    if (installerFile > copiedFile) {
                        // Check if File is Zip-File
                        int endIndex = file.getName().indexOf(".zip");

                        if (endIndex > 0) {
                            String unzipURL = file.getName().substring(0, endIndex);
                            File unzipFile = new File(newEmoticonDir, unzipURL);

                            if (!unzipFile.exists() || !checkIfSameFile(file, newFile)) {
                                // Copy over and expand :)
                                URLFileSystem.copy(file.toURI().toURL(), newFile);
                                expandNewPack(newFile, newEmoticonDir);
                            }
                        }
                    }
                } catch (IOException e) {
                    Log.error(e);
                }
            }
        }
        EMOTICON_DIRECTORY = newEmoticonDir;
    }

    private boolean checkIfSameFile(File oldZip, File newZip) {
        boolean result = true;

        ZipFile oldZipFile = null;
        ZipFile newZipFile = null;
        try {
            oldZipFile = new JarFile(oldZip);
            newZipFile = new JarFile(newZip);
            if (oldZipFile.size() == newZipFile.size()) {
                for (Enumeration<?> e = newZipFile.entries(); e.hasMoreElements();) {
                    JarEntry entry = (JarEntry) e.nextElement();
                    if (oldZipFile.getEntry(entry.getName()) == null
                            || entry.hashCode() != oldZipFile.getEntry(entry.getName()).hashCode()) {
                        result = false;
                        break;
                    }
                }
            } else {
                result = false;
            }
        } catch (IOException e) {
            Log.error(e);
        }
        closeFile(newZipFile);
        closeFile(oldZipFile);
        return result;
    }

    private void closeFile(ZipFile zipFile) {
        try {
            if (zipFile != null) {
                zipFile.close();
            }
        } catch (IOException e) {
            Log.error(e);
        }
    }

    /**
     * Returns the active emoticon set within Spark.
     *
     * @return the active set of emoticons.
     */
    public Collection<Emoticon> getActiveEmoticonSet() {
        final LocalPreferences pref = SettingsManager.getLocalPreferences();
        String emoticonPack = null;
        emoticonPack = pref.getEmoticonPack();
        // If EmoticonPack is set
        //When no emoticon set is available, return an empty list
        if (emoticonPack != null) {
            Collection<Emoticon> emoticonSet = emoticonMap.get(emoticonPack);
            Collection<Emoticon> empty = Collections.emptyList();
            return emoticonSet == null ? empty : emoticonSet;
        }
        return Collections.emptyList();
    }

    /**
     * Returns the name of the active emoticon set.
     *
     * @return the name of the active emoticon set.
     */
    public String getActiveEmoticonSetName() {
        final LocalPreferences pref = SettingsManager.getLocalPreferences();
        return pref.getEmoticonPack();
    }

    /**
     * Sets the active emoticon set.
     *
     * @param pack the archive containing the emotiocon pack.
     */
    public void setActivePack(String pack) {
        final LocalPreferences pref = SettingsManager.getLocalPreferences();
        pref.setEmoticonPack(pack);
        SettingsManager.saveSettings();
        imageMap.clear();
    }

    /**
     * Installs a new Adium style emoticon pack into Spark.
     *
     * @param pack the emotiocn pack (contains Emotiocons.plist)
     * @return the name of the newly installed emoticon set.
     */
    public String installPack(File pack) {
        if (!containsEmoticonPList(pack)) {
            return null;
        }

        String name = null;

        // Copy to the emoticon area
        try {
            URLFileSystem.copy(pack.toURI().toURL(), new File(EMOTICON_DIRECTORY, pack.getName()));

            File rootDirectory = unzipPack(pack, EMOTICON_DIRECTORY);
            name = URLFileSystem.getName(rootDirectory.toURI().toURL());
            addEmoticonPack(name);
        } catch (IOException e) {
            Log.error(e);
        }

        return name;
    }

    /**
     * Loads an emoticon set.
     *
     * @param packName the name of the pack.
     */
    public void addEmoticonPack(String packName) {
        File emoticonSet = new File(EMOTICON_DIRECTORY, packName + ".adiumemoticonset");
        if (!emoticonSet.exists()) {
            emoticonSet = new File(EMOTICON_DIRECTORY, packName + ".AdiumEmoticonset");
        }

        if (!emoticonSet.exists()) {
            emoticonSet = new File(EMOTICON_DIRECTORY, "Default.adiumemoticonset");
            packName = "Default";
            setActivePack("Default");
        }

        List<Emoticon> emoticons = new ArrayList<>();

        final File plist = new File(emoticonSet, "Emoticons.plist");

        // Create SaxReader and set to non-validating parser.
        // This will allow for non-http problems to not break spark :)
        final SAXReader saxParser = new SAXReader();
        saxParser.setValidation(false);
        try {
            saxParser.setFeature("http://xml.org/sax/features/validation", false);
            saxParser.setFeature("http://xml.org/sax/features/namespaces", false);
            saxParser.setFeature("http://apache.org/xml/features/validation/schema", false);
            saxParser.setFeature("http://apache.org/xml/features/validation/schema-full-checking", false);
            saxParser.setFeature("http://apache.org/xml/features/validation/dynamic", false);
            saxParser.setFeature("http://apache.org/xml/features/allow-java-encodings", true);
            saxParser.setFeature("http://apache.org/xml/features/continue-after-fatal-error", true);
            saxParser.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
            saxParser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
        } catch (SAXException e) {
            e.printStackTrace();
        }

        Document emoticonFile;
        try {
            emoticonFile = saxParser.read(plist);
        } catch (DocumentException e) {
            Log.error(e);
            return;
        }

        Node root = emoticonFile.selectSingleNode("/plist/dict/dict");

        List<?> keyList = root.selectNodes("key");
        List<?> dictonaryList = root.selectNodes("dict");

        Iterator<?> keys = keyList.iterator();
        Iterator<?> dicts = dictonaryList.iterator();

        while (keys.hasNext()) {
            Element keyEntry = (Element) keys.next();
            String key = keyEntry.getText();

            Element dict = (Element) dicts.next();
            String name = dict.selectSingleNode("string").getText();

            // Load equivilants
            final List<String> equivs = new ArrayList<>();
            final List<?> equivilants = dict.selectNodes("array/string");
            equivilants.stream().map((equivilant1) -> (Element) equivilant1)
                    .map((equivilant) -> equivilant.getText()).forEach((equivilantString) -> {
                        equivs.add(equivilantString);
                    });

            final Emoticon emoticon = new Emoticon(key, name, equivs, emoticonSet);
            emoticons.add(emoticon);
        }

        emoticonMap.put(packName, emoticons);
    }

    /**
     * Retrieve the URL to an emoticon.
     *
     * @param emoticon the emoticon.
     * @return the URL of the image.
     */
    public URL getEmoticonURL(Emoticon emoticon) {
        final String imageName = emoticon.getImageName();

        File file = new File(emoticon.getEmoticonDirectory(), imageName);
        try {
            return file.toURI().toURL();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Retrieves the associated key emoticon.
     *
     * @param packName the name of the Archive Pack File.
     * @param key the key.
     * @return the emoticon.
     */
    public Emoticon getEmoticon(String packName, String key) {
        final Collection<Emoticon> emoticons = emoticonMap.get(packName);
        if (emoticons == null) {
            return null;
        }
        for (Emoticon emoticon : emoticons) {
            for (String string : emoticon.getEquivalants()) {
                if (key.equals(string)) {
                    return emoticon;
                }
            }
        }

        return null;
    }

    /**
     * Returns the <code>Emoticon</code> associated with the given key. Note:
     * This gets the emoticon from the active emoticon pack.
     *
     * @param key the key.
     * @return the Emoticon found. If no emoticon is found, null is returned.
     */
    public Emoticon getEmoticon(String key) {
        final Collection<Emoticon> emoticons = emoticonMap.get(getActiveEmoticonSetName());

        for (Emoticon emoticon : emoticons) {
            for (String string : emoticon.getEquivalants()) {
                if (key.toLowerCase().equals(string.toLowerCase())) {
                    return emoticon;
                }
            }
        }

        return null;
    }

    /**
     * Returns the Icon that is mapped to a given key.
     *
     * @param key the key to search for.
     * @return the Icon representing the key.
     */
    public ImageIcon getEmoticonImage(String key) {
        final Emoticon emoticon = getEmoticon(key);
        if (emoticon != null) {
            ImageIcon icon = imageMap.get(key);
            if (icon == null) {
                URL url = getEmoticonURL(emoticon);
                icon = new ImageIcon(url);
                imageMap.put(key, icon);
            }

            return imageMap.get(key);
        }

        return null;
    }

    /**
     * Returns a list of all available emoticon packs.
     *
     * @return Collection of Emoticon Pack names.
     */
    public Collection<String> getEmoticonPacks() {
        final List<String> emoticonList = new ArrayList<>();

        File[] dirs = EMOTICON_DIRECTORY.listFiles();

        // If no emoticons are available
        if (dirs == null) {

            return null;
        }

        for (File file : dirs) {
            if (file.isDirectory() && file.getName().toLowerCase().endsWith("adiumemoticonset")) {
                try {
                    String name = URLFileSystem.getName(file.toURI().toURL());
                    name = name.replaceAll("adiumemoticonset", "");
                    name = name.replaceAll("AdiumEmoticonset", "");
                    emoticonList.add(name);
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                }
            }
        }
        return emoticonList;
    }

    /**
     * Expands any zipped Emoticon Packs.
     *
     * @param file File to unpack.
     * @param dist Dist file.
     */
    private void expandNewPack(File file, File dist) {
        URL url = null;
        try {
            url = file.toURI().toURL();
        } catch (MalformedURLException e) {
            Log.error(e);
        }
        String name = URLFileSystem.getName(url);
        File directory = new File(dist, name);

        // Unzip contents into directory
        unzipPack(file, directory.getParentFile());
    }

    /**
     * Checks zip file for the Emoticons.plist file. This is Sparks way of
     * detecting a valid file.
     *
     * @param zip the zip file to check.
     * @return true if the EmoticonPlist exists in the archive.
     */
    private boolean containsEmoticonPList(File zip) {
        ZipFile zipFile = null;
        boolean result = false;
        try {
            zipFile = new JarFile(zip);
            for (Enumeration<?> e = zipFile.entries(); e.hasMoreElements();) {
                JarEntry entry = (JarEntry) e.nextElement();
                if (entry.getName().contains("Emoticons.plist")) {
                    result = true;
                    break;
                }
            }
        } catch (IOException e) {
            Log.error(e);
        }
        closeFile(zipFile);
        return result;
    }

    /**
     * Unzips a theme from a ZIP file into a directory.
     *
     * @param zip the ZIP file
     * @param dir the directory to extract the plugin to.
     * @return the root directory.
     */
    private File unzipPack(File zip, File dir) {
        File rootDirectory = null;
        try {
            ZipFile zipFile = new JarFile(zip);

            dir.mkdir();
            for (Enumeration<?> e = zipFile.entries(); e.hasMoreElements();) {
                JarEntry entry = (JarEntry) e.nextElement();
                File entryFile = new File(dir, entry.getName());
                // Ignore any manifest.mf entries.
                if (entry.getName().toLowerCase().endsWith("manifest.mf")) {
                    continue;
                }

                if (entry.isDirectory() && rootDirectory == null) {
                    rootDirectory = entryFile;
                }

                if (!entry.isDirectory()) {
                    entryFile.getParentFile().mkdirs();
                    FileOutputStream out = new FileOutputStream(entryFile);
                    InputStream zin = zipFile.getInputStream(entry);
                    byte[] b = new byte[512];
                    int len;
                    while ((len = zin.read(b)) != -1) {
                        out.write(b, 0, len);
                    }
                    out.flush();
                    out.close();
                    zin.close();
                }
            }
            zipFile.close();
        } catch (IOException e) {
            Log.error("Error unzipping emoticon pack", e);
        }

        return rootDirectory;
    }

}