org.eclipse.xtext.ui.preferences.OptionsConfigurationBlock.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.xtext.ui.preferences.OptionsConfigurationBlock.java

Source

/*******************************************************************************
 * Copyright (c) 2011 itemis AG (http://www.itemis.eu) 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
 *******************************************************************************/
package org.eclipse.xtext.ui.preferences;

import java.io.IOException;
import java.util.List;
import java.util.Map;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPersistentPreferenceStore;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.osgi.util.TextProcessor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.forms.events.ExpansionAdapter;
import org.eclipse.ui.forms.events.ExpansionEvent;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer;
import org.eclipse.xtext.ui.editor.preferences.PreferenceStoreAccessImpl;

import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.MapDifference;
import com.google.common.collect.MapDifference.ValueDifference;
import com.google.common.collect.Maps;
import com.google.inject.Inject;

/**
 * Initially copied from jdt's OptionsConfigurationBlock
 * 
 * @author Michael Clay
 * @since 2.1
 */
public abstract class OptionsConfigurationBlock {
    @Inject
    AbstractUIPlugin uiPlugin;

    @Inject
    protected PreferenceStoreAccessImpl preferenceStoreAccessImpl;

    private static final String IS_PROJECT_SPECIFIC = "is_project_specific"; //$NON-NLS-1$
    private static final String SETTINGS_EXPANDED = "expanded"; //$NON-NLS-1$
    private static final String REBUILD_COUNT_KEY = "preferences_build_requested"; //$NON-NLS-1$

    protected static class ControlData {
        private String key;
        private String[] values;

        public ControlData(String key, String[] values) {
            this.key = key;
            this.values = values;
        }

        public String getKey() {
            return key;
        }

        public String getValue(boolean selection) {
            int index = selection ? 0 : 1;
            return values[index];
        }

        public String getValue(int index) {
            return values[index];
        }

        public int getSelection(String value) {
            if (value != null) {
                for (int i = 0; i < values.length; i++) {
                    if (value.equals(values[i])) {
                        return i;
                    }
                }
            }
            return values.length - 1; // assume the last option is the least severe
        }
    }

    public static final class BuildJob extends Job {
        private final IProject project;

        public BuildJob(String name, IProject project) {
            super(name);
            this.project = project;
        }

        public boolean isCoveredBy(BuildJob other) {
            if (other.project == null) {
                return true;
            }
            return project != null && project.equals(other.project);
        }

        @Override
        protected IStatus run(IProgressMonitor monitor) {
            synchronized (getClass()) {
                if (monitor.isCanceled()) {
                    return Status.CANCEL_STATUS;
                }
                Job[] buildJobs = Job.getJobManager().find(ResourcesPlugin.FAMILY_MANUAL_BUILD);
                for (int i = 0; i < buildJobs.length; i++) {
                    if (buildJobs[i] != this && buildJobs[i] instanceof BuildJob) {
                        BuildJob job = (BuildJob) buildJobs[i];
                        if (job.isCoveredBy(this)) {
                            buildJobs[i].cancel();
                        }
                    }
                }
            }
            try {
                if (project != null) {
                    monitor.beginTask(
                            String.format(Messages.BuilderConfigurationBlock_BuildJob_TitleBuildProject_TaskName,
                                    TextProcessor.process(project.getName(), ":.")), //$NON-NLS-2$  //$NON-NLS-1$
                            2);
                    project.build(IncrementalProjectBuilder.FULL_BUILD, new SubProgressMonitor(monitor, 1));
                    ResourcesPlugin.getWorkspace().build(IncrementalProjectBuilder.INCREMENTAL_BUILD,
                            new SubProgressMonitor(monitor, 1));
                } else {
                    monitor.beginTask(Messages.BuilderConfigurationBlock_BuildJob_TitleBuildAll_TaskName, 2);
                    ResourcesPlugin.getWorkspace().build(IncrementalProjectBuilder.FULL_BUILD,
                            new SubProgressMonitor(monitor, 2));
                }
            } catch (CoreException e) {
                return e.getStatus();
            } catch (OperationCanceledException e) {
                return Status.CANCEL_STATUS;
            } finally {
                monitor.done();
            }
            return Status.OK_STATUS;
        }

        @Override
        public boolean belongsTo(Object family) {
            return ResourcesPlugin.FAMILY_MANUAL_BUILD == family;
        }
    }

    protected final List<Button> checkBoxes = Lists.newArrayList();
    protected final List<Text> textBoxes = Lists.newArrayList();
    protected final List<Combo> comboBoxes = Lists.newArrayList();
    protected final Map<Control, Label> labels = Maps.newHashMap();
    protected final List<ExpandableComposite> expandedComposites = Lists.newArrayList();
    private SelectionListener selectionListener;
    private ModifyListener textModifyListener;
    protected IStatusChangeListener statusChangeListener;
    protected IProject project;
    protected String[] keys;
    private Shell shell;
    private Map<String, String> disabledProjectSettings;
    private Map<String, String> originalSettings;
    private int rebuildCount;
    private IPreferenceStore preferenceStore;
    private IWorkbenchPreferenceContainer workbenchPreferenceContainer;

    private List<String> keysToRegister = Lists.newArrayList();

    public IProject getProject() {
        return project;
    }

    public void setStatusChangeListener(IStatusChangeListener fContext) {
        this.statusChangeListener = fContext;
    }

    public OptionsConfigurationBlock(IProject project, IPreferenceStore preferenceStore,
            IWorkbenchPreferenceContainer container) {
        this.project = project;
        this.keys = new String[] {};
        this.setPreferenceStore(preferenceStore);
        this.workbenchPreferenceContainer = container;
    }

    public OptionsConfigurationBlock() {
        this.keys = new String[] {};
    }

    public void setWorkbenchPreferenceContainer(IWorkbenchPreferenceContainer workbenchPreferenceContainer) {
        this.workbenchPreferenceContainer = workbenchPreferenceContainer;
    }

    public void setProject(IProject project) {
        this.project = project;
    }

    public void setPreferenceStore(IPreferenceStore preferenceStore) {
        this.preferenceStore = preferenceStore;
        this.rebuildCount = getRebuildCount();
    }

    private void updateDisabledProjSettings(IProject project, IPreferenceStore preferenceStore, String[] allKeys) {
        if (project == null || hasProjectSpecificOptions(project)) {
            disabledProjectSettings = null;
        } else {
            disabledProjectSettings = Maps.newHashMap();
            for (String key : allKeys) {
                disabledProjectSettings.put(key, preferenceStore.getString(key));
            }
        }
    }

    public boolean hasProjectSpecificOptions(IProject project) {
        IPreferenceStore ps = preferenceStore;
        if (project != getProject()) {
            ps = preferenceStoreAccessImpl.getWritablePreferenceStore(project);
        }
        // backward compatibility
        boolean oldSettingsUsed = ps.getBoolean(IS_PROJECT_SPECIFIC);
        boolean newSettingsValue = ps.getBoolean(getIsProjectSpecificPropertyKey(getPropertyPrefix()));
        if (oldSettingsUsed) {
            if (!newSettingsValue) {
                ps.setValue(getIsProjectSpecificPropertyKey(getPropertyPrefix()), true);
                return true;
            }
        }
        return newSettingsValue;
    }

    protected void setShell(Shell shell) {
        this.shell = shell;
    }

    protected Shell getShell() {
        return shell;
    }

    public final Control createContents(Composite parent) {
        Control content = doCreateContents(parent);
        collectRegistredKeys();
        updateDisabledProjSettings(project, preferenceStore, keys);
        captureOriginalSettings(keys);
        return content;
    }

    protected abstract Control doCreateContents(Composite parent);

    protected Button addCheckBox(Composite parent, String label, String key, String[] values, int indent) {
        ControlData data = new ControlData(key, values);
        GridData gd = new GridData(SWT.FILL, SWT.CENTER, true, false);
        gd.horizontalSpan = 3;
        gd.horizontalIndent = indent;
        return addCheckboxWithData(parent, label, data, gd);
    }

    protected Button addCheckboxWithData(Composite parent, String label, ControlData data, GridData gd) {
        Button checkBox = new Button(parent, SWT.CHECK);
        checkBox.setFont(JFaceResources.getDialogFont());
        checkBox.setText(label);
        checkBox.setData(data);
        checkBox.setLayoutData(gd);
        checkBox.addSelectionListener(getSelectionListener());
        makeScrollableCompositeAware(checkBox);
        updateCheckBox(checkBox);
        checkBoxes.add(checkBox);
        return checkBox;
    }

    protected Text addTextField(Composite parent, String label, String key, int indent, int widthHint) {
        Label labelControl = new Label(parent, SWT.WRAP);
        labelControl.setText(label);
        labelControl.setFont(JFaceResources.getDialogFont());
        GridData labelLayoutData = new GridData();
        labelLayoutData.horizontalIndent = indent;
        labelControl.setLayoutData(labelLayoutData);
        Text textBox = new Text(parent, SWT.BORDER | SWT.SINGLE);
        textBox.setData(key);
        makeScrollableCompositeAware(textBox);
        labels.put(textBox, labelControl);
        updateText(textBox);
        textBox.addModifyListener(getTextModifyListener());

        GridData textLayoutData = new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1);
        if (widthHint != 0) {
            textLayoutData.widthHint = widthHint;
        }
        textBox.setLayoutData(textLayoutData);
        textBoxes.add(textBox);
        return textBox;
    }

    protected Combo addComboBox(Composite parent, String label, String key, int indent, String[] values,
            String[] valueLabels) {
        GridData gd = new GridData(GridData.FILL, GridData.CENTER, true, false, 2, 1);
        gd.horizontalIndent = indent;

        Label labelControl = new Label(parent, SWT.LEFT);
        labelControl.setFont(JFaceResources.getDialogFont());
        labelControl.setText(label);
        labelControl.setLayoutData(gd);

        Combo comboBox = newComboControl(parent, key, values, valueLabels);
        comboBox.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));

        labels.put(comboBox, labelControl);

        return comboBox;
    }

    protected Combo newComboControl(Composite composite, String key, String[] values, String[] valueLabels) {
        ControlData data = new ControlData(key, values);

        Combo comboBox = new Combo(composite, SWT.READ_ONLY);
        comboBox.setItems(valueLabels);
        comboBox.setData(data);
        comboBox.addSelectionListener(getSelectionListener());
        comboBox.setFont(JFaceResources.getDialogFont());
        comboBox.setVisibleItemCount(30);

        makeScrollableCompositeAware(comboBox);

        updateCombo(comboBox);

        comboBoxes.add(comboBox);
        return comboBox;
    }

    private ScrolledPageContent getParentScrolledComposite(Control control) {
        Control parent = control.getParent();
        while (!(parent instanceof ScrolledPageContent) && parent != null) {
            parent = parent.getParent();
        }
        if (parent instanceof ScrolledPageContent) {
            return (ScrolledPageContent) parent;
        }
        return null;
    }

    private void makeScrollableCompositeAware(Control control) {
        ScrolledPageContent parentScrolledComposite = getParentScrolledComposite(control);
        if (parentScrolledComposite != null) {
            parentScrolledComposite.adaptChild(control);
        }
    }

    protected ExpandableComposite createStyleSection(Composite parent, String label, int nColumns) {
        ExpandableComposite excomposite = new ExpandableComposite(parent, SWT.NONE,
                ExpandableComposite.TWISTIE | ExpandableComposite.CLIENT_INDENT);
        excomposite.setText(label);
        excomposite.setExpanded(false);
        excomposite.setFont(JFaceResources.getFontRegistry().getBold(JFaceResources.DIALOG_FONT));
        excomposite.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false, nColumns, 1));
        excomposite.addExpansionListener(new ExpansionAdapter() {
            @Override
            public void expansionStateChanged(ExpansionEvent e) {
                expandedStateChanged((ExpandableComposite) e.getSource());
            }
        });
        expandedComposites.add(excomposite);
        makeScrollableCompositeAware(excomposite);
        return excomposite;
    }

    protected final void expandedStateChanged(ExpandableComposite expandable) {
        ScrolledPageContent parentScrolledComposite = getParentScrolledComposite(expandable);
        if (parentScrolledComposite != null) {
            parentScrolledComposite.reflow(true);
        }
    }

    protected void restoreSectionExpansionStates(IDialogSettings settings) {
        for (int i = 0; i < expandedComposites.size(); i++) {
            ExpandableComposite excomposite = expandedComposites.get(i);
            if (settings == null) {
                excomposite.setExpanded(i == 0); // only expand the first node by default
            } else {
                excomposite.setExpanded(settings.getBoolean(SETTINGS_EXPANDED + String.valueOf(i)));
            }
        }
    }

    protected void storeSectionExpansionStates(IDialogSettings settings) {
        for (int i = 0; i < expandedComposites.size(); i++) {
            ExpandableComposite curr = expandedComposites.get(i);
            settings.put(SETTINGS_EXPANDED + String.valueOf(i), curr.isExpanded());
        }
    }

    protected SelectionListener getSelectionListener() {
        if (selectionListener == null) {
            selectionListener = new SelectionListener() {
                @Override
                public void widgetDefaultSelected(SelectionEvent e) {
                }

                @Override
                public void widgetSelected(SelectionEvent e) {
                    controlChanged(e.widget);
                }
            };
        }
        return selectionListener;
    }

    protected ModifyListener getTextModifyListener() {
        if (textModifyListener == null) {
            textModifyListener = new ModifyListener() {
                @Override
                public void modifyText(ModifyEvent e) {
                    textChanged((Text) e.widget);
                }
            };
        }
        return textModifyListener;
    }

    protected void controlChanged(Widget widget) {
        ControlData data = (ControlData) widget.getData();
        String newValue = null;
        if (widget instanceof Button) {
            newValue = data.getValue(((Button) widget).getSelection());
        } else if (widget instanceof Combo) {
            newValue = data.getValue(((Combo) widget).getSelectionIndex());
        } else {
            return;
        }
        String oldValue = setValue(data.getKey(), newValue);
        validateSettings(data.getKey(), oldValue, newValue);
    }

    protected void textChanged(Text textControl) {
        String key = (String) textControl.getData();
        String number = textControl.getText();
        String oldValue = setValue(key, number);
        validateSettings(key, oldValue, number);
    }

    protected String getValue(String key) {
        if (disabledProjectSettings != null) {
            return disabledProjectSettings.get(key);
        }
        return preferenceStore.getString(key);
    }

    protected String setValue(String key, String value) {
        if (disabledProjectSettings != null) {
            return disabledProjectSettings.put(key, value);
        }
        String oldValue = getValue(key);
        preferenceStore.putValue(key, value);
        return oldValue;
    }

    protected String setToDefault(String key) {
        String value = preferenceStore.getDefaultString(key);
        if (disabledProjectSettings != null) {
            return disabledProjectSettings.put(key, value);
        }
        String oldValue = getValue(key);
        preferenceStore.setValue(key, value);
        return oldValue;
    }

    public void useProjectSpecificSettings(boolean enable) {
        boolean hasProjectSpecificOption = disabledProjectSettings == null;
        if (enable != hasProjectSpecificOption && project != null) {
            if (enable) {
                for (int i = 0; i < keys.length; i++) {
                    String curr = keys[i];
                    String val = disabledProjectSettings.get(curr);
                    preferenceStore.putValue(curr, val);
                }
                disabledProjectSettings = null;
                updateControls();
                validateSettings(null, null, null);
                preferenceStore.setValue(getIsProjectSpecificPropertyKey(getPropertyPrefix()), true);
            } else {
                disabledProjectSettings = Maps.newHashMap();
                for (int i = 0; i < keys.length; i++) {
                    String curr = keys[i];
                    String oldSetting = preferenceStore.getString(curr);
                    disabledProjectSettings.put(curr, oldSetting);
                    preferenceStore.setToDefault(curr);
                }
                preferenceStore.setToDefault(getIsProjectSpecificPropertyKey(getPropertyPrefix()));
                //backward compatibility
                preferenceStore.setToDefault(IS_PROJECT_SPECIFIC);
            }
        }
    }

    public boolean performOk() {
        return processChanges(workbenchPreferenceContainer);
    }

    public boolean performApply() {
        return processChanges(workbenchPreferenceContainer);
    }

    private int getRebuildCount() {
        return preferenceStore.getDefaultInt(REBUILD_COUNT_KEY);
    }

    private void incrementRebuildCount() {
        preferenceStore.setDefault(REBUILD_COUNT_KEY, getRebuildCount() + 1);
    }

    protected boolean processChanges(IWorkbenchPreferenceContainer container) {
        boolean needsBuild = !getPreferenceChanges().isEmpty();
        boolean doBuild = false;
        if (needsBuild) {
            int count = getRebuildCount();
            if (count > rebuildCount) {
                needsBuild = false;
                rebuildCount = count;
            }
        }
        if (needsBuild) {
            String[] strings = getFullBuildDialogStrings(project == null);
            if (strings != null) {
                MessageDialog dialog = new MessageDialog(shell, strings[0], null, strings[1],
                        MessageDialog.QUESTION, new String[] { IDialogConstants.YES_LABEL,
                                IDialogConstants.NO_LABEL, IDialogConstants.CANCEL_LABEL },
                        2);
                int res = dialog.open();
                if (res == 0) {
                    doBuild = true;
                } else if (res != 1) {
                    return false;
                }
            }
        }
        savePreferences();
        if (container != null) {
            if (doBuild) {
                incrementRebuildCount();
                container.registerUpdateJob(getBuildJob(getProject()));
            }
        } else {
            if (doBuild) {
                getBuildJob(getProject()).schedule();
            }
        }
        captureOriginalSettings(keys);
        return true;
    }

    protected void savePreferences() {
        try {
            if (preferenceStore instanceof IPersistentPreferenceStore) {
                ((IPersistentPreferenceStore) preferenceStore).save();
            }
        } catch (IOException e) {
            logError("Unexpected internal error: ", e); //$NON-NLS-1$
        }
    }

    private void logError(String text, IOException e) {
        IStatus status = new Status(IStatus.ERROR, uiPlugin.getBundle().getSymbolicName(), text, e);
        uiPlugin.getLog().log(status);
    }

    public Map<String, ValueDifference<String>> getPreferenceChanges() {
        Map<String, String> currentSettings = Maps.newHashMapWithExpectedSize(keys.length);
        for (String key : keys) {
            currentSettings.put(key, preferenceStore.getString(key));
        }
        MapDifference<String, String> mapDifference = Maps.difference(currentSettings, originalSettings);
        Map<String, ValueDifference<String>> entriesDiffering = mapDifference.entriesDiffering();
        return entriesDiffering;
    }

    protected abstract Job getBuildJob(IProject project);

    protected abstract String[] getFullBuildDialogStrings(boolean workspaceSettings);

    public void performDefaults() {
        for (int i = 0; i < keys.length; i++) {
            String curr = keys[i];
            setToDefault(curr);
        }
        updateControls();
        validateSettings(null, null, null);
    }

    public void dispose() {
    }

    protected void updateControls() {
        for (int i = comboBoxes.size() - 1; i >= 0; i--) {
            updateCombo(comboBoxes.get(i));
        }
        for (int i = checkBoxes.size() - 1; i >= 0; i--) {
            updateCheckBox(checkBoxes.get(i));
        }
        for (int i = textBoxes.size() - 1; i >= 0; i--) {
            updateText(textBoxes.get(i));
        }
    }

    protected void collectRegistredKeys() {
        List<String> collectedKeys = Lists.newArrayList();
        for (Combo combo : comboBoxes) {
            ControlData data = (ControlData) combo.getData();
            collectedKeys.add(data.getKey());
        }
        for (Button button : checkBoxes) {
            ControlData data = (ControlData) button.getData();
            collectedKeys.add(data.getKey());
        }
        for (Text textField : textBoxes) {
            collectedKeys.add((String) textField.getData());
        }
        collectedKeys.addAll(keysToRegister);
        this.keys = Iterables.toArray(collectedKeys, String.class);
    }

    protected void updateCombo(Combo curr) {
        ControlData data = (ControlData) curr.getData();
        String currValue = getValue(data.getKey());
        int selection = data.getSelection(currValue);
        curr.select(selection);
    }

    protected void updateCheckBox(Button curr) {
        ControlData data = (ControlData) curr.getData();
        curr.setSelection(checkBoxValue(data));
    }

    private boolean checkBoxValue(ControlData data) {
        String currValue = getValue(data.getKey());
        boolean selection = data.getSelection(currValue) == 0;
        return selection;
    }

    protected void updateText(Text curr) {
        String key = (String) curr.getData();
        String currValue = getValue(key);
        if (currValue != null) {
            curr.setText(currValue);
        }
    }

    protected abstract void validateSettings(String changedKey, String oldValue, String newValue);

    private void captureOriginalSettings(String[] keys) {
        originalSettings = Maps.newHashMapWithExpectedSize(keys.length);
        for (String key : keys) {
            originalSettings.put(key, preferenceStore.getString(key));
        }
    }

    protected void registerKey(String key) {
        keysToRegister.add(key);
    }

    public abstract String getPropertyPrefix();

    public String getIsProjectSpecificPropertyKey(String propertyPrefix) {
        String key = IS_PROJECT_SPECIFIC;
        if (propertyPrefix != null) {
            key = isProjectSpecificPropertyKey(propertyPrefix);
        } else {
            logError("Project specific key is not qualified", null);
        }
        return key;
    }

    public static String isProjectSpecificPropertyKey(String propertyPrefix) {
        return propertyPrefix + "." + IS_PROJECT_SPECIFIC;
    }
}