package gov.nih.nci.cabig.ctms.tools.configuration;

import org.springframework.transaction.annotation.Transactional;
import gov.nih.nci.cabig.ctms.CommonsSystemException;

import java.util.List;
import java.util.Map;

/**
 * Template base class for {@link Configuration} implementations.
 *
 * @author Rhett Sutphin
 */
public abstract class AbstractConfiguration implements Configuration {
    private Map<String, Object> map;
    private ConfigurationEventDispatchSupport dispatcher;

    protected AbstractConfiguration() {
        dispatcher = new ConfigurationEventDispatchSupport(this);
    }

    public <V> V get(ConfigurationProperty<V> property) {
        ConfigurationEntry entry = getEntry(property);
        if (entry == null) {
            return property.getDefault();
        } else {
            return entry.getValue() == null
                ? null
                : property.fromStorageFormat(entry.getValue());
        }
    }

    public <V> void set(ConfigurationProperty<V> property, V value) {
        ConfigurationEntry entry = getEntry(property);
        if (entry == null) {
            try {
                entry = getConfigurationEntryClass().newInstance();
                entry.setKey(property.getKey());
            } catch (InstantiationException e) {
                throw new CommonsSystemException(
                    "Could not instantiate a new configuration entry of class %s", e,
                    getConfigurationEntryClass().getName());
            } catch (IllegalAccessException e) {
                throw new CommonsSystemException(
                    "Could not instantiate a new configuration entry of class %s", e,
                    getConfigurationEntryClass().getName());
            }
        }
        entry.setValue(value == null ? null : property.toStorageFormat(value));
        store(entry);
        getEventDispatcher().dispatchUpdate(property);
    }

    public boolean isSet(ConfigurationProperty<?> property) {
        return getEntry(property) != null;
    }

    public <V> void reset(ConfigurationProperty<V> property) {
        ConfigurationEntry entry = getEntry(property);
        if (entry != null) remove(entry);
        getEventDispatcher().dispatchUpdate(property);
    }

    protected abstract <V> ConfigurationEntry getEntry(ConfigurationProperty<V> property);

    protected abstract void store(ConfigurationEntry entry);

    protected abstract void remove(ConfigurationEntry entry);

    /**
     * Allows subclasses to specify a subclass of {@link ConfigurationEntry} to
     * use.  The primary anticipated benefit of this is to allow for a
     * {@link DatabaseBackedConfiguration} stored in a table not called
     * "configuration".
     * <p>
     * The default is {@link DefaultConfigurationEntry},
     * which will usually be fine.
     */
    protected Class<? extends ConfigurationEntry> getConfigurationEntryClass() {
        return DefaultConfigurationEntry.class;
    }

    public java.util.Map<String, Object> getMap() {
        if (map == null) map = new DefaultConfigurationMap(this);
        return map;
    }

    protected ConfigurationEventDispatchSupport getEventDispatcher() {
        return dispatcher;
    }

    public synchronized void addConfigurationListener(ConfigurationListener listener) {
        getEventDispatcher().addListener(listener);
    }

    /**
     * Bean-style setter for listeners.  Intended for use in a dependency injection context.
     * Does not remove any existing listeners.
     */
    public synchronized void setConfigurationListeners(List<ConfigurationListener> listeners) {
        for (ConfigurationListener listener : listeners) {
            getEventDispatcher().addListener(listener);
        }
    }
}

@@ -50,73 +50,8 @@
 n;%0A%0A
-import org.springframework.transaction.annotation.Transactional;%0A
 impo
@@ -96,24 +96,24 @@
 mException;%0A
+
 %0Aimport java
@@ -1048,636 +1048,41 @@
     
-try %7B%0A                entry = getConfigurationEntryClass().newInstance();%0A                entry.setKey(property.getKey());%0A            %7D catch (InstantiationException e) %7B%0A                throw new CommonsSystemException(%0A                    %22Could not instantiate a new configuration entry of class %25s%22, e,%0A                    getConfigurationEntryClass().getName());%0A            %7D catch (IllegalAccessException e) %7B%0A                throw new CommonsSystemException(%0A                    %22Could not instantiate a new configuration entry of class %25s%22, e,%0A                    getConfigurationEntryClass().getName());%0A            %7D
+entry = createNewEntry(property);
 %0A   
@@ -2246,24 +2246,24 @@
 ryClass() %7B%0A
-
         retu
@@ -2297,32 +2297,1036 @@
 y.class;%0A    %7D%0A%0A
+    /**%0A     * Allows subclasses to specify how new entries should be created.  The default%0A     * is to create a new instance of the class returned by %7B@link #getConfigurationEntryClass()%7D,%0A     * set the key from the provided %3Ccode%3Eproperty%3C/code%3E and return.%0A     */%0A    protected %3CV%3E ConfigurationEntry createNewEntry(ConfigurationProperty%3CV%3E property) %7B%0A        try %7B%0A            ConfigurationEntry entry = getConfigurationEntryClass().newInstance();%0A            entry.setKey(property.getKey());%0A            return entry;%0A        %7D catch (InstantiationException e) %7B%0A            throw new CommonsSystemException(%0A                %22Could not instantiate a new configuration entry of class %25s%22, e,%0A                getConfigurationEntryClass().getName());%0A        %7D catch (IllegalAccessException e) %7B%0A            throw new CommonsSystemException(%0A                %22Could not instantiate a new configuration entry of class %25s%22, e,%0A                getConfigurationEntryClass().getName());%0A        %7D%0A    %7D%0A%0A
     public java.

Allow subclasses to determine how new entries are created.