org.b3log.solo.util.Skins.java Source code

Java tutorial

Introduction

Here is the source code for org.b3log.solo.util.Skins.java

Source

/*
 * Copyright (c) 2009, 2010, 2011, 2012, B3log Team
 *
 * 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.b3log.solo.util;

import java.io.File;
import java.io.FileFilter;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.b3log.latke.Keys;
import org.b3log.latke.Latkes;
import org.b3log.latke.cache.PageCaches;
import org.b3log.latke.service.ServiceException;
import org.b3log.latke.util.Locales;
import org.b3log.latke.util.Stopwatchs;
import org.b3log.latke.util.freemarker.Templates;
import org.b3log.solo.SoloServletListener;
import org.b3log.solo.model.Preference;
import org.b3log.solo.service.PreferenceMgmtService;
import static org.b3log.solo.model.Skin.*;
import org.json.JSONArray;
import org.json.JSONObject;

/**
 * Skin utilities.
 *
 * @author <a href="mailto:DL88250@gmail.com">Liang Ding</a>
 * @version 1.0.2.6, Jun 12, 2012
 * @since 0.3.1
 */
public final class Skins {

    /**
     * Logger.
     */
    private static final Logger LOGGER = Logger.getLogger(Skins.class.getName());
    /**
     * Properties map.
     */
    private static final Map<String, Map<String, String>> LANG_MAP = new HashMap<String, Map<String, String>>();

    /**
     * Private default constructor.
     */
    private Skins() {
    }

    /**
     * Fills the specified data model with the current skink's language 
     * configurations.
     * 
     * @param localeString the specified locale string
     * @param currentSkinDirName the specified current skin directory name
     * @param dataModel the specified data model
     * @throws ServiceException service exception 
     */
    public static void fillSkinLangs(final String localeString, final String currentSkinDirName,
            final Map<String, Object> dataModel) throws ServiceException {
        Stopwatchs.start("Fill Skin Langs");

        try {
            final String langName = currentSkinDirName + "." + localeString;
            Map<String, String> langs = LANG_MAP.get(langName);
            if (null == langs) {
                LANG_MAP.clear(); // Collect unused skin languages

                LOGGER.log(Level.INFO, "Loading skin[dirName={0}, locale={1}]",
                        new Object[] { currentSkinDirName, localeString });
                langs = new HashMap<String, String>();

                final String webRootPath = SoloServletListener.getWebRoot();

                final String language = Locales.getLanguage(localeString);
                final String country = Locales.getCountry(localeString);

                final Properties props = new Properties();
                props.load(new FileReader(
                        webRootPath + "skins" + File.separator + currentSkinDirName + File.separator + Keys.LANGUAGE
                                + File.separator + Keys.LANGUAGE + '_' + language + '_' + country + ".properties"));
                final Set<Object> keys = props.keySet();
                for (final Object key : keys) {
                    langs.put((String) key, props.getProperty((String) key));
                }

                LANG_MAP.put(langName, langs);
                LOGGER.log(Level.INFO, "Loaded skin[dirName={0}, locale={1}, keyCount={2}]",
                        new Object[] { currentSkinDirName, localeString, langs.size() });
            }

            dataModel.putAll(langs);
        } catch (final IOException e) {
            LOGGER.log(Level.SEVERE, "Fills skin langs failed", e);
            throw new ServiceException(e);
        } finally {
            Stopwatchs.end();
        }
    }

    /**
     * Loads skins for the specified preference and initializes templates 
     * loading.
     * 
     * <p>
     * If the skins directory has been changed, persists the change into 
     * preference.
     * </p>
     *
     * @param preference the specified preference
     * @throws Exception exception
     */
    public static void loadSkins(final JSONObject preference) throws Exception {
        Stopwatchs.start("Load Skins");

        LOGGER.info("Loading skins....");

        final Set<String> skinDirNames = getSkinDirNames();
        LOGGER.log(Level.FINER, "Loaded skins[dirNames={0}]", skinDirNames);
        final JSONArray skinArray = new JSONArray();
        for (final String dirName : skinDirNames) {
            final JSONObject skin = new JSONObject();
            final String name = getSkinName(dirName);
            if (null == name) {
                LOGGER.log(Level.WARNING, "The directory[{0}] does not contain any skin, ignored it", dirName);
                continue;
            }

            skin.put(SKIN_NAME, name);
            skin.put(SKIN_DIR_NAME, dirName);

            skinArray.put(skin);
        }

        final String currentSkinDirName = preference.optString(SKIN_DIR_NAME);
        final String skinName = preference.optString(SKIN_NAME);
        LOGGER.log(Level.INFO, "Current skin[name={0}]", skinName);

        if (!skinDirNames.contains(currentSkinDirName)) {
            LOGGER.log(Level.WARNING,
                    "Configred skin[dirName={0}] can not find, try to use " + "default skin[dirName=ease] instead.",
                    currentSkinDirName);
            if (!skinDirNames.contains("ease")) {
                LOGGER.log(Level.SEVERE, "Can not find skin[dirName=ease]");

                throw new IllegalStateException(
                        "Can not find default skin[dirName=ease], please redeploy your B3log Solo and make sure "
                                + "contains this default skin!");
            }

            preference.put(SKIN_DIR_NAME, "ease");
            preference.put(SKIN_NAME, "ease");

            PreferenceMgmtService.getInstance().updatePreference(preference);
            PageCaches.removeAll(); // Clears cache manually.
        }

        final String skinsString = skinArray.toString();
        if (!skinsString.equals(preference.getString(SKINS))) {
            LOGGER.log(Level.INFO,
                    "The skins directory has been changed, persists " + "the change into preference");
            preference.put(SKINS, skinsString);
            PreferenceMgmtService.getInstance().updatePreference(preference);
            PageCaches.removeAll(); // Clears cache manually.
        }

        if (preference.getBoolean(Preference.PAGE_CACHE_ENABLED)) {
            Latkes.enablePageCache();
        } else {
            Latkes.disablePageCache();
        }

        setDirectoryForTemplateLoading(preference.getString(SKIN_DIR_NAME));

        final String localeString = preference.getString(Preference.LOCALE_STRING);
        if ("zh_CN".equals(localeString)) {
            TimeZones.setTimeZone("Asia/Shanghai");
        }

        LOGGER.info("Loaded skins....");

        Stopwatchs.end();
    }

    /**
     * Sets the directory for template loading with the specified skin directory
     * name, and sets the directory for mobile request template loading.
     * 
     * @param skinDirName the specified skin directory name
     */
    public static void setDirectoryForTemplateLoading(final String skinDirName) {
        try {
            final String webRootPath = SoloServletListener.getWebRoot();
            final String skinPath = webRootPath + SKINS + File.separator + skinDirName;
            Templates.MAIN_CFG.setDirectoryForTemplateLoading(new File(skinPath));

            Templates.MOBILE_CFG
                    .setDirectoryForTemplateLoading(new File(webRootPath + SKINS + File.separator + "mobile"));
        } catch (final IOException e) {
            LOGGER.log(Level.SEVERE, "Loads skins error!", e);
            throw new IllegalStateException(e);
        }
    }

    /**
     * Gets all skin directory names. Scans the
     * {@linkplain SoloServletListener#getWebRoot() Web root}/skins/ directory,
     * using the subdirectory of it as the skin directory name, for example,
     * <pre>
     * ${Web root}/skins/
     *     <b>default</b>/
     *     <b>mobile</b>/
     *     <b>classic</b>/
     * </pre>
     * Skips files that name starts with . and {@linkplain File#isHidden() 
     * hidden} files.
     *
     * @return a set of skin name, returns an empty set if not found
     */
    public static Set<String> getSkinDirNames() {
        final String webRootPath = SoloServletListener.getWebRoot();
        final File skins = new File(webRootPath + "skins" + File.separator);
        final File[] skinDirs = skins.listFiles(new FileFilter() {

            @Override
            public boolean accept(final File file) {
                return file.isDirectory() && !file.getName().startsWith(".");
            }
        });

        final Set<String> ret = new HashSet<String>();
        if (null == skinDirs) {
            LOGGER.severe("Skin directory is null");

            return ret;
        }

        for (int i = 0; i < skinDirs.length; i++) {
            final File file = skinDirs[i];
            ret.add(file.getName());
        }

        return ret;
    }

    /**
     * Gets the skin name for the specified skin directory name. The skin name
     * was configured in skin.properties file({@code name} as the key) under
     * skin directory specified by the given skin directory name.
     *
     * @param skinDirName the given skin directory name
     * @return skin name, returns {@code null} if not found or error occurs
     * @see #getSkinDirNames()
     */
    public static String getSkinName(final String skinDirName) {
        final String webRootPath = SoloServletListener.getWebRoot();
        final File skins = new File(webRootPath + "skins" + File.separator);
        final File[] skinDirs = skins.listFiles(new FileFilter() {

            @Override
            public boolean accept(final File pathname) {
                return pathname.isDirectory() && pathname.getName().equals(skinDirName) ? true : false;
            }
        });

        if (null == skinDirs) {
            LOGGER.severe("Skin directory is null");

            return null;
        }

        if (1 != skinDirs.length) {
            LOGGER.log(Level.SEVERE, "Skin directory count[{0}]", skinDirs.length);

            return null;
        }

        try {
            final Properties ret = new Properties();
            final String skinPropsPath = skinDirs[0].getPath() + File.separator + "skin.properties";
            ret.load(new FileReader(skinPropsPath));

            return ret.getProperty("name");
        } catch (final Exception e) {
            LOGGER.log(Level.SEVERE, "Read skin configuration error[msg={0}]", e.getMessage());

            return null;
        }
    }
}