improved PHP parser
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / ui / preferences / CodeTemplateBlock.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.ui.preferences;
12
13 import java.io.BufferedInputStream;
14 import java.io.BufferedOutputStream;
15 import java.io.File;
16 import java.io.FileInputStream;
17 import java.io.FileNotFoundException;
18 import java.io.FileOutputStream;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.io.OutputStream;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.List;
25
26 import net.sourceforge.phpdt.internal.corext.template.php.CodeTemplateContextType;
27 import net.sourceforge.phpdt.internal.ui.text.IPHPPartitions;
28 import net.sourceforge.phpdt.internal.ui.text.template.preferences.TemplateVariableProcessor;
29 import net.sourceforge.phpdt.internal.ui.util.PixelConverter;
30 import net.sourceforge.phpdt.internal.ui.wizards.dialogfields.DialogField;
31 import net.sourceforge.phpdt.internal.ui.wizards.dialogfields.IDialogFieldListener;
32 import net.sourceforge.phpdt.internal.ui.wizards.dialogfields.ITreeListAdapter;
33 import net.sourceforge.phpdt.internal.ui.wizards.dialogfields.LayoutUtil;
34 import net.sourceforge.phpdt.internal.ui.wizards.dialogfields.SelectionButtonDialogField;
35 import net.sourceforge.phpdt.internal.ui.wizards.dialogfields.TreeListDialogField;
36 import net.sourceforge.phpdt.ui.PreferenceConstants;
37 import net.sourceforge.phpdt.ui.text.JavaTextTools;
38 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
39 import net.sourceforge.phpeclipse.phpeditor.JavaSourceViewer;
40
41 import org.eclipse.jface.dialogs.MessageDialog;
42 import org.eclipse.jface.preference.IPreferenceStore;
43 import org.eclipse.jface.resource.JFaceResources;
44 import org.eclipse.jface.text.Document;
45 import org.eclipse.jface.text.IDocument;
46 import org.eclipse.jface.text.source.SourceViewer;
47 import org.eclipse.jface.text.templates.Template;
48 import org.eclipse.jface.text.templates.TemplateContextType;
49 import org.eclipse.jface.text.templates.persistence.TemplatePersistenceData;
50 import org.eclipse.jface.text.templates.persistence.TemplateReaderWriter;
51 import org.eclipse.jface.text.templates.persistence.TemplateStore;
52 import org.eclipse.jface.viewers.LabelProvider;
53 import org.eclipse.jface.viewers.StructuredSelection;
54 import org.eclipse.jface.window.Window;
55 import org.eclipse.swt.SWT;
56 import org.eclipse.swt.events.KeyEvent;
57 import org.eclipse.swt.graphics.Font;
58 import org.eclipse.swt.graphics.Image;
59 import org.eclipse.swt.layout.GridData;
60 import org.eclipse.swt.layout.GridLayout;
61 import org.eclipse.swt.widgets.Composite;
62 import org.eclipse.swt.widgets.Control;
63 import org.eclipse.swt.widgets.FileDialog;
64 import org.eclipse.swt.widgets.Label;
65 import org.eclipse.swt.widgets.Shell;
66
67 /**
68  */
69 public class CodeTemplateBlock {
70
71   private class CodeTemplateAdapter implements ITreeListAdapter, IDialogFieldListener {
72
73     private final Object[] NO_CHILDREN = new Object[0];
74
75     public void customButtonPressed(TreeListDialogField field, int index) {
76       doButtonPressed(index, field.getSelectedElements());
77     }
78
79     public void selectionChanged(TreeListDialogField field) {
80       List selected = field.getSelectedElements();
81       field.enableButton(IDX_EDIT, canEdit(selected));
82       field.enableButton(IDX_EXPORT, !selected.isEmpty());
83
84       updateSourceViewerInput(selected);
85     }
86
87     public void doubleClicked(TreeListDialogField field) {
88       List selected = field.getSelectedElements();
89       if (canEdit(selected)) {
90         doButtonPressed(IDX_EDIT, selected);
91       }
92     }
93
94     public Object[] getChildren(TreeListDialogField field, Object element) {
95       if (element == COMMENT_NODE || element == CODE_NODE) {
96         return getTemplateOfCategory(element == COMMENT_NODE);
97       }
98       return NO_CHILDREN;
99     }
100
101     public Object getParent(TreeListDialogField field, Object element) {
102       if (element instanceof TemplatePersistenceData) {
103         TemplatePersistenceData data = (TemplatePersistenceData) element;
104         if (data.getTemplate().getName().endsWith(CodeTemplateContextType.COMMENT_SUFFIX)) {
105           return COMMENT_NODE;
106         }
107         return CODE_NODE;
108       }
109       return null;
110     }
111
112     public boolean hasChildren(TreeListDialogField field, Object element) {
113       return (element == COMMENT_NODE || element == CODE_NODE);
114     }
115
116     public void dialogFieldChanged(DialogField field) {
117     }
118
119     public void keyPressed(TreeListDialogField field, KeyEvent event) {
120     }
121
122   }
123
124   private static class CodeTemplateLabelProvider extends LabelProvider {
125
126     /*
127      * (non-Javadoc)
128      * 
129      * @see org.eclipse.jface.viewers.ILabelProvider#getImage(java.lang.Object)
130      */
131     public Image getImage(Object element) {
132       return null;
133
134     }
135
136     /*
137      * (non-Javadoc)
138      * 
139      * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object)
140      */
141     public String getText(Object element) {
142       if (element == COMMENT_NODE || element == CODE_NODE) {
143         return (String) element;
144       }
145       TemplatePersistenceData data = (TemplatePersistenceData) element;
146       Template template = data.getTemplate();
147       String name = template.getName();
148       if (CodeTemplateContextType.CATCHBLOCK.equals(name)) {
149         return PreferencesMessages.getString("CodeTemplateBlock.catchblock.label"); //$NON-NLS-1$
150       } else if (CodeTemplateContextType.METHODSTUB.equals(name)) {
151         return PreferencesMessages.getString("CodeTemplateBlock.methodstub.label"); //$NON-NLS-1$
152       } else if (CodeTemplateContextType.CONSTRUCTORSTUB.equals(name)) {
153         return PreferencesMessages.getString("CodeTemplateBlock.constructorstub.label"); //$NON-NLS-1$
154       } else if (CodeTemplateContextType.GETTERSTUB.equals(name)) {
155         return PreferencesMessages.getString("CodeTemplateBlock.getterstub.label"); //$NON-NLS-1$
156       } else if (CodeTemplateContextType.SETTERSTUB.equals(name)) {
157         return PreferencesMessages.getString("CodeTemplateBlock.setterstub.label"); //$NON-NLS-1$
158       } else if (CodeTemplateContextType.NEWTYPE.equals(name)) {
159         return PreferencesMessages.getString("CodeTemplateBlock.newtype.label"); //$NON-NLS-1$
160       } else if (CodeTemplateContextType.TYPECOMMENT.equals(name)) {
161         return PreferencesMessages.getString("CodeTemplateBlock.typecomment.label"); //$NON-NLS-1$
162       } else if (CodeTemplateContextType.FIELDCOMMENT.equals(name)) {
163         return PreferencesMessages.getString("CodeTemplateBlock.fieldcomment.label"); //$NON-NLS-1$
164       } else if (CodeTemplateContextType.METHODCOMMENT.equals(name)) {
165         return PreferencesMessages.getString("CodeTemplateBlock.methodcomment.label"); //$NON-NLS-1$
166       } else if (CodeTemplateContextType.OVERRIDECOMMENT.equals(name)) {
167         return PreferencesMessages.getString("CodeTemplateBlock.overridecomment.label"); //$NON-NLS-1$
168       } else if (CodeTemplateContextType.CONSTRUCTORCOMMENT.equals(name)) {
169         return PreferencesMessages.getString("CodeTemplateBlock.constructorcomment.label"); //$NON-NLS-1$
170       } else if (CodeTemplateContextType.GETTERCOMMENT.equals(name)) {
171         return PreferencesMessages.getString("CodeTemplateBlock.gettercomment.label"); //$NON-NLS-1$
172       } else if (CodeTemplateContextType.SETTERCOMMENT.equals(name)) {
173         return PreferencesMessages.getString("CodeTemplateBlock.settercomment.label"); //$NON-NLS-1$
174       }
175       return template.getDescription();
176     }
177
178   }
179
180   private final static int IDX_EDIT = 0;
181
182   private final static int IDX_IMPORT = 2;
183
184   private final static int IDX_EXPORT = 3;
185
186   private final static int IDX_EXPORTALL = 4;
187
188   protected final static Object COMMENT_NODE = PreferencesMessages.getString("CodeTemplateBlock.templates.comment.node"); //$NON-NLS-1$
189
190   protected final static Object CODE_NODE = PreferencesMessages.getString("CodeTemplateBlock.templates.code.node"); //$NON-NLS-1$
191
192   private static final String PREF_JAVADOC_STUBS = PreferenceConstants.CODEGEN_ADD_COMMENTS;
193
194   private TreeListDialogField fCodeTemplateTree;
195
196   private SelectionButtonDialogField fCreateJavaDocComments;
197
198   protected TemplateStore fTemplates;
199
200   private PixelConverter fPixelConverter;
201
202   private SourceViewer fPatternViewer;
203
204   private Control fSWTWidget;
205
206   private TemplateVariableProcessor fTemplateProcessor;
207
208   public CodeTemplateBlock() {
209
210     fTemplates = PHPeclipsePlugin.getDefault().getCodeTemplateStore();
211     fTemplateProcessor = new TemplateVariableProcessor();
212
213     CodeTemplateAdapter adapter = new CodeTemplateAdapter();
214
215     String[] buttonLabels = new String[] {
216     /* IDX_EDIT */PreferencesMessages.getString("CodeTemplateBlock.templates.edit.button"), //$NON-NLS-1$
217         /* */null,
218         /* IDX_IMPORT */PreferencesMessages.getString("CodeTemplateBlock.templates.import.button"), //$NON-NLS-1$
219         /* IDX_EXPORT */PreferencesMessages.getString("CodeTemplateBlock.templates.export.button"), //$NON-NLS-1$
220         /* IDX_EXPORTALL */PreferencesMessages.getString("CodeTemplateBlock.templates.exportall.button") //$NON-NLS-1$
221
222     };
223     fCodeTemplateTree = new TreeListDialogField(adapter, buttonLabels, new CodeTemplateLabelProvider());
224     fCodeTemplateTree.setDialogFieldListener(adapter);
225     fCodeTemplateTree.setLabelText(PreferencesMessages.getString("CodeTemplateBlock.templates.label")); //$NON-NLS-1$
226
227     fCodeTemplateTree.enableButton(IDX_EXPORT, false);
228     fCodeTemplateTree.enableButton(IDX_EDIT, false);
229
230     fCodeTemplateTree.addElement(COMMENT_NODE);
231     fCodeTemplateTree.addElement(CODE_NODE);
232
233     fCreateJavaDocComments = new SelectionButtonDialogField(SWT.CHECK | SWT.WRAP);
234     fCreateJavaDocComments.setLabelText(PreferencesMessages.getString("CodeTemplateBlock.createcomment.label")); //$NON-NLS-1$
235     fCreateJavaDocComments.setSelection(PreferenceConstants.getPreferenceStore().getBoolean(PREF_JAVADOC_STUBS));
236
237     fCodeTemplateTree.selectFirstElement();
238   }
239
240   protected Control createContents(Composite parent) {
241     fPixelConverter = new PixelConverter(parent);
242     fSWTWidget = parent;
243
244     Composite composite = new Composite(parent, SWT.NONE);
245     GridLayout layout = new GridLayout();
246     layout.marginHeight = 0;
247     layout.marginWidth = 0;
248     layout.numColumns = 2;
249     composite.setLayout(layout);
250
251     fCodeTemplateTree.doFillIntoGrid(composite, 3);
252     LayoutUtil.setHorizontalSpan(fCodeTemplateTree.getLabelControl(null), 2);
253     LayoutUtil.setHorizontalGrabbing(fCodeTemplateTree.getTreeControl(null));
254
255     fPatternViewer = createViewer(composite, 2);
256
257     fCreateJavaDocComments.doFillIntoGrid(composite, 2);
258
259     DialogField label = new DialogField();
260     label.setLabelText(PreferencesMessages.getString("CodeTemplateBlock.createcomment.description")); //$NON-NLS-1$
261     label.doFillIntoGrid(composite, 2);
262
263     return composite;
264
265   }
266
267   private Shell getShell() {
268     if (fSWTWidget != null) {
269       return fSWTWidget.getShell();
270     }
271     return PHPeclipsePlugin.getActiveWorkbenchShell();
272   }
273
274   private SourceViewer createViewer(Composite parent, int nColumns) {
275     Label label = new Label(parent, SWT.NONE);
276     label.setText(PreferencesMessages.getString("CodeTemplateBlock.preview")); //$NON-NLS-1$
277     GridData data = new GridData();
278     data.horizontalSpan = nColumns;
279     label.setLayoutData(data);
280
281     IDocument document = new Document();
282     JavaTextTools tools = PHPeclipsePlugin.getDefault().getJavaTextTools();
283     tools.setupJavaDocumentPartitioner(document, IPHPPartitions.PHP_PARTITIONING);
284     IPreferenceStore store = PHPeclipsePlugin.getDefault().getCombinedPreferenceStore();
285     SourceViewer viewer = new JavaSourceViewer(parent, null, null, false, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL, store);
286     TemplateEditorSourceViewerConfiguration configuration = new TemplateEditorSourceViewerConfiguration(tools.getColorManager(),
287         store, null, fTemplateProcessor);
288     viewer.configure(configuration);
289     viewer.setEditable(false);
290     viewer.setDocument(document);
291
292     Font font = JFaceResources.getFont(PreferenceConstants.EDITOR_TEXT_FONT);
293     viewer.getTextWidget().setFont(font);
294     new JavaSourcePreviewerUpdater(viewer, configuration, store);
295
296     Control control = viewer.getControl();
297     data = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.FILL_VERTICAL);
298     data.horizontalSpan = nColumns;
299     data.heightHint = fPixelConverter.convertHeightInCharsToPixels(5);
300     control.setLayoutData(data);
301
302     return viewer;
303   }
304
305   protected TemplatePersistenceData[] getTemplateOfCategory(boolean isComment) {
306     ArrayList res = new ArrayList();
307     TemplatePersistenceData[] templates = fTemplates.getTemplateData(false);
308     for (int i = 0; i < templates.length; i++) {
309       TemplatePersistenceData curr = templates[i];
310       if (isComment == curr.getTemplate().getName().endsWith(CodeTemplateContextType.COMMENT_SUFFIX)) {
311         res.add(curr);
312       }
313     }
314     return (TemplatePersistenceData[]) res.toArray(new TemplatePersistenceData[res.size()]);
315   }
316
317   protected static boolean canEdit(List selected) {
318     return selected.size() == 1 && (selected.get(0) instanceof TemplatePersistenceData);
319   }
320
321   protected void updateSourceViewerInput(List selection) {
322     if (fPatternViewer == null || fPatternViewer.getTextWidget().isDisposed()) {
323       return;
324     }
325     if (selection.size() == 1 && selection.get(0) instanceof TemplatePersistenceData) {
326       TemplatePersistenceData data = (TemplatePersistenceData) selection.get(0);
327       Template template = data.getTemplate();
328       TemplateContextType type = PHPeclipsePlugin.getDefault().getCodeTemplateContextRegistry().getContextType(
329           template.getContextTypeId());
330       fTemplateProcessor.setContextType(type);
331       fPatternViewer.getDocument().set(template.getPattern());
332     } else {
333       fPatternViewer.getDocument().set(""); //$NON-NLS-1$
334     }
335   }
336
337   protected void doButtonPressed(int buttonIndex, List selected) {
338     if (buttonIndex == IDX_EDIT) {
339       edit((TemplatePersistenceData) selected.get(0));
340     } else if (buttonIndex == IDX_EXPORT) {
341       export(selected);
342     } else if (buttonIndex == IDX_EXPORTALL) {
343       exportAll();
344     } else if (buttonIndex == IDX_IMPORT) {
345       import_();
346     }
347   }
348
349   private void edit(TemplatePersistenceData data) {
350     Template newTemplate = new Template(data.getTemplate());
351     EditTemplateDialog dialog = new EditTemplateDialog(getShell(), newTemplate, true, false, PHPeclipsePlugin.getDefault()
352         .getCodeTemplateContextRegistry());
353     if (dialog.open() == Window.OK) {
354       // changed
355       data.setTemplate(newTemplate);
356       fCodeTemplateTree.refresh(data);
357       fCodeTemplateTree.selectElements(new StructuredSelection(data));
358     }
359   }
360
361   private void import_() {
362     FileDialog dialog = new FileDialog(getShell());
363     dialog.setText(PreferencesMessages.getString("CodeTemplateBlock.import.title")); //$NON-NLS-1$
364     dialog.setFilterExtensions(new String[] { PreferencesMessages.getString("CodeTemplateBlock.import.extension") }); //$NON-NLS-1$
365     String path = dialog.open();
366
367     if (path == null)
368       return;
369
370     try {
371       TemplateReaderWriter reader = new TemplateReaderWriter();
372       File file = new File(path);
373       if (file.exists()) {
374         InputStream input = new BufferedInputStream(new FileInputStream(file));
375         TemplatePersistenceData[] datas = reader.read(input, null);
376         for (int i = 0; i < datas.length; i++) {
377           updateTemplate(datas[i]);
378         }
379       }
380
381       fCodeTemplateTree.refresh();
382       updateSourceViewerInput(fCodeTemplateTree.getSelectedElements());
383
384     } catch (FileNotFoundException e) {
385       openReadErrorDialog(e);
386     } catch (IOException e) {
387       openReadErrorDialog(e);
388     }
389
390   }
391
392   private void updateTemplate(TemplatePersistenceData data) {
393     TemplatePersistenceData[] datas = fTemplates.getTemplateData(true);
394     for (int i = 0; i < datas.length; i++) {
395       String id = datas[i].getId();
396       if (id != null && id.equals(data.getId())) {
397         datas[i].setTemplate(data.getTemplate());
398         break;
399       }
400     }
401   }
402
403   private void exportAll() {
404     export(fTemplates.getTemplateData(false));
405   }
406
407   private void export(List selected) {
408     List datas = new ArrayList();
409     for (int i = 0; i < selected.size(); i++) {
410       Object curr = selected.get(i);
411       if (curr instanceof TemplatePersistenceData) {
412         datas.add(curr);
413       } else {
414         TemplatePersistenceData[] cat = getTemplateOfCategory(curr == COMMENT_NODE);
415         datas.addAll(Arrays.asList(cat));
416       }
417     }
418     export((TemplatePersistenceData[]) datas.toArray(new TemplatePersistenceData[datas.size()]));
419   }
420
421   private void export(TemplatePersistenceData[] templates) {
422     FileDialog dialog = new FileDialog(getShell(), SWT.SAVE);
423     dialog.setText(PreferencesMessages.getFormattedString("CodeTemplateBlock.export.title", String.valueOf(templates.length))); //$NON-NLS-1$
424     dialog.setFilterExtensions(new String[] { PreferencesMessages.getString("CodeTemplateBlock.export.extension") }); //$NON-NLS-1$
425     dialog.setFileName(PreferencesMessages.getString("CodeTemplateBlock.export.filename")); //$NON-NLS-1$
426     String path = dialog.open();
427
428     if (path == null)
429       return;
430
431     File file = new File(path);
432
433     if (file.isHidden()) {
434       String title = PreferencesMessages.getString("CodeTemplateBlock.export.error.title"); //$NON-NLS-1$ 
435       String message = PreferencesMessages.getFormattedString("CodeTemplateBlock.export.error.hidden", file.getAbsolutePath()); //$NON-NLS-1$
436       MessageDialog.openError(getShell(), title, message);
437       return;
438     }
439
440     if (file.exists() && !file.canWrite()) {
441       String title = PreferencesMessages.getString("CodeTemplateBlock.export.error.title"); //$NON-NLS-1$
442       String message = PreferencesMessages.getFormattedString("CodeTemplateBlock.export.error.canNotWrite", file.getAbsolutePath()); //$NON-NLS-1$
443       MessageDialog.openError(getShell(), title, message);
444       return;
445     }
446
447     if (!file.exists() || confirmOverwrite(file)) {
448       try {
449         OutputStream output = new BufferedOutputStream(new FileOutputStream(file));
450         TemplateReaderWriter writer = new TemplateReaderWriter();
451         writer.save(templates, output);
452       } catch (IOException e) {
453         openWriteErrorDialog(e);
454       }
455     }
456
457   }
458
459   private boolean confirmOverwrite(File file) {
460     return MessageDialog.openQuestion(getShell(), PreferencesMessages.getString("CodeTemplateBlock.export.exists.title"), //$NON-NLS-1$
461         PreferencesMessages.getFormattedString("CodeTemplateBlock.export.exists.message", file.getAbsolutePath())); //$NON-NLS-1$
462   }
463
464   public void performDefaults() {
465     IPreferenceStore prefs = PHPeclipsePlugin.getDefault().getPreferenceStore();
466     fCreateJavaDocComments.setSelection(prefs.getDefaultBoolean(PREF_JAVADOC_STUBS));
467
468     fTemplates.restoreDefaults();
469
470     // refresh
471     fCodeTemplateTree.refresh();
472     updateSourceViewerInput(fCodeTemplateTree.getSelectedElements());
473   }
474
475   public boolean performOk(boolean enabled) {
476     IPreferenceStore prefs = PreferenceConstants.getPreferenceStore();
477     prefs.setValue(PREF_JAVADOC_STUBS, fCreateJavaDocComments.isSelected());
478     PHPeclipsePlugin.getDefault().savePluginPreferences();
479
480     try {
481       fTemplates.save();
482     } catch (IOException e) {
483       PHPeclipsePlugin.log(e);
484       openWriteErrorDialog(e);
485     }
486     return true;
487   }
488
489   public void performCancel() {
490     try {
491       fTemplates.load();
492     } catch (IOException e) {
493       openReadErrorDialog(e);
494     }
495   }
496
497   private void openReadErrorDialog(Exception e) {
498     String title = PreferencesMessages.getString("CodeTemplateBlock.error.read.title"); //$NON-NLS-1$
499
500     String message = e.getLocalizedMessage();
501     if (message != null)
502       message = PreferencesMessages.getFormattedString("CodeTemplateBlock.error.parse.message", message); //$NON-NLS-1$
503     else
504       message = PreferencesMessages.getString("CodeTemplateBlock.error.read.message"); //$NON-NLS-1$
505     MessageDialog.openError(getShell(), title, message);
506   }
507
508   private void openWriteErrorDialog(Exception e) {
509     String title = PreferencesMessages.getString("CodeTemplateBlock.error.write.title"); //$NON-NLS-1$
510     String message = PreferencesMessages.getString("CodeTemplateBlock.error.write.message"); //$NON-NLS-1$
511     MessageDialog.openError(getShell(), title, message);
512   }
513
514 }