de.uniluebeck.itm.spyglass.gui.configuration.AbstractDatabindingPreferencePage.java Source code

Java tutorial

Introduction

Here is the source code for de.uniluebeck.itm.spyglass.gui.configuration.AbstractDatabindingPreferencePage.java

Source

/*
 * --------------------------------------------------------------------------------
 * This file is part of the WSN visualization framework SpyGlass. Copyright (C)
 * 2004-2007 by the SwarmNet (www.swarmnet.de) project SpyGlass is free
 * software; you can redistribute it and/or modify it under the terms of the BSD
 * License. Refer to spyglass-licence.txt file in the root of the SpyGlass
 * source tree for further details.
 * --------------------------------------------------------------------------------
 */
package de.uniluebeck.itm.spyglass.gui.configuration;

import org.apache.log4j.Logger;
import org.eclipse.core.databinding.AggregateValidationStatus;
import org.eclipse.core.databinding.Binding;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.observable.ChangeEvent;
import org.eclipse.core.databinding.observable.IChangeListener;
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.core.databinding.observable.list.IListChangeListener;
import org.eclipse.core.databinding.observable.list.ListChangeEvent;
import org.eclipse.core.databinding.observable.list.ListDiffEntry;
import org.eclipse.core.databinding.observable.value.IValueChangeListener;
import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.databinding.swt.SWTObservables;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.PreferencePage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;

import de.uniluebeck.itm.spyglass.util.SpyglassLoggerFactory;

// --------------------------------------------------------------------------------
/**
 * This is common superclass to all preference pages in spyglass which use databinding.
 * 
 * 
 * @author Dariush Forouher
 * 
 */
public abstract class AbstractDatabindingPreferencePage extends PreferencePage {

    private static Logger log = SpyglassLoggerFactory.getLogger(AbstractDatabindingPreferencePage.class);

    /**
     * databinding context. may be null before createContents() is called.
     */
    protected DataBindingContext dbc = null;

    /**
     * This flag indicates if the page contains unsaved changes (or, correcty, has been touched in
     * some way).
     * 
     * This flag will automatically be set to true if a field connected to the <code>dbc</code> is
     * modified.
     */
    private boolean formIsDirty = false;

    /**
     * Image that is displayed in the top of the window.
     */
    private Image image;

    /**
     * This listener is called whenever someone modifies a field, which is observed by databinding
     */
    private final IChangeListener formGotDirtyListener = new IChangeListener() {

        @Override
        public void handleChange(final ChangeEvent event) {
            markFormDirty();
        }
    };

    protected final Realm getRealm() {
        return SWTObservables.getRealm(getControl().getDisplay());
    }

    /**
     * Add error handling to the databindingcontext.
     * 
     */
    protected AggregateValidationStatus addErrorBinding() {

        final AggregateValidationStatus aggregateStatus = new AggregateValidationStatus(getRealm(),
                dbc.getValidationStatusProviders(), AggregateValidationStatus.MAX_SEVERITY);

        new DatabindingErrorHandler(dbc, aggregateStatus, getShell());

        aggregateStatus.addValueChangeListener(new IValueChangeListener() {
            public void handleValueChange(final ValueChangeEvent event) {
                final Status valStatus = (Status) aggregateStatus.getValue();

                if (valStatus.getSeverity() == IStatus.ERROR) {
                    setErrorMessage(valStatus.getMessage());
                    setValid(false);
                } else {
                    setValid(true);
                    setErrorMessage(null);
                }

                // only mark the page invalid if we have an error
                setValid(valStatus.getSeverity() != IStatus.ERROR);

                // If the status contains an exception, show it.
                // (ordinary validation errors don't contain exceptions, so this is to track bugs.)
                if (valStatus.getException() != null) {
                    log.error(valStatus.getMessage(), valStatus.getException());
                }
            }
        });

        return aggregateStatus;
    }

    // --------------------------------------------------------------------------------
    /**
     * Does the form contain unsaved data? The return value of this method is only an indicator, IOW
     * it may return false-positives.
     * 
     * Subclasses overriding this method should include the return value of super() in their answer.
     * 
     * @return true if this page contains unsaved data.
     */
    public final boolean hasUnsavedChanges() {
        return formIsDirty;
    }

    /**
     * Transfers the form data into the model.
     */
    @Override
    public void performApply() {
        log.info("Pressed button Apply");
        if (!this.isValid()) {
            MessageDialog.openError(this.getShell(), "Can not store changes",
                    "Could not store your changes. There are still errors remaining in the form.");
        } else {
            this.storeToModel();
        }

    }

    /**
     * Store the form data into the model
     * 
     * Subclasses overriding this method must call this method!
     */
    protected void storeToModel() {
        log.debug("Storing form to model");
        dbc.updateModels();
        checkForErrors();
        dbc.updateTargets();
        checkForErrors();
        resetDirtyFlag();
    }

    /**
     * Checks for errors in the dbc and displays an error message for each if there are so
     * 
     * (these errors are likely bugs in the application, but we have to display them anyway.)
     */
    private final void checkForErrors() {
        final IStatus status = AggregateValidationStatus.getStatusMerged(dbc.getValidationStatusProviders());
        if (!status.isOK()) {
            for (final IStatus s : status.getChildren()) {
                if (s.getSeverity() == IStatus.ERROR) {
                    log.error(s.getMessage(), s.getException());
                } else if (s.getSeverity() == IStatus.WARNING) {
                    log.warn(s.getMessage(), s.getException());
                } else if (s.getSeverity() == IStatus.INFO) {
                    log.info(s.getMessage(), s.getException());
                }
            }
        }
    }

    protected void resetDirtyFlag() {
        formIsDirty = false;
    }

    /**
     * ReStore the form data from the model
     * 
     * Subclasses overriding this method must call this method!
     */
    protected void loadFromModel() {
        log.debug("Restoring form from model");

        dbc.updateTargets();
        checkForErrors();

        // update the models (with the already existent values)
        // this is necessary to (re)validate the values in case of erroneous values already existent
        // in the configuration
        dbc.updateModels();
        checkForErrors();
        resetDirtyFlag();
    }

    /**
     * Calling this method marks the form dirty (and thus enables the "Apply" button)
     */
    public void markFormDirty() {
        formIsDirty = true;
    }

    protected Composite createContentsInternal(final Composite parent) {
        final Composite composite = createComposite(parent);

        dbc = new DataBindingContext(getRealm());

        // Add a Listener to each binding, so we get informed if the user modifies a field.
        dbc.getBindings().addListChangeListener(new IListChangeListener() {

            @Override
            public void handleListChange(final ListChangeEvent event) {
                for (final ListDiffEntry e : event.diff.getDifferences()) {
                    final Binding b = (Binding) e.getElement();
                    if (e.isAddition()) {
                        b.getTarget().addChangeListener(formGotDirtyListener);
                    } else {
                        b.getTarget().removeChangeListener(formGotDirtyListener);
                    }
                }
            }

        });

        addErrorBinding();

        return composite;
    }

    protected Composite createComposite(final Composite parent) {
        final Composite c = new Composite(parent, SWT.NONE);
        c.setLayout(new GridLayout(1, true));
        final GridData gridData = new GridData(SWT.LEFT, SWT.TOP, true, true);
        gridData.horizontalAlignment = GridData.FILL;
        gridData.verticalAlignment = GridData.FILL;
        c.setLayoutData(gridData);
        return c;
    }

    public void setImage(final Image image) {
        this.image = image;
    }

    @Override
    public Image getImage() {
        return image;
    }

}