/*
* 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.dsl.ui;
import java.io.File;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.codehaus.groovy.eclipse.GroovyLogManager;
import org.codehaus.groovy.eclipse.TraceCategory;
import org.codehaus.groovy.eclipse.dsl.DSLDStore;
import org.codehaus.groovy.eclipse.dsl.DSLDStoreManager;
import org.codehaus.groovy.eclipse.dsl.DSLPreferencesInitializer;
import org.codehaus.groovy.eclipse.dsl.DisabledScriptsCache;
import org.codehaus.groovy.eclipse.dsl.GroovyDSLCoreActivator;
import org.codehaus.groovy.eclipse.editor.GroovyEditor;
import org.codehaus.jdt.groovy.model.GroovyNature;
import org.eclipse.core.internal.filesystem.local.LocalFile;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IStorage;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.internal.core.JavaModelManager;
import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.DialogField;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.ITreeListAdapter;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.LayoutUtil;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.TreeListDialogField;
import org.eclipse.jdt.ui.ISharedImages;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.layout.PixelConverter;
import org.eclipse.jface.preference.PreferencePage;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPreferencePage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.dialogs.ContainerCheckedTreeViewer;
import org.eclipse.ui.ide.FileStoreEditorInput;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.internal.Workbench;
import org.eclipse.ui.model.WorkbenchLabelProvider;
import org.eclipse.ui.progress.UIJob;
public class DSLPreferencesPage extends PreferencePage implements IWorkbenchPreferencePage {
private static final String[] LABELS = {"Edit...", "Recompile Scripts", "Refresh List", "Check All", "Uncheck All"};
private static final int IDX_EDIT = 0;
private static final int IDX_RECOMPILE = 1;
private static final int IDX_REFRESH = 2;
private static final int IDX_CHECK_ALL = 3;
private static final int IDX_UNCHECK_ALL= 4;
private class CheckStateListener implements ICheckStateListener {
public void checkStateChanged(CheckStateChangedEvent event) {
Object element = event.getElement();
if (element instanceof ProjectContextKey) {
ProjectContextKey key = (ProjectContextKey) element;
key.isChecked = event.getChecked();
} else if (element instanceof String) {
ProjectContextKey[] children = elementsMap.get(element);
for (ProjectContextKey child : children) {
child.isChecked = event.getChecked();
}
}
}
}
private class CheckedTreeListDialogField extends TreeListDialogField<String> {
private ContainerCheckedTreeViewer checkboxViewer;
private CheckedTreeListDialogField(ITreeListAdapter<String> adapter, String[] buttonLabels, ILabelProvider lprovider) {
super(adapter, buttonLabels, lprovider);
}
protected TreeViewer createTreeViewer(Composite parent) {
Tree tree = new Tree(parent, getTreeStyle() | SWT.CHECK);
tree.setFont(parent.getFont());
checkboxViewer = new ContainerCheckedTreeViewer(tree);
checkboxViewer.addCheckStateListener(new CheckStateListener());
return checkboxViewer;
}
void setChecked(Object child, boolean newState) {
checkboxViewer.setChecked(child, newState);
}
}
private class DSLLabelProvider extends LabelProvider {
WorkbenchLabelProvider provider = new WorkbenchLabelProvider();
public void dispose() {
provider.dispose();
super.dispose();
}
@Override
public String getText(Object element) {
if (element instanceof ProjectContextKey) {
ProjectContextKey pck = (ProjectContextKey) element;
return pck.dslFile.getName();
}
return super.getText(element);
}
public Image getImage(Object element) {
IProject proj = toProject(element);
if (proj != null) {
return provider.getImage(proj);
}
IFile file = null;
if (element instanceof ProjectContextKey && ((ProjectContextKey) element).dslFile instanceof IFile) {
file = (IFile) ((ProjectContextKey) element).dslFile;
}
if (file != null) {
return provider.getImage(file);
}
return JavaUI.getSharedImages().getImage(ISharedImages.IMG_OBJS_CFILE);
}
}
private class DSLListAdapter implements ITreeListAdapter<String> {
public void customButtonPressed(TreeListDialogField<String> field, int index) {
if (index == IDX_EDIT) {
// edit
edit();
} else if (index == IDX_RECOMPILE) {
// recompile
recompile();
} else if (index == IDX_REFRESH) {
// refresh
refresh();
} else if (index == IDX_CHECK_ALL) {
// check all
checkAll(true);
} else if (index == IDX_UNCHECK_ALL) {
// uncheck all
checkAll(false);
}
}
public void selectionChanged(TreeListDialogField<String> field) {
if (canEdit()) {
field.enableButton(IDX_EDIT, true);
} else {
field.enableButton(IDX_EDIT, false);
}
}
public void doubleClicked(TreeListDialogField<String> field) {
edit();
}
public void keyPressed(TreeListDialogField<String> field, KeyEvent event) { }
public Object[] getChildren(TreeListDialogField<String> field, Object element) {
if (element instanceof String) {
return elementsMap.get(element);
} else {
return null;
}
}
public Object getParent(TreeListDialogField<String> field, Object element) {
if (element instanceof ProjectContextKey) {
return ((ProjectContextKey) element).projectName;
}
return null;
}
public boolean hasChildren(TreeListDialogField<String> field, Object element) {
Object[] children = getChildren(field, element);
return children != null && children.length > 0;
}
}
private static class ProjectContextKey {
final String projectName;
final IStorage dslFile;
boolean isChecked; // set later
public ProjectContextKey(String projectName, IStorage dslFile) {
this.projectName = projectName;
this.dslFile = dslFile;
}
}
//--------------------------------------------------------------------------
DisabledScriptsCache cache;
Map<String, ProjectContextKey[]> elementsMap;
private CheckedTreeListDialogField tree;
private IWorkbenchPage page;
private Button autoAdd;
private Button disableDSLDs;
public void init(IWorkbench workbench) {
cache = new DisabledScriptsCache();
elementsMap = new HashMap<String, ProjectContextKey[]>();
try {
page = workbench.getActiveWorkbenchWindow().getActivePage();
} catch (NullPointerException e) {
// there is handling below for either state of page reference
}
}
@Override
protected Control createContents(Composite parent) {
Composite composite= new Composite(parent, SWT.NONE);
composite.setFont(parent.getFont());
tree = new CheckedTreeListDialogField(new DSLListAdapter(), LABELS, new DSLLabelProvider());
tree.setTreeExpansionLevel(2);
LayoutUtil.doDefaultLayout(composite, new DialogField[] { tree }, true, SWT.DEFAULT, SWT.DEFAULT);
LayoutUtil.setHorizontalGrabbing(tree.getTreeControl(null));
refresh();
PixelConverter converter= new PixelConverter(parent);
int buttonBarWidth= converter.convertWidthInCharsToPixels(24);
tree.setButtonsMinWidth(buttonBarWidth);
autoAdd = new Button(composite, SWT.CHECK);
autoAdd.setText("Automatically add DSL Support to all Groovy projects");
autoAdd.setSelection(GroovyDSLCoreActivator.getDefault().getPreferenceStore().getBoolean(DSLPreferencesInitializer.AUTO_ADD_DSL_SUPPORT));
GridData data = new GridData(SWT.LEFT, SWT.TOP, true, false);
data.horizontalSpan = 2;
autoAdd.setLayoutData(data);
disableDSLDs = new Button(composite, SWT.CHECK);
disableDSLDs.setText("Disable DSLD support in your workspace. (Requires restart)");
boolean isDisabled = GroovyDSLCoreActivator.getDefault().getPreferenceStore().getBoolean(DSLPreferencesInitializer.DSLD_DISABLED);
disableDSLDs.setSelection(isDisabled);
disableDSLDs.setLayoutData(data);
if (disableDSLDs.getSelection()) {
Label l = new Label(composite, SWT.NONE);
l.setText("NOTE: DSLD support is currently disabled.");
}
return composite;
}
protected IProject toProject(Object element) {
if (element instanceof String) {
String name = (String) element;
IProject proj = ResourcesPlugin.getWorkspace().getRoot().getProject(name);
if (GroovyNature.hasGroovyNature(proj)) {
return proj;
}
}
return null;
}
protected boolean canEdit() {
List<?> selected = tree.getSelectedElements();
return selected.size() == 1 && selected.get(0) instanceof ProjectContextKey;
}
protected void edit() {
if (canEdit()) {
List<?> selected = tree.getSelectedElements();
ProjectContextKey pck = (ProjectContextKey) selected.get(0);
IStorage storage = pck.dslFile;
IEditorInput input = getEditorInput(storage);
if (input != null) {
try {
if (page != null) {
IDE.openEditor(page, input, GroovyEditor.EDITOR_ID, true);
}
} catch (PartInitException e) {
if (page != null) {
ErrorDialog.openError(page.getWorkbenchWindow().getShell(),
"Error opening editor", "See error log: " + e.getLocalizedMessage(), e.getStatus());
}
GroovyDSLCoreActivator.logException(e);
}
} else {
if (page != null) {
ErrorDialog.openError(page.getWorkbenchWindow().getShell(),
"Could not open editor", "File " + pck.dslFile + " is not accessible.", new Status(IStatus.ERROR, GroovyDSLCoreActivator.PLUGIN_ID, "Could not open editor"));
}
}
}
}
private IEditorInput getEditorInput(IStorage storage) {
if (storage instanceof IFile && ((IFile) storage).getProject().equals(JavaModelManager.getExternalManager().getExternalFoldersProject())) {
return new FileStoreEditorInput(new LocalFile(new File(((IFile) storage).getLocationURI())));
} else {
return EditorUtility.getEditorInput(storage);
}
}
protected void refresh() {
DSLDStoreManager manager = GroovyDSLCoreActivator.getDefault().getContextStoreManager();
List<String> allStores = manager.getAllStores();
elementsMap.clear();
for (String element : allStores) {
IProject project = toProject(element);
if (project != null) {
DSLDStore store = manager.getDSLDStore(project);
if (store != null) {
IStorage[] keys = store.getAllContextKeys();
ProjectContextKey[] pck = new ProjectContextKey[keys.length];
for (int i = 0; i < pck.length; i++) {
pck[i] = new ProjectContextKey(element, keys[i]);
pck[i].isChecked = ! cache.isDisabled(DSLDStore.toUniqueString(pck[i].dslFile));
}
elementsMap.put(element, pck);
}
}
}
Collections.sort(allStores);
tree.setElements(allStores);
tree.refresh();
for (ProjectContextKey[] keys : elementsMap.values()) {
for (ProjectContextKey key : keys) {
tree.setChecked(key, key.isChecked);
}
}
}
void checkAll(boolean newState) {
for (ProjectContextKey[] keys : elementsMap.values()) {
for (ProjectContextKey key : keys) {
key.isChecked = newState;
tree.setChecked(key, key.isChecked);
}
}
}
private static final String EVENT = "Recompiling all DSLDs in the workspace.";
protected void recompile() {
// re-compile all scripts and then wait for the results to refresh the UIs
new UIJob("Refresh DSLD launcher") {
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
GroovyLogManager.manager.log(TraceCategory.DSL, EVENT);
GroovyLogManager.manager.logStart(EVENT);
GroovyDSLCoreActivator.getDefault().getContextStoreManager().initializeAll(true);
if (!DSLPreferencesPage.this.getControl().isDisposed()) {
refresh();
}
GroovyLogManager.manager.logEnd(EVENT, TraceCategory.DSL);
return Status.OK_STATUS;
}
}.schedule();
}
@Override
protected void performDefaults() {
super.performDefaults();
checkAll(true);
DSLPreferencesInitializer.reset();
autoAdd.setSelection(true);
}
@Override
public boolean performOk() {
Set<String> unchecked = new HashSet<String>();
for (ProjectContextKey[] keys : elementsMap.values()) {
for (ProjectContextKey key : keys) {
if (! key.isChecked) {
unchecked.add(DSLDStore.toUniqueString(key.dslFile));
}
}
}
cache.setDisabled(unchecked);
GroovyDSLCoreActivator.getDefault().getPreferenceStore().setValue(DSLPreferencesInitializer.AUTO_ADD_DSL_SUPPORT, autoAdd.getSelection());
boolean origDisabled = GroovyDSLCoreActivator.getDefault().getPreferenceStore().getBoolean(DSLPreferencesInitializer.DSLD_DISABLED);
if (origDisabled != disableDSLDs.getSelection()) {
GroovyDSLCoreActivator.getDefault().getPreferenceStore().setValue(DSLPreferencesInitializer.DSLD_DISABLED, disableDSLDs.getSelection());
boolean res = MessageDialog.openQuestion(getShell(), "Restart now?", "You have " +
(disableDSLDs.getSelection() ? "disabled" : "enabled") + " DSLDs in your worksoace." +
" This will not take effect until a restart has been performed.\n\nDo you want to restart now?");
if (res) {
Workbench.getInstance().restart();
}
}
return super.performOk();
}
}