| /*=============================================================================# |
| # 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); |
| } |
| |
| } |