blob: 4a2bcff5e6aa408752c4c08a15300753b1770e05 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2005, 2019 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.internal.ide.ui.preferences;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.layout.PixelConverter;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.text.templates.Template;
import org.eclipse.jface.text.templates.TemplateContextType;
import org.eclipse.jface.text.templates.persistence.TemplatePersistenceData;
import org.eclipse.jface.text.templates.persistence.TemplateReaderWriter;
import org.eclipse.jface.text.templates.persistence.TemplateStore;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.statushandlers.StatusManager;
import org.eclipse.statet.ecommons.preferences.ui.ConfigurationBlock;
import org.eclipse.statet.ecommons.preferences.ui.SettingsUpdater;
import org.eclipse.statet.ecommons.templates.TemplateVariableProcessor;
import org.eclipse.statet.ecommons.text.ui.TextViewerEditorColorUpdater;
import org.eclipse.statet.ecommons.text.ui.TextViewerJFaceUpdater;
import org.eclipse.statet.ecommons.ui.ISettingsChangedHandler;
import org.eclipse.statet.ecommons.ui.dialogs.groups.CategorizedOptionButtonsGroup;
import org.eclipse.statet.ecommons.ui.dialogs.groups.CategorizedOptionsGroup.CategorizedItem;
import org.eclipse.statet.ecommons.ui.util.UIAccess;
import org.eclipse.statet.base.ext.templates.ICodeGenerationTemplateCategory;
import org.eclipse.statet.internal.ide.ui.StatetUIPlugin;
import org.eclipse.statet.ltk.ui.sourceediting.ISourceEditor;
import org.eclipse.statet.ltk.ui.sourceediting.SourceEditorViewerConfigurator;
import org.eclipse.statet.ltk.ui.sourceediting.ViewerSourceEditorAdapter;
import org.eclipse.statet.ltk.ui.templates.config.EditTemplateDialog;
import org.eclipse.statet.ltk.ui.templates.config.TemplateConfigUI;
/**
* The page to configure the codegenerator templates.
*/
public class CodeGenerationTemplatesConfigurationBlock extends ConfigurationBlock {
public static final String EXTENSION_POINT = "codeGenerationTemplatesCategory"; //$NON-NLS-1$
public static final String ATT_ID = "id"; //$NON-NLS-1$
public static final String ATT_NAME = "name"; //$NON-NLS-1$
public static final String ATT_CLASS = "providerClass"; //$NON-NLS-1$
private final static int IDX_EDIT = 0;
private final static int IDX_IMPORT = 2;
private final static int IDX_EXPORT = 3;
private final static int IDX_EXPORTALL = 4;
public class TemplateItem extends CategorizedItem {
protected TemplatePersistenceData fData;
public TemplateItem(final TemplatePersistenceData data) {
super(data.getTemplate().getDescription());
this.fData = data;
}
}
private class Group extends CategorizedOptionButtonsGroup<TemplateItem> {
public Group() {
super(new String[] {
Messages.CodeTemplates_EditButton_label,
null,
Messages.CodeTemplates_ImportButton_label,
Messages.CodeTemplates_ExportButton_label,
Messages.CodeTemplates_ExportAllButton_label,
});
}
@Override
protected void handleDoubleClick(final TemplateItem item, final IStructuredSelection rawSelection) {
doEdit(item);
}
@Override
public void handleSelection(final TemplateItem item, final IStructuredSelection rawSelection) {
this.fButtonGroup.enableButton(IDX_EDIT, (item != null));
this.fButtonGroup.enableButton(IDX_EXPORT, rawSelection.size() > 0);
updateSourceViewerInput(item);
}
@Override
public void handleButtonPressed(final int buttonIdx, final TemplateItem item, final IStructuredSelection rawSelection) {
switch (buttonIdx) {
case IDX_EDIT:
if (item != null) {
doEdit(item);
}
break;
case IDX_EXPORT:
final List<TemplatePersistenceData> datas= new ArrayList<>();
for (final Iterator<?> iter = rawSelection.iterator(); iter.hasNext();) {
final Object curr = iter.next();
if (curr instanceof TemplateItem) {
datas.add( ((TemplateItem) curr).fData );
} else {
final int catIndex = getIndexOfCategory(curr);
for (int j = 0; j < this.fCategoryChilds[catIndex].length; j++) {
datas.add(this.fCategoryChilds[catIndex][j].fData );
}
}
}
doExport(datas.toArray(new TemplatePersistenceData[datas.size()]));
break;
case IDX_EXPORTALL:
doExport(CodeGenerationTemplatesConfigurationBlock.this.fTemplatesStore.getAllTemplateData());
break;
case IDX_IMPORT:
doImport();
break;
}
}
}
protected IProject fProject;
private String[] fCategoryIds;
private ICodeGenerationTemplateCategory[] fCategoryProvider;
protected CodeGenerationTemplatesStore fTemplatesStore;
protected Group fGroup;
private SourceViewer fPatternViewer;
private int fPatternViewerConfiguredCategory = -1;
private TextViewerJFaceUpdater fPatternViewerUpdater = null;
private SourceEditorViewerConfigurator fPatternConfigurator;
private ISourceEditor fPatternEditor;
private final TemplateVariableProcessor fPatternTemplateProcessor;
private final TemplateVariableProcessor fEditTemplateProcessor;
public CodeGenerationTemplatesConfigurationBlock(final IProject project) throws CoreException {
this.fProject = project;
this.fGroup = new Group();
loadRegisteredTemplates();
this.fGroup.generateListModel();
this.fPatternTemplateProcessor = new TemplateVariableProcessor();
this.fEditTemplateProcessor = new TemplateVariableProcessor();
}
private void loadRegisteredTemplates() {
final IExtensionRegistry registry = Platform.getExtensionRegistry();
final IConfigurationElement[] elements = registry.getConfigurationElementsFor(StatetUIPlugin.BUNDLE_ID, EXTENSION_POINT);
this.fCategoryIds = new String[elements.length];
this.fCategoryProvider = new ICodeGenerationTemplateCategory[elements.length];
this.fGroup.fCategorys = new String[elements.length];
this.fGroup.fCategoryChilds = new TemplateItem[elements.length][];
final TemplateStore[] templates = new TemplateStore[elements.length];
for (int i = 0; i < elements.length; i++) {
this.fCategoryIds[i] = elements[i].getAttribute(ATT_ID);
Assert.isLegal(this.fCategoryIds[i] != null);
this.fGroup.fCategorys[i] = elements[i].getAttribute(ATT_NAME);
if (this.fGroup.fCategorys[i] == null) {
this.fGroup.fCategorys[i] = this.fCategoryIds[i];
}
try {
this.fCategoryProvider[i] = (ICodeGenerationTemplateCategory) elements[i].createExecutableExtension(ATT_CLASS);
} catch (final CoreException e) {
throw new IllegalArgumentException("Error occured when loading templateprovider of category with id "+this.fCategoryIds[i], e); //$NON-NLS-1$
}
templates[i] = this.fCategoryProvider[i].getTemplateStore();
}
this.fTemplatesStore = new CodeGenerationTemplatesStore(this.fProject, templates);
try {
this.fTemplatesStore.load();
} catch (final IOException e) {
StatetUIPlugin.logUnexpectedError(e);
}
for (int i = 0; i < this.fCategoryIds.length; i++) {
final TemplatePersistenceData[] datas = this.fTemplatesStore.getTemplateData(i);
this.fGroup.fCategoryChilds[i] = new TemplateItem[datas.length];
for (int j = 0; j < this.fGroup.fCategoryChilds[i].length; j++) {
this.fGroup.fCategoryChilds[i][j] = new TemplateItem(datas[j]);
}
}
}
private void reloadTemplateData() {
for (int i = 0; i < this.fCategoryIds.length; i++) {
for (int j = 0; j < this.fGroup.fCategoryChilds[i].length; j++) {
this.fGroup.fCategoryChilds[i][j].fData = this.fTemplatesStore.getTemplateData(
i, this.fGroup.fCategoryChilds[i][j].fData.getId());
}
}
}
private void updateTemplate(final TemplatePersistenceData data) {
final TemplatePersistenceData[] datas = this.fTemplatesStore.getAllTemplateData();
for (int i = 0; i < datas.length; i++) {
final String id = datas[i].getId();
if (id != null && id.equals(data.getId())) {
datas[i].setTemplate(data.getTemplate());
break;
}
}
}
@Override
public void setUseProjectSpecificSettings(final boolean enable) {
super.setUseProjectSpecificSettings(enable);
if (enable) {
UIAccess.getDisplay(getShell()).asyncExec(new Runnable() {
@Override
public void run() {
CodeGenerationTemplatesConfigurationBlock.this.fGroup.reselect();
}
});
}
}
/* GUI ************************************************************************/
@Override
protected void createBlockArea(final Composite pageComposite) {
final Label label = new Label(pageComposite, SWT.LEFT);
label.setText(Messages.CodeTemplates_label);
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
this.fGroup.createGroup(pageComposite, 1);
this.fGroup.initFields();
this.fPatternViewer = createViewer(pageComposite);
}
private SourceViewer createViewer(final Composite parent) {
final Label label = new Label(parent, SWT.LEFT);
label.setText(Messages.CodeTemplates_Preview_label);
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
final SourceViewer viewer = new SourceViewer(parent, null, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
viewer.setEditable(false);
viewer.getTextWidget().setFont(JFaceResources.getFont(JFaceResources.TEXT_FONT));
new TextViewerEditorColorUpdater(viewer, EditorsUI.getPreferenceStore());
final IDocument document = new Document();
viewer.setDocument(document);
final GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
data.heightHint = new PixelConverter(viewer.getControl()).convertHeightInCharsToPixels(5);
viewer.getControl().setLayoutData(data);
this.fPatternEditor = new ViewerSourceEditorAdapter(viewer, null);
new SettingsUpdater(new ISettingsChangedHandler() {
@Override
public void handleSettingsChanged(final Set<String> groupIds, final Map<String, Object> options) {
if (CodeGenerationTemplatesConfigurationBlock.this.fPatternConfigurator != null) {
CodeGenerationTemplatesConfigurationBlock.this.fPatternConfigurator.handleSettingsChanged(groupIds, options);
}
}
}, viewer.getControl());
return viewer;
}
protected void updateSourceViewerInput(final TemplateItem item) {
if (this.fPatternViewer == null || !(UIAccess.isOkToUse(this.fPatternViewer.getControl())) ) {
return;
}
if (item != null) {
final TemplatePersistenceData data = item.fData;
final ICodeGenerationTemplateCategory category = this.fCategoryProvider[item.getCategoryIndex()];
final Template template = data.getTemplate();
final TemplateContextType type = category.getContextTypeRegistry().getContextType(template.getContextTypeId());
this.fPatternTemplateProcessor.setContextType(type);
if (item.getCategoryIndex() != this.fPatternViewerConfiguredCategory) {
this.fPatternViewerConfiguredCategory = item.getCategoryIndex();
if (this.fPatternViewerUpdater != null) {
this.fPatternViewerUpdater.dispose();
this.fPatternViewerUpdater = null;
}
if (this.fPatternConfigurator != null) {
this.fPatternConfigurator.unconfigureTarget();
this.fPatternConfigurator = null;
}
this.fPatternConfigurator = category.getEditTemplateDialogConfiguator(this.fPatternTemplateProcessor, this.fProject);
this.fPatternConfigurator.setTarget(this.fPatternEditor);
this.fPatternViewerUpdater = new TextViewerJFaceUpdater(this.fPatternViewer,
this.fPatternConfigurator.getSourceViewerConfiguration().getPreferences() );
final IDocument document = new Document(template.getPattern());
this.fPatternConfigurator.getDocumentSetupParticipant().setup(document);
this.fPatternViewer.setDocument(document);
}
else {
this.fPatternViewer.getDocument().set(template.getPattern());
}
} else {
this.fPatternViewer.getDocument().set(""); //$NON-NLS-1$
}
}
/* Execute Actions ************************************************************/
public void doEdit(final TemplateItem item) {
final EditTemplateDialog dialog = new EditTemplateDialog(
getShell(), item.fData.getTemplate(), true,
EditTemplateDialog.FIX_TEMPLATE,
this.fCategoryProvider[item.getCategoryIndex()].getEditTemplateDialogConfiguator(this.fEditTemplateProcessor, this.fProject),
this.fEditTemplateProcessor,
this.fCategoryProvider[item.getCategoryIndex()].getContextTypeRegistry(),
TemplateConfigUI.PREF_QUALIFIER );
if (dialog.open() == Window.OK) {
// changed
item.fData.setTemplate(dialog.getTemplate());
this.fGroup.getStructuredViewer().refresh(item);
this.fGroup.getStructuredViewer().setSelection(new StructuredSelection(item));
}
}
public void doImport() {
final FileDialog dialog = new FileDialog(getShell(), SWT.OPEN);
dialog.setText(Messages.CodeTemplates_Import_title);
dialog.setFilterExtensions(new String[] { Messages.CodeTemplates_Import_extension });
final String path = dialog.open();
if (path == null) {
return;
}
try {
final TemplateReaderWriter reader = new TemplateReaderWriter();
final File file = new File(path);
if (file.exists()) {
final InputStream input = new BufferedInputStream(new FileInputStream(file));
try {
final TemplatePersistenceData[] datas = reader.read(input, null);
for (int i= 0; i < datas.length; i++) {
updateTemplate(datas[i]);
}
} finally {
try {
input.close();
} catch (final IOException x) {
}
}
}
this.fGroup.getStructuredViewer().refresh();
updateSourceViewerInput(this.fGroup.getSingleItem(this.fGroup.getSelectedItems()));
} catch (final FileNotFoundException e) {
openReadErrorDialog(e);
} catch (final IOException e) {
openReadErrorDialog(e);
}
}
public void doExport(final TemplatePersistenceData[] templates) {
final FileDialog dialog = new FileDialog(getShell(), SWT.SAVE);
dialog.setText(NLS.bind(Messages.CodeTemplates_Export_title, String.valueOf(templates.length)));
dialog.setFilterExtensions(new String[] { Messages.CodeTemplates_Export_extension });
dialog.setFileName(Messages.CodeTemplates_Export_filename);
final String path = dialog.open();
if (path == null) {
return;
}
final File file = new File(path);
if (file.isHidden()) {
MessageDialog.openError(getShell(),
Messages.CodeTemplates_Export_Error_title,
NLS.bind(Messages.CodeTemplates_Export_Error_Hidden_message, file.getAbsolutePath()) );
return;
}
if (file.exists() && !file.canWrite()) {
MessageDialog.openError(getShell(),
Messages.CodeTemplates_Export_Error_title,
NLS.bind(Messages.CodeTemplates_Export_Error_CanNotWrite_message, file.getAbsolutePath()) );
return;
}
if (!file.exists() || confirmOverwrite(file)) {
OutputStream output= null;
try {
output= new BufferedOutputStream(new FileOutputStream(file));
final TemplateReaderWriter writer= new TemplateReaderWriter();
writer.save(templates, output);
output.close();
} catch (final IOException e) {
if (output != null) {
try {
output.close();
} catch (final IOException e2) {
// ignore
}
}
openWriteErrorDialog(e);
}
}
}
private boolean confirmOverwrite(final File file) {
return MessageDialog.openQuestion(getShell(),
Messages.CodeTemplates_Export_Exists_title,
NLS.bind(Messages.CodeTemplates_Export_Exists_message, file.getAbsolutePath()) );
}
/* IConfigurationBlock ********************************************************/
@Override
public void performDefaults() {
this.fTemplatesStore.restoreDefaults();
reloadTemplateData();
// refresh
this.fGroup.getStructuredViewer().refresh();
updateSourceViewerInput(this.fGroup.getSingleItem(this.fGroup.getSelectedItems()));
}
@Override
public boolean performOk(final int flags) {
if (this.fProject != null) {
final TemplatePersistenceData[] templateData = this.fTemplatesStore.getAllTemplateData();
this.fTemplatesStore.setProjectSpecific(isUseProjectSpecificSettings());
for (int i = 0; i < templateData.length; i++) {
this.fTemplatesStore.setProjectSpecific(templateData[i].getId(), isUseProjectSpecificSettings());
}
}
try {
this.fTemplatesStore.save();
} catch (final IOException e) {
StatetUIPlugin.logUnexpectedError(e);
openWriteErrorDialog(e);
}
return true;
}
@Override
public void performCancel() {
try {
this.fTemplatesStore.revertChanges();
} catch (final IOException e) {
openReadErrorDialog(e);
}
}
/* Error Dialogs **************************************************************/
private void openReadErrorDialog(final Exception e) {
StatusManager.getManager().handle(new Status(IStatus.ERROR, StatetUIPlugin.BUNDLE_ID, 0,
Messages.CodeTemplates_error_Read_message, e));
MessageDialog.openError(getShell(),
Messages.CodeTemplates_error_title, Messages.CodeTemplates_error_Read_message);
}
private void openWriteErrorDialog(final Exception e) {
StatusManager.getManager().handle(new Status(IStatus.ERROR, StatetUIPlugin.BUNDLE_ID, 0,
Messages.CodeTemplates_error_Write_message, e));
MessageDialog.openError(getShell(),
Messages.CodeTemplates_error_title, Messages.CodeTemplates_error_Write_message);
}
}