view dwtx/jface/text/templates/persistence/TemplateStore.d @ 153:f70d9508c95c

Fix java Collection imports
author Frank Benoit <benoit@tionex.de>
date Mon, 25 Aug 2008 00:27:31 +0200
parents b3dec671d1f7
children 1a5b8f8129df
line wrap: on
line source

/*******************************************************************************
 * Copyright (c) 2000, 2006 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 * Port to the D programming language:
 *     Frank Benoit <benoit@tionex.de>
 *******************************************************************************/
module dwtx.jface.text.templates.persistence.TemplateStore;

import dwtx.jface.text.templates.persistence.TemplatePersistenceData; // packageimport
import dwtx.jface.text.templates.persistence.TemplateReaderWriter; // packageimport
import dwtx.jface.text.templates.persistence.TemplatePersistenceMessages; // packageimport


import dwt.dwthelper.utils;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import dwtx.dwtxhelper.Collection;



import dwtx.core.runtime.Assert;
import dwtx.jface.preference.IPersistentPreferenceStore;
import dwtx.jface.preference.IPreferenceStore;
import dwtx.jface.text.templates.ContextTypeRegistry;
import dwtx.jface.text.templates.Template;
import dwtx.jface.text.templates.TemplateException;
import dwtx.jface.util.IPropertyChangeListener;
import dwtx.jface.util.PropertyChangeEvent;

/**
 * A collection of templates. Clients may instantiate this class. In order to
 * load templates contributed using the <code>dwtx.ui.editors.templates</code>
 * extension point, use a <code>ContributionTemplateStore</code>.
 *
 * @since 3.0
 */
public class TemplateStore {
    /** The stored templates. */
    private const List fTemplates= new ArrayList();
    /** The preference store. */
    private IPreferenceStore fPreferenceStore;
    /**
     * The key into <code>fPreferenceStore</code> the value of which holds custom templates
     * encoded as XML.
     */
    private String fKey;
    /**
     * The context type registry, or <code>null</code> if all templates regardless
     * of context type should be loaded.
     */
    private ContextTypeRegistry fRegistry;
    /**
     * Set to <code>true</code> if property change events should be ignored (e.g. during writing
     * to the preference store).
     *
     * @since 3.2
     */
    private bool fIgnorePreferenceStoreChanges= false;
    /**
     * The property listener, if any is registered, <code>null</code> otherwise.
     *
     * @since 3.2
     */
    private IPropertyChangeListener fPropertyListener;


    /**
     * Creates a new template store.
     *
     * @param store the preference store in which to store custom templates
     *        under <code>key</code>
     * @param key the key into <code>store</code> where to store custom
     *        templates
     */
    public this(IPreferenceStore store, String key) {
        Assert.isNotNull(store);
        Assert.isNotNull(key);
        fPreferenceStore= store;
        fKey= key;
    }

    /**
     * Creates a new template store with a context type registry. Only templates
     * that specify a context type contained in the registry will be loaded by
     * this store if the registry is not <code>null</code>.
     *
     * @param registry a context type registry, or <code>null</code> if all
     *        templates should be loaded
     * @param store the preference store in which to store custom templates
     *        under <code>key</code>
     * @param key the key into <code>store</code> where to store custom
     *        templates
     */
    public this(ContextTypeRegistry registry, IPreferenceStore store, String key) {
        this(store, key);
        fRegistry= registry;
    }

    /**
     * Loads the templates from contributions and preferences.
     *
     * @throws IOException if loading fails.
     */
    public void load()  {
        fTemplates.clear();
        loadContributedTemplates();
        loadCustomTemplates();
    }

    /**
     * Starts listening for property changes on the preference store. If the configured preference
     * key changes, the template store is {@link #load() reloaded}. Call
     * {@link #stopListeningForPreferenceChanges()} to remove any listener and stop the
     * auto-updating behavior.
     *
     * @since 3.2
     */
    public final void startListeningForPreferenceChanges() {
        if (fPropertyListener is null) {
            fPropertyListener= new class()  IPropertyChangeListener {
                public void propertyChange(PropertyChangeEvent event) {
                    /*
                     * Don't load if we are in the process of saving ourselves. We are in sync anyway after the
                     * save operation, and clients may trigger reloading by listening to preference store
                     * updates.
                     */
                    if (!fIgnorePreferenceStoreChanges && fKey.equals(event.getProperty()))
                        try {
                            load();
                        } catch (IOException x) {
                            handleException(x);
                        }
                }
            };
            fPreferenceStore.addPropertyChangeListener(fPropertyListener);
        }

    }

    /**
     * Stops the auto-updating behavior started by calling
     * {@link #startListeningForPreferenceChanges()}.
     *
     * @since 3.2
     */
    public final void stopListeningForPreferenceChanges() {
        if (fPropertyListener !is null) {
            fPreferenceStore.removePropertyChangeListener(fPropertyListener);
            fPropertyListener= null;
        }
    }

    /**
     * Handles an {@link IOException} thrown during reloading the preferences due to a preference
     * store update. The default is to write to stderr.
     *
     * @param x the exception
     * @since 3.2
     */
    protected void handleException(IOException x) {
        x.printStackTrace();
    }

    /**
     * Hook method to load contributed templates. Contributed templates are superseded
     * by customized versions of user added templates stored in the preferences.
     * <p>
     * The default implementation does nothing.</p>
     *
     * @throws IOException if loading fails
     */
    protected void loadContributedTemplates()  {
    }

    /**
     * Adds a template to the internal store. The added templates must have
     * a unique id.
     *
     * @param data the template data to add
     */
    protected void internalAdd(TemplatePersistenceData data) {
        if (!data.isCustom()) {
            // check if the added template is not a duplicate id
            String id= data.getId();
            for (Iterator it= fTemplates.iterator(); it.hasNext();) {
                TemplatePersistenceData d2= cast(TemplatePersistenceData) it.next();
                if (d2.getId() !is null && d2.getId().equals(id))
                    return;
            }
            fTemplates.add(data);
        }
    }

    /**
     * Saves the templates to the preferences.
     *
     * @throws IOException if the templates cannot be written
     */
    public void save()  {
        ArrayList custom= new ArrayList();
        for (Iterator it= fTemplates.iterator(); it.hasNext();) {
            TemplatePersistenceData data= cast(TemplatePersistenceData) it.next();
            if (data.isCustom() && !(data.isUserAdded() && data.isDeleted())) // don't save deleted user-added templates
                custom.add(data);
        }

        StringWriter output= new StringWriter();
        TemplateReaderWriter writer= new TemplateReaderWriter();
        writer.save((TemplatePersistenceData[]) custom.toArray(new TemplatePersistenceData[custom.size()]), output);

        fIgnorePreferenceStoreChanges= true;
        try {
            fPreferenceStore.setValue(fKey, output.toString());
            if ( cast(IPersistentPreferenceStore)fPreferenceStore )
                (cast(IPersistentPreferenceStore)fPreferenceStore).save();
        } finally {
            fIgnorePreferenceStoreChanges= false;
        }
    }

    /**
     * Adds a template encapsulated in its persistent form.
     *
     * @param data the template to add
     */
    public void add(TemplatePersistenceData data) {

        if (!validateTemplate(data.getTemplate()))
            return;

        if (data.isUserAdded()) {
            fTemplates.add(data);
        } else {
            for (Iterator it= fTemplates.iterator(); it.hasNext();) {
                TemplatePersistenceData d2= cast(TemplatePersistenceData) it.next();
                if (d2.getId() !is null && d2.getId().equals(data.getId())) {
                    d2.setTemplate(data.getTemplate());
                    d2.setDeleted(data.isDeleted());
                    d2.setEnabled(data.isEnabled());
                    return;
                }
            }

            // add an id which is not contributed as add-on
            if (data.getTemplate() !is null) {
                TemplatePersistenceData newData= new TemplatePersistenceData(data.getTemplate(), data.isEnabled());
                fTemplates.add(newData);
            }
        }
    }

    /**
     * Removes a template from the store.
     *
     * @param data the template to remove
     */
    public void delete_(TemplatePersistenceData data) {
        if (data.isUserAdded())
            fTemplates.remove(data);
        else
            data.setDeleted(true);
    }

    /**
     * Restores all contributed templates that have been deleted.
     */
    public void restoreDeleted() {
        for (Iterator it= fTemplates.iterator(); it.hasNext();) {
            TemplatePersistenceData data= cast(TemplatePersistenceData) it.next();
            if (data.isDeleted())
                data.setDeleted(false);
        }
    }

    /**
     * Deletes all user-added templates and reverts all contributed templates.
     */
    public void restoreDefaults() {
        try {
            fIgnorePreferenceStoreChanges= true;
            fPreferenceStore.setToDefault(fKey);
        } finally {
            fIgnorePreferenceStoreChanges= false;
        }
        try {
            load();
        } catch (IOException x) {
            // can't log from jface-text
            x.printStackTrace();
        }
    }

    /**
     * Returns all enabled templates.
     *
     * @return all enabled templates
     */
    public Template[] getTemplates() {
        return getTemplates(null);
    }

    /**
     * Returns all enabled templates for the given context type.
     *
     * @param contextTypeId the id of the context type of the requested templates, or <code>null</code> if all templates should be returned
     * @return all enabled templates for the given context type
     */
    public Template[] getTemplates(String contextTypeId) {
        List templates= new ArrayList();
        for (Iterator it= fTemplates.iterator(); it.hasNext();) {
            TemplatePersistenceData data= cast(TemplatePersistenceData) it.next();
            if (data.isEnabled() && !data.isDeleted() && (contextTypeId is null || contextTypeId.equals(data.getTemplate().getContextTypeId())))
                templates.add(data.getTemplate());
        }

        return (Template[]) templates.toArray(new Template[templates.size()]);
    }

    /**
     * Returns the first enabled template that matches the name.
     *
     * @param name the name of the template searched for
     * @return the first enabled template that matches both name and context type id, or <code>null</code> if none is found
     */
    public Template findTemplate(String name) {
        return findTemplate(name, null);
    }

    /**
     * Returns the first enabled template that matches both name and context type id.
     *
     * @param name the name of the template searched for
     * @param contextTypeId the context type id to clip unwanted templates, or <code>null</code> if any context type is OK
     * @return the first enabled template that matches both name and context type id, or <code>null</code> if none is found
     */
    public Template findTemplate(String name, String contextTypeId) {
        Assert.isNotNull(name);

        for (Iterator it= fTemplates.iterator(); it.hasNext();) {
            TemplatePersistenceData data= cast(TemplatePersistenceData) it.next();
            Template template_= data.getTemplate();
            if (data.isEnabled() && !data.isDeleted()
                    && (contextTypeId is null || contextTypeId.equals(template_.getContextTypeId()))
                    && name.equals(template_.getName()))
                return template_;
        }

        return null;
    }

    /**
     * Returns the first enabled template that matches the given template id.
     *
     * @param id the id of the template searched for
     * @return the first enabled template that matches id, or <code>null</code> if none is found
     * @since 3.1
     */
    public Template findTemplateById(String id) {
        TemplatePersistenceData data= getTemplateData(id);
        if (data !is null && !data.isDeleted())
            return data.getTemplate();

        return null;
    }

    /**
     * Returns all template data.
     *
     * @param includeDeleted whether to include deleted data
     * @return all template data, whether enabled or not
     */
    public TemplatePersistenceData[] getTemplateData(bool includeDeleted) {
        List datas= new ArrayList();
        for (Iterator it= fTemplates.iterator(); it.hasNext();) {
            TemplatePersistenceData data= cast(TemplatePersistenceData) it.next();
            if (includeDeleted || !data.isDeleted())
                datas.add(data);
        }

        return (TemplatePersistenceData[]) datas.toArray(new TemplatePersistenceData[datas.size()]);
    }

    /**
     * Returns the template data of the template with id <code>id</code> or
     * <code>null</code> if no such template can be found.
     *
     * @param id the id of the template data
     * @return the template data of the template with id <code>id</code> or <code>null</code>
     * @since 3.1
     */
    public TemplatePersistenceData getTemplateData(String id) {
        Assert.isNotNull(id);
        for (Iterator it= fTemplates.iterator(); it.hasNext();) {
            TemplatePersistenceData data= cast(TemplatePersistenceData) it.next();
            if (id.equals(data.getId()))
                return data;
        }

        return null;
    }

    private void loadCustomTemplates()  {
        String pref= fPreferenceStore.getString(fKey);
        if (pref !is null && pref.trim().length() > 0) {
            Reader input= new StringReader(pref);
            TemplateReaderWriter reader= new TemplateReaderWriter();
            TemplatePersistenceData[] datas= reader.read(input);
            for (int i= 0; i < datas.length; i++) {
                TemplatePersistenceData data= datas[i];
                add(data);
            }
        }
    }

    /**
     * Validates a template against the context type registered in the context
     * type registry. Returns always <code>true</code> if no registry is
     * present.
     *
     * @param template the template to validate
     * @return <code>true</code> if validation is successful or no context
     *         type registry is specified, <code>false</code> if validation
     *         fails
     */
    private bool validateTemplate(Template template_) {
        String contextTypeId= template_.getContextTypeId();
        if (contextExists(contextTypeId)) {
            if (fRegistry !is null)
                try {
                    fRegistry.getContextType(contextTypeId).validate(template_.getPattern());
                } catch (TemplateException e) {
                    return false;
                }
            return true;
        }

        return false;
    }

    /**
     * Returns <code>true</code> if a context type id specifies a valid context type
     * or if no context type registry is present.
     *
     * @param contextTypeId the context type id to look for
     * @return <code>true</code> if the context type specified by the id
     *         is present in the context type registry, or if no registry is
     *         specified
     */
    private bool contextExists(String contextTypeId) {
        return contextTypeId !is null && (fRegistry is null || fRegistry.getContextType(contextTypeId) !is null);
    }

    /**
     * Returns the registry.
     *
     * @return Returns the registry
     */
    protected final ContextTypeRegistry getRegistry() {
        return fRegistry;
    }
}