All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jaxx.runtime.swing.config.model.ConfigUIModel Maven / Gradle / Ivy

There is a newer version: 3.0-alpha-1
Show newest version
package jaxx.runtime.swing.config.model;

/*
 * #%L
 * JAXX :: Config
 * %%
 * Copyright (C) 2008 - 2014 Code Lutin, Tony Chemit
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as 
 * published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public 
 * License along with this program.  If not, see
 * .
 * #L%
 */

import com.google.common.base.Joiner;
import com.google.common.base.Supplier;
import com.google.common.collect.Lists;
import jaxx.runtime.JAXXUtil;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuiton.config.ApplicationConfig;

import javax.swing.Icon;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import static org.nuiton.i18n.I18n.t;

/**
 * Le modele de l'ui des preferences.
 *
 * Ce modele contient les catégories des options.
 *
 * @author Tony Chemit - [email protected]
 * @since 2.5.11
 */
public class ConfigUIModel implements Iterable {

    /** Logger. */
    private static final Log log = LogFactory.getLog(ConfigUIModel.class);

    public static final String CATEGORY_MODEL_PROPERTY_NAME = "categoryModel";

    /** le dictionnaire des options disponibles par categorie */
    protected final Map categories;

    /**
     * Le fichier où sauvegarder la configuration.
     * @since 2.5.21
     */
    protected final File configFile;

    /** La configuration de l'application */
    protected final Object configBean;

    /** La configuration de l'application */
    protected final ApplicationConfig applicationConfig;

    /** la cateogrie en cours d'utilisation */
    protected CategoryModel categoryModel;

    /**
     * un drapeau pour savoir si la configuration a été modifiée au moins une
     * fois.
     *
     * On utilise ce drapeau lors de la sortie pour verifier s'il faut ou non
     * redemarer l'application (si non en mode standalone)
     */
    protected boolean saved;

    /**
     * un drapeau pour savoir si l'ui de configuration a été lancée en mode
     * standalone ou pas.
     *
     * Si pas lancée en mode standalone, et si la confi a été sauvé on vérifie
     * s'il ne faut pas relancer l'application.
     */
    protected boolean standalone;

    /** Callbacks manager */
    protected CallBacksManager callBacksManager;

    /**
     * optional action to execute (if not null) if no call backs fits.
     *
     * @since 1.4.2
     */
    protected Runnable closeAction;

    /** support of modification notifications */
    protected final PropertyChangeSupport pcs;

    public ConfigUIModel(Supplier config) {
        this(config, config.get());
    }

    public ConfigUIModel(Supplier config, File configFile) {
        this(config, config.get(), configFile);
    }

    public ConfigUIModel(ApplicationConfig applicationConfig) {
        this(applicationConfig, applicationConfig);
    }

    public ConfigUIModel(ApplicationConfig applicationConfig, File configFile) {
        this(applicationConfig, applicationConfig, configFile);
    }

    public ConfigUIModel(Object configBean, ApplicationConfig applicationConfig) {
        this(configBean, applicationConfig, applicationConfig.getUserConfigFile());
    }

    public ConfigUIModel(Object configBean, ApplicationConfig applicationConfig, File configFile) {
        this.configBean = configBean;
        this.applicationConfig = applicationConfig;
        this.configFile = configFile;
        categories = new LinkedHashMap();
        callBacksManager = new CallBacksManager();
        pcs = new PropertyChangeSupport(this);
    }

    /**
     * Ajoute une categorie dans le modele.
     *
     * @param category la categorie a ajouter au modèle.
     */
    public void addCategory(CategoryModel category) {
        if (categories.containsKey(category.getCategory())) {
            throw new IllegalArgumentException(
                    t("config.error.category.already.exists",
                      category.getCategory()));
        }
        categories.put(category.getCategory(), category);
    }

    /**
     * Change la categorie en cours d'édition.
     *
     * @param category l'id de la categorie courante
     */
    public void setCategory(String category) {
        if (!categories.containsKey(category)) {
            throw new IllegalArgumentException(
                    t("config.error.category.not.found", category));
        }
        CategoryModel newCategoryModel = categories.get(category);
        setCategoryModel(newCategoryModel);
        newCategoryModel.firePropertyChange(
                CategoryModel.MODIFIED_PROPERTY_NAME, false,
                getCategoryModel().isModified());
        newCategoryModel.firePropertyChange(
                CategoryModel.VALID_PROPERTY_NAME, false,
                getCategoryModel().isValid());
    }

    /**
     * Registers a new callback.
     *
     * Note: the order of registred callback is used to determine
     * the higher priority of callback to launch if required.
     *
     * @param name        the unique name of a callback
     * @param description the i18n key to describe the action
     * @param icon        the icon of the callBack (used in ui)
     * @param action      the action of the callback
     */
    public void registerCallBack(String name,
                                 String description,
                                 Icon icon,
                                 Runnable action) {
        callBacksManager.registerCallBack(name, description, icon, action);
    }

    /**
     * Registers a option into a known callback.
     *
     * @param name   the name of the callback
     * @param option the option to register for the given callback
     */
    public void registerOptionCallBack(String name, OptionModel option) {
        callBacksManager.registerOption(name, option);
    }

    public void setFinalizer(CallBackFinalizer finalizer) {
        callBacksManager.setFinalizer(finalizer);
    }

    public Runnable getCloseAction() {
        return closeAction;
    }

    public void setCloseAction(Runnable closeAction) {
        this.closeAction = closeAction;
    }

    /**
     * Obtain the dictionnary of callback for all to saved modified options.
     *
     * @return the dictonnary
     */
    public CallBackMap getCallBacksForSaved() {
        return callBacksManager.getCallBacksForSaved(this);
    }

    @Override
    public Iterator iterator() {
        return categories.values().iterator();
    }

    public CategoryModel getCategoryModel() {
        return categoryModel;
    }

    public void setCategoryModel(CategoryModel categoryModel) {
        CategoryModel old = this.categoryModel;
        this.categoryModel = categoryModel;
        firePropertyChange(CATEGORY_MODEL_PROPERTY_NAME, old, categoryModel);
    }

    public boolean isSaved() {
        return saved;
    }

    public void setSaved(boolean saved) {
        this.saved = saved;
    }

    public boolean isStandalone() {
        return standalone;
    }

    public void setStandalone(boolean standalone) {
        this.standalone = standalone;
    }

    public void saveModified() {
        // compute transients keys (to never be saved)
        List transients = new ArrayList();

        applicationConfig.setAdjusting(true);

        try {
            for (OptionModel option : categoryModel) {
                if (option.isModified()) {
                    Object value = option.getValue();
                    if (option.getPropertyName() != null) {

                        // this is a javaBean option, push value via mutator
                        try {
                            PropertyUtils.setProperty(configBean,
                                                      option.getPropertyName(), value);
                        } catch (Exception e) {
                            throw new RuntimeException(
                                    "could not set property [" +
                                    option.getPropertyName() +
                                    "] with value = " + value, e);
                        }
                    } else {

                        // simple option with no javabeans, just push the option
                        // value

                        String toSave;
                        if (value == null) {
                            toSave = null;
                        } else {

                            if (option.isArrayType()) {
                                int length = Array.getLength(value);
                                List values = Lists.newArrayListWithCapacity(length);
                                for (int i = 0; i < length; i++) {
                                    values.add(Array.get(value, i));
                                }
                                toSave = Joiner.on(",").join(values);
                            } else {
                                toSave = value.toString();
                            }
                        }
                        applicationConfig.setOption(option.getKey(), toSave);

                    }
                    // l'option a été sauvegardée, on la marque
                    option.setSaved(true);
                    // this is the new original value
                    option.initValue(value);
                }
                if (option.isTransient()) {
                    transients.add(option.getKey());
                }
            }
        } finally {
            applicationConfig.setAdjusting(false);
        }

        setSaved(true);
        // save config
        // Ano
//        applicationConfig.saveForUser(transients.toArray(new String[transients.size()]));
        try {
            applicationConfig.save(configFile, false, transients.toArray(new String[transients.size()]));
        } catch (IOException eee) {
            if (log.isWarnEnabled()) {
                log.warn(t("config.error.applicationconfig.save", configFile),
                         eee);
            }
        }

        // notify data has changed
        categoryModel.firePropertyChange(
                CategoryModel.MODIFIED_PROPERTY_NAME,
                categoryModel.isModified(), true);
        categoryModel.firePropertyChange(
                CategoryModel.VALID_PROPERTY_NAME,
                false, categoryModel.isValid());
        categoryModel.firePropertyChange(
                CategoryModel.RELOAD_PROPERTY_NAME, false, true);
    }

    public void reset() {
        // reset all modified options of the current category
        for (OptionModel key : categoryModel) {
            if (key.isModified()) {
                key.initValue(key.getOriginalValue());
            }
        }
        // notify data has changed
        categoryModel.firePropertyChange(
                CategoryModel.MODIFIED_PROPERTY_NAME,
                categoryModel.isModified(), true);
        categoryModel.firePropertyChange(
                CategoryModel.VALID_PROPERTY_NAME,
                false, categoryModel.isValid());
        categoryModel.firePropertyChange(
                CategoryModel.RELOAD_PROPERTY_NAME, false, true);
    }

    public int getCategoryIndex(String category) {
        int i = 0;
        for (CategoryModel m : this) {
            if (category.equals(m.getCategory())) {
                return i;
            }
            i++;
        }
        // not found
        return -1;
    }

    public void firePropertyChange(String propertyName,
                                   Object oldValue,
                                   Object newValue) {
        pcs.firePropertyChange(propertyName, oldValue, newValue);
    }

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        pcs.addPropertyChangeListener(listener);
    }

    public void addPropertyChangeListener(String propertyName,
                                          PropertyChangeListener listener) {
        pcs.addPropertyChangeListener(propertyName, listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        pcs.removePropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(String propertyName,
                                             PropertyChangeListener listener) {
        pcs.removePropertyChangeListener(propertyName, listener);
    }

    public boolean hasListeners(String propertyName) {
        return pcs.hasListeners(propertyName);
    }

    public PropertyChangeListener[] getPropertyChangeListeners(
            String propertyName) {
        return pcs.getPropertyChangeListeners(propertyName);
    }

    public PropertyChangeListener[] getPropertyChangeListeners() {
        return pcs.getPropertyChangeListeners();
    }

    public void destroy() {
        JAXXUtil.destroy(pcs);
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        destroy();
    }

    /**
     * @return the underlined application config
     * @deprecated since 2.5.4 use now {@link #getApplicationConfig()}.
     */
    protected ApplicationConfig getConfig() {
        return applicationConfig;
    }

    /**
     * @return the underlined application config
     * @since 2.5.4
     */
    protected ApplicationConfig getApplicationConfig() {
        return applicationConfig;
    }

    protected CallBacksManager getCallBacksManager() {
        return callBacksManager;
    }

    public String getCategoryName(int index) {
        return Lists.newArrayList(categories.keySet()).get(index);
    }
}