/*
* Copyright 2009-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.groovy.eclipse.preferences;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.codehaus.groovy.eclipse.core.GroovyCore;
import org.codehaus.jdt.groovy.model.GroovyNature;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.jdt.groovy.core.Activator;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.JavaPluginImages;
import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
import org.eclipse.jdt.internal.ui.viewsupport.ImageDescriptorRegistry;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.CheckedListDialogField;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.DialogField;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.IDialogFieldListener;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.IListAdapter;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.ListDialogField;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.BooleanFieldEditor;
import org.eclipse.jface.preference.FieldEditor;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.jface.window.Window;
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 org.eclipse.swt.widgets.Label;
/**
* Dialog for creating and editing script folders in the workspace.
*/
public class ScriptFolderSelectorPreferences {
private static final int IDX_ADD = 0;
private static final int IDX_EDIT = 1;
private static final int IDX_REMOVE = 2;
private static final int IDX_CHECKALL = 3;
private static final int IDX_UNCHECKALL = 4;
private static final String[] BUTTON_LABELS = {"Add", "Edit", "Remove", "Check all", "Uncheck all"};
private static final ImageDescriptor DESCRIPTOR = JavaPluginImages.DESC_OBJS_INCLUSION_FILTER_ATTRIB;
private static class ScriptLabelProvider extends LabelProvider {
private Image fElementImage;
public ScriptLabelProvider(ImageDescriptor descriptor) {
ImageDescriptorRegistry registry = JavaPlugin.getImageDescriptorRegistry();
fElementImage = registry.get(descriptor);
}
@Override
public Image getImage(Object element) {
return fElementImage;
}
@Override @SuppressWarnings("static-access")
public String getText(Object element) {
return BasicElementLabels.getFilePattern((String) element);
}
}
private class ScriptPatternAdapter implements IListAdapter<String>, IDialogFieldListener {
public void customButtonPressed(ListDialogField<String> field, int index) {
doCustomButtonPressed(field, index);
hasChanges = true;
}
public void selectionChanged(ListDialogField<String> field) {
doSelectionChanged(field);
}
public void doubleClicked(ListDialogField<String> field) {
doDoubleClicked(field);
hasChanges = true;
}
public void dialogFieldChanged(DialogField field) {
hasChanges = true;
}
}
private static class BuildJob extends Job {
private IProject[] projects;
public BuildJob(IProject...projects) {
super(getName(projects));
this.projects = projects;
}
private static String getName(IProject...projects) {
if (projects.length == 1) {
return "Building proiject " + projects[0].getName();
} else {
StringBuilder sb = new StringBuilder();
sb.append("Building proijects ");
for (IProject project : projects) {
sb.append(project.getName() + " ");
}
return sb.toString();
}
}
protected IStatus run(IProgressMonitor monitor) {
try {
IProgressMonitor sub = SubMonitor.convert(monitor, projects.length);
for (IProject project : projects) {
project.build(IncrementalProjectBuilder.FULL_BUILD, sub);
sub.worked(1);
}
return Status.OK_STATUS;
} catch (CoreException e) {
GroovyCore.logException("Error building groovy project", e);
return e.getStatus();
} finally {
monitor.done();
}
}
}
//--------------------------------------------------------------------------
private final Composite parent;
private final IEclipsePreferences preferences;
private final IPreferenceStore store;
private final IProject project;
private CheckedListDialogField<String> patternList;
private BooleanFieldEditor disableButton;
private boolean hasChanges;
public ScriptFolderSelectorPreferences(Composite parent, IEclipsePreferences preferences, IPreferenceStore store, IProject project) {
this.parent = parent;
this.preferences = preferences;
this.store = store;
this.project = project;
}
public ListDialogField<String> createListContents() {
Label label = new Label(parent, SWT.WRAP);
label.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false));
label.setText("Groovy Script Folders:");
label.setFont(JFaceResources.getFontRegistry().getBold(JFaceResources.DIALOG_FONT));
Composite inner = new Composite(parent, SWT.BORDER);
inner.setFont(parent.getFont());
GridLayout layout = new GridLayout();
layout.marginHeight = 3;
layout.marginWidth = 3;
layout.numColumns = 1;
inner.setLayout(layout);
inner.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
disableButton = new BooleanFieldEditor(Activator.GROOVY_SCRIPT_FILTERS_ENABLED, "Enable script folder support", BooleanFieldEditor.DEFAULT, inner);
disableButton.setPreferenceStore(store);
disableButton.load();
// inner composite contains the dialog itself
final Composite innerInner = new Composite(inner, SWT.NONE);
innerInner.setFont(parent.getFont());
layout = new GridLayout();
layout.marginHeight = 3;
layout.marginWidth = 3;
layout.numColumns = 3;
innerInner.setLayout(layout);
innerInner.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
innerInner.setToolTipText("CHECKED boxes are COPIED to output folder.\nUNCHECKED boxes are NOT copied.");
// enable/disable pattern list
disableButton.setPropertyChangeListener(new IPropertyChangeListener() {
public void propertyChange(PropertyChangeEvent event) {
if (event.getProperty() == FieldEditor.VALUE) {
Object o = event.getNewValue();
if (o instanceof Boolean) {
enablePatternList((Boolean) o);
}
}
hasChanges = true;
}
});
ScriptPatternAdapter adapter = new ScriptPatternAdapter();
patternList = new CheckedListDialogField<String>(adapter, BUTTON_LABELS, new ScriptLabelProvider(DESCRIPTOR));
patternList.setDialogFieldListener(adapter);
patternList.setLabelText("Groovy files that match these patterns are treated as scripts, i.e. compiled at run-time. " +
"Any script that matches a checked pattern and is in a source folder will be copied as-is to the output folder.\n\n" +
"CHECKED boxes will be COPIED to the output folder. UNCHECKED boxes are NOT copied to the output folder.");
patternList.enableButton(IDX_ADD, true);
patternList.enableButton(IDX_EDIT, false);
patternList.setRemoveButtonIndex(IDX_REMOVE);
patternList.setCheckAllButtonIndex(IDX_CHECKALL);
patternList.setUncheckAllButtonIndex(IDX_UNCHECKALL);
patternList.doFillIntoGrid(innerInner, 3);
Label l = patternList.getLabelControl(innerInner);
GridData gd = new GridData(SWT.FILL, SWT.TOP, true, false);
gd.widthHint = 200;
l.setLayoutData(gd);
populatePatternList(Activator.getDefault().getScriptFilters(preferences));
patternList.setViewerComparator(new ViewerComparator());
enablePatternList(disableButton.getBooleanValue());
return patternList;
}
protected void doCustomButtonPressed(ListDialogField<String> field, int index) {
if (index == IDX_ADD) {
addEntry(field);
} else if (index == IDX_EDIT) {
editEntry(field);
}
}
protected void doSelectionChanged(ListDialogField<String> field) {
List<String> selected = field.getSelectedElements();
field.enableButton(IDX_EDIT, canEdit(selected));
}
protected void doDoubleClicked(ListDialogField<String> field) {
editEntry(field);
}
private boolean canEdit(List<String> selected) {
return selected.size() == 1;
}
private void addEntry(ListDialogField<String> field) {
InputDialog dialog = createInputDialog("");
if (dialog.open() == Window.OK) {
field.addElement(dialog.getValue());
}
}
private InputDialog createInputDialog(String initial) {
InputDialog dialog = new InputDialog(
parent.getShell(),
"Add script folder",
"Enter a pattern for denoting script files in Groovy projects. Allowed wildcards are '*', '?' and '**'. Examples: 'java/util/A*.java', 'java/util/', '**/Test*'. All patterns are relative to the current project.",
initial, null);
return dialog;
}
private void editEntry(ListDialogField<String> field) {
List<String> selElements = field.getSelectedElements();
if (selElements.size() != 1) {
return;
}
String entry = selElements.get(0);
InputDialog dialog = createInputDialog(entry);
if (dialog.open() == Window.OK) {
field.replaceElement(entry, dialog.getValue());
}
}
public void applyPreferences() {
if (!hasChanges) {
return;
}
hasChanges = false;
// must do the store before setting the preference
// to ensure that the store is flushed
disableButton.store();
List<String> elts = patternList.getElements();
List<String> result = new ArrayList<String>(elts.size() * 2);
for (String elt : elts) {
result.add(elt);
result.add(patternList.isChecked(elt) ? "y" : "n");
}
Activator.getDefault().setScriptFilters(preferences, result);
boolean yesNo = MessageDialog.openQuestion(parent.getShell(), "Do full build?", "Script folder preferences have changed.\n" +
"Must do a full build before they come completely into effect. Do you want to do a full build now?");
if (yesNo) {
if (project != null) {
new BuildJob(project).schedule();
} else {
new BuildJob(GroovyNature.getAllAccessibleGroovyProjects().toArray(new IProject[0])).schedule();
}
}
}
public void enableControls() {
enablePatternList(disableButton.getBooleanValue());
}
public void restoreDefaults() {
populatePatternList(Arrays.asList(Activator.DEFAULT_GROOVY_SCRIPT_FILTER.split(",")));
disableButton.loadDefault();
hasChanges = true;
}
private void enablePatternList(boolean enabled) {
patternList.getListControl(null).setEnabled(enabled);
patternList.getLabelControl(null).setEnabled(enabled);
}
private void populatePatternList(List<String> elements) {
List<String> filteredElements = new ArrayList<String>(elements.size() / 2);
List<String> checkedElements = new ArrayList<String>(elements.size() / 2);
for (Iterator<String> eltIter = elements.iterator(); eltIter.hasNext();) {
String elt = eltIter.next();
filteredElements.add(elt);
if (eltIter.hasNext()) {
String doCopy = eltIter.next();
if (doCopy.equals("y")) {
checkedElements.add(elt);
}
}
}
patternList.setElements(filteredElements);
patternList.setCheckedElements(checkedElements);
}
}