/*=============================================================================#
# Copyright (c) 2005-2016 Stephan Wahlbrink (WalWare.de) 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
#
# Contributors:
# Stephan Wahlbrink - initial API and implementation
#=============================================================================*/
package de.walware.statet.base.internal.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 de.walware.ecommons.ltk.ui.sourceediting.ISourceEditor;
import de.walware.ecommons.ltk.ui.sourceediting.SourceEditorViewerConfigurator;
import de.walware.ecommons.ltk.ui.sourceediting.ViewerSourceEditorAdapter;
import de.walware.ecommons.ltk.ui.templates.EditTemplateDialog;
import de.walware.ecommons.preferences.ui.ConfigurationBlock;
import de.walware.ecommons.preferences.ui.SettingsUpdater;
import de.walware.ecommons.templates.TemplateVariableProcessor;
import de.walware.ecommons.text.ui.TextViewerEditorColorUpdater;
import de.walware.ecommons.text.ui.TextViewerJFaceUpdater;
import de.walware.ecommons.ui.ISettingsChangedHandler;
import de.walware.ecommons.ui.dialogs.groups.CategorizedOptionButtonsGroup;
import de.walware.ecommons.ui.dialogs.groups.CategorizedOptionsGroup.CategorizedItem;
import de.walware.ecommons.ui.util.UIAccess;
import de.walware.statet.ext.templates.ICodeGenerationTemplatesCategory;
import de.walware.statet.base.internal.ui.StatetUIPlugin;
/**
* 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());
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) {
fButtonGroup.enableButton(IDX_EDIT, (item != null));
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 < fCategoryChilds[catIndex].length; j++) {
datas.add(fCategoryChilds[catIndex][j].fData );
}
}
}
doExport(datas.toArray(new TemplatePersistenceData[datas.size()]));
break;
case IDX_EXPORTALL:
doExport(fTemplatesStore.getAllTemplateData());
break;
case IDX_IMPORT:
doImport();
break;
}
}
}
protected IProject fProject;
private String[] fCategoryIds;
private ICodeGenerationTemplatesCategory[] 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 {
fProject = project;
fGroup = new Group();
loadRegisteredTemplates();
fGroup.generateListModel();
fPatternTemplateProcessor = new TemplateVariableProcessor();
fEditTemplateProcessor = new TemplateVariableProcessor();
}
private void loadRegisteredTemplates() {
final IExtensionRegistry registry = Platform.getExtensionRegistry();
final IConfigurationElement[] elements = registry.getConfigurationElementsFor(StatetUIPlugin.PLUGIN_ID, EXTENSION_POINT);
fCategoryIds = new String[elements.length];
fCategoryProvider = new ICodeGenerationTemplatesCategory[elements.length];
fGroup.fCategorys = new String[elements.length];
fGroup.fCategoryChilds = new TemplateItem[elements.length][];
final TemplateStore[] templates = new TemplateStore[elements.length];
for (int i = 0; i < elements.length; i++) {
fCategoryIds[i] = elements[i].getAttribute(ATT_ID);
Assert.isLegal(fCategoryIds[i] != null);
fGroup.fCategorys[i] = elements[i].getAttribute(ATT_NAME);
if (fGroup.fCategorys[i] == null) {
fGroup.fCategorys[i] = fCategoryIds[i];
}
try {
fCategoryProvider[i] = (ICodeGenerationTemplatesCategory) elements[i].createExecutableExtension(ATT_CLASS);
} catch (final CoreException e) {
throw new IllegalArgumentException("Error occured when loading templateprovider of category with id "+fCategoryIds[i], e); //$NON-NLS-1$
}
templates[i] = fCategoryProvider[i].getTemplateStore();
}
fTemplatesStore = new CodeGenerationTemplatesStore(fProject, templates);
try {
fTemplatesStore.load();
} catch (final IOException e) {
StatetUIPlugin.logUnexpectedError(e);
}
for (int i = 0; i < fCategoryIds.length; i++) {
final TemplatePersistenceData[] datas = fTemplatesStore.getTemplateData(i);
fGroup.fCategoryChilds[i] = new TemplateItem[datas.length];
for (int j = 0; j < fGroup.fCategoryChilds[i].length; j++) {
fGroup.fCategoryChilds[i][j] = new TemplateItem(datas[j]);
}
}
}
private void reloadTemplateData() {
for (int i = 0; i < fCategoryIds.length; i++) {
for (int j = 0; j < fGroup.fCategoryChilds[i].length; j++) {
fGroup.fCategoryChilds[i][j].fData = fTemplatesStore.getTemplateData(
i, fGroup.fCategoryChilds[i][j].fData.getId());
}
}
}
private void updateTemplate(final TemplatePersistenceData data) {
final TemplatePersistenceData[] datas = 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() {
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));
fGroup.createGroup(pageComposite, 1);
fGroup.initFields();
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);
fPatternEditor = new ViewerSourceEditorAdapter(viewer, null);
new SettingsUpdater(new ISettingsChangedHandler() {
@Override
public void handleSettingsChanged(final Set<String> groupIds, final Map<String, Object> options) {
if (fPatternConfigurator != null) {
fPatternConfigurator.handleSettingsChanged(groupIds, options);
}
}
}, viewer.getControl());
return viewer;
}
protected void updateSourceViewerInput(final TemplateItem item) {
if (fPatternViewer == null || !(UIAccess.isOkToUse(fPatternViewer.getControl())) ) {
return;
}
if (item != null) {
final TemplatePersistenceData data = item.fData;
final ICodeGenerationTemplatesCategory category = fCategoryProvider[item.getCategoryIndex()];
final Template template = data.getTemplate();
final TemplateContextType type = category.getContextTypeRegistry().getContextType(template.getContextTypeId());
fPatternTemplateProcessor.setContextType(type);
if (item.getCategoryIndex() != fPatternViewerConfiguredCategory) {
fPatternViewerConfiguredCategory = item.getCategoryIndex();
if (fPatternViewerUpdater != null) {
fPatternViewerUpdater.dispose();
fPatternViewerUpdater = null;
}
if (fPatternConfigurator != null) {
fPatternConfigurator.unconfigureTarget();
fPatternConfigurator = null;
}
fPatternConfigurator = category.getEditTemplateDialogConfiguator(fPatternTemplateProcessor, fProject);
fPatternConfigurator.setTarget(fPatternEditor);
fPatternViewerUpdater = new TextViewerJFaceUpdater(fPatternViewer,
fPatternConfigurator.getSourceViewerConfiguration().getPreferences() );
final IDocument document = new Document(template.getPattern());
fPatternConfigurator.getDocumentSetupParticipant().setup(document);
fPatternViewer.setDocument(document);
}
else {
fPatternViewer.getDocument().set(template.getPattern());
}
} else {
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,
fCategoryProvider[item.getCategoryIndex()].getEditTemplateDialogConfiguator(fEditTemplateProcessor, fProject),
fEditTemplateProcessor,
fCategoryProvider[item.getCategoryIndex()].getContextTypeRegistry());
if (dialog.open() == Window.OK) {
// changed
item.fData.setTemplate(dialog.getTemplate());
fGroup.getStructuredViewer().refresh(item);
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) {
}
}
}
fGroup.getStructuredViewer().refresh();
updateSourceViewerInput(fGroup.getSingleItem(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() {
fTemplatesStore.restoreDefaults();
reloadTemplateData();
// refresh
fGroup.getStructuredViewer().refresh();
updateSourceViewerInput(fGroup.getSingleItem(fGroup.getSelectedItems()));
}
@Override
public boolean performOk() {
if (fProject != null) {
final TemplatePersistenceData[] templateData = fTemplatesStore.getAllTemplateData();
fTemplatesStore.setProjectSpecific(isUseProjectSpecificSettings());
for (int i = 0; i < templateData.length; i++) {
fTemplatesStore.setProjectSpecific(templateData[i].getId(), isUseProjectSpecificSettings());
}
}
try {
fTemplatesStore.save();
} catch (final IOException e) {
StatetUIPlugin.logUnexpectedError(e);
openWriteErrorDialog(e);
}
return true;
}
@Override
public void performCancel() {
try {
fTemplatesStore.revertChanges();
} catch (final IOException e) {
openReadErrorDialog(e);
}
}
/* Error Dialogs **************************************************************/
private void openReadErrorDialog(final Exception e) {
StatusManager.getManager().handle(new Status(IStatus.ERROR, StatetUIPlugin.PLUGIN_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.PLUGIN_ID, 0,
Messages.CodeTemplates_error_Write_message, e));
MessageDialog.openError(getShell(),
Messages.CodeTemplates_error_title, Messages.CodeTemplates_error_Write_message);
}
}