avoid NullPointerException in debug
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / ui / preferences / OptionsConfigurationBlock.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2003 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.util.ArrayList;
14 import java.util.HashMap;
15 import java.util.Hashtable;
16 import java.util.Iterator;
17 import java.util.Map;
18 import java.util.StringTokenizer;
19 import java.util.Map.Entry;
20
21 import net.sourceforge.phpdt.core.IJavaProject;
22 import net.sourceforge.phpdt.core.JavaCore;
23 import net.sourceforge.phpdt.internal.ui.wizards.IStatusChangeListener;
24 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
25
26 import org.eclipse.core.resources.IncrementalProjectBuilder;
27 import org.eclipse.core.resources.ResourcesPlugin;
28 import org.eclipse.core.runtime.CoreException;
29 import org.eclipse.core.runtime.IProgressMonitor;
30 import org.eclipse.core.runtime.IStatus;
31 import org.eclipse.core.runtime.OperationCanceledException;
32 import org.eclipse.core.runtime.Status;
33 import org.eclipse.core.runtime.SubProgressMonitor;
34 import org.eclipse.core.runtime.jobs.Job;
35 import org.eclipse.jface.dialogs.IDialogConstants;
36 import org.eclipse.jface.dialogs.MessageDialog;
37 import org.eclipse.swt.SWT;
38 import org.eclipse.swt.events.ModifyEvent;
39 import org.eclipse.swt.events.ModifyListener;
40 import org.eclipse.swt.events.SelectionEvent;
41 import org.eclipse.swt.events.SelectionListener;
42 import org.eclipse.swt.layout.GridData;
43 import org.eclipse.swt.layout.GridLayout;
44 import org.eclipse.swt.widgets.Button;
45 import org.eclipse.swt.widgets.Combo;
46 import org.eclipse.swt.widgets.Composite;
47 import org.eclipse.swt.widgets.Control;
48 import org.eclipse.swt.widgets.Label;
49 import org.eclipse.swt.widgets.Shell;
50 import org.eclipse.swt.widgets.Text;
51 import org.eclipse.swt.widgets.Widget;
52
53 /**
54  * Abstract options configuration block providing a general implementation for setting up
55  * an options configuration page.
56  * 
57  * @since 2.1
58  */
59 public abstract class OptionsConfigurationBlock {
60
61         protected static class ControlData {
62                 private String fKey;
63                 private String[] fValues;
64                 
65                 public ControlData(String key, String[] values) {
66                         fKey= key;
67                         fValues= values;
68                 }
69                 
70                 public String getKey() {
71                         return fKey;
72                 }
73                 
74                 public String getValue(boolean selection) {
75                         int index= selection ? 0 : 1;
76                         return fValues[index];
77                 }
78                 
79                 public String getValue(int index) {
80                         return fValues[index];
81                 }               
82                 
83                 public int getSelection(String value) {
84                         if (value != null) {
85                                 for (int i= 0; i < fValues.length; i++) {
86                                         if (value.equals(fValues[i])) {
87                                                 return i;
88                                         }
89                                 }
90                         }
91                         return fValues.length -1; // assume the last option is the least severe
92                 }
93         }
94         
95         
96         protected Map fWorkingValues;
97
98         protected ArrayList fCheckBoxes;
99         protected ArrayList fComboBoxes;
100         protected ArrayList fTextBoxes;
101         protected HashMap fLabels;
102         
103         private SelectionListener fSelectionListener;
104         private ModifyListener fTextModifyListener;
105
106         protected IStatusChangeListener fContext;
107         protected IJavaProject fProject; // project or null
108         protected String[] fAllKeys;
109         
110         private Shell fShell;
111
112         public OptionsConfigurationBlock(IStatusChangeListener context, IJavaProject project, String[] allKeys) {
113                 fContext= context;
114                 fProject= project;
115                 fAllKeys= allKeys;
116                 
117                 fWorkingValues= getOptions(true);
118                 testIfOptionsComplete(fWorkingValues, allKeys);
119                 
120                 fCheckBoxes= new ArrayList();
121                 fComboBoxes= new ArrayList();
122                 fTextBoxes= new ArrayList(2);
123                 fLabels= new HashMap();
124         }
125         
126         private void testIfOptionsComplete(Map workingValues, String[] allKeys) {
127                 for (int i= 0; i < allKeys.length; i++) {
128                         if (workingValues.get(allKeys[i]) == null) {
129                                 PHPeclipsePlugin.logErrorMessage("preference option missing: " + allKeys[i] + " (" + this.getClass().getName() +')');  //$NON-NLS-1$//$NON-NLS-2$
130                         }
131                 }
132         }
133
134         protected Map getOptions(boolean inheritJavaCoreOptions) {
135                 if (fProject != null) {
136                         return fProject.getOptions(inheritJavaCoreOptions);
137                 } else {
138                         return JavaCore.getOptions();
139                 }       
140         }
141         
142         protected Map getDefaultOptions() {
143                 return JavaCore.getDefaultOptions();
144         }       
145         
146         public final boolean hasProjectSpecificOptions() {
147                 if (fProject != null) {
148                         Map settings= fProject.getOptions(false);
149                         String[] allKeys= fAllKeys;
150                         for (int i= 0; i < allKeys.length; i++) {
151                                 if (settings.get(allKeys[i]) != null) {
152                                         return true;
153                                 }
154                         }
155                 }
156                 return false;
157         }       
158                 
159         protected void setOptions(Map map) {
160                 if (fProject != null) {
161                         Map oldOptions= fProject.getOptions(false);
162                         fProject.setOptions(map);
163                         firePropertyChangeEvents(oldOptions, map);
164                 } else {
165                         JavaCore.setOptions((Hashtable) map);
166                 }       
167         } 
168         
169         /**
170          * Computes the differences between the given old and new options and fires corresponding
171          * property change events on the Java plugin's mockup preference store.
172          * @param oldOptions The old options
173          * @param newOptions The new options
174          */
175         private void firePropertyChangeEvents(Map oldOptions, Map newOptions) {
176                 oldOptions= new HashMap(oldOptions);
177                 Object source= fProject.getProject();
178                 MockupPreferenceStore store= PHPeclipsePlugin.getDefault().getMockupPreferenceStore();
179                 Iterator iter= newOptions.entrySet().iterator();
180                 while (iter.hasNext()) {
181                         Entry entry= (Entry) iter.next();
182                 
183                         String name= (String) entry.getKey();
184                         Object oldValue= oldOptions.get(name);
185                         Object newValue= entry.getValue();
186                         
187                         if ((oldValue != null && !oldValue.equals(newValue)) || (oldValue == null && newValue != null))
188                                 store.firePropertyChangeEvent(source, name, oldValue, newValue);
189                         oldOptions.remove(name);
190                 }
191                 
192                 iter= oldOptions.entrySet().iterator();
193                 while (iter.hasNext()) {
194                         Entry entry= (Entry) iter.next();
195                         store.firePropertyChangeEvent(source, (String) entry.getKey(), entry.getValue(), null);
196                 }
197         }
198
199         protected Shell getShell() {
200                 return fShell;
201         }
202         
203         protected void setShell(Shell shell) {
204                 fShell= shell;
205         }       
206         
207         protected abstract Control createContents(Composite parent);
208         
209         protected Button addCheckBox(Composite parent, String label, String key, String[] values, int indent) {
210                 ControlData data= new ControlData(key, values);
211                 
212                 GridData gd= new GridData(GridData.HORIZONTAL_ALIGN_FILL);
213                 gd.horizontalSpan= 3;
214                 gd.horizontalIndent= indent;
215                 
216                 Button checkBox= new Button(parent, SWT.CHECK);
217                 checkBox.setText(label);
218                 checkBox.setData(data);
219                 checkBox.setLayoutData(gd);
220                 checkBox.addSelectionListener(getSelectionListener());
221                 
222                 String currValue= (String)fWorkingValues.get(key);
223                 checkBox.setSelection(data.getSelection(currValue) == 0);
224                 
225                 fCheckBoxes.add(checkBox);
226                 
227                 return checkBox;
228         }
229         
230         protected Combo addComboBox(Composite parent, String label, String key, String[] values, String[] valueLabels, int indent) {
231                 ControlData data= new ControlData(key, values);
232                 
233                 GridData gd= new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
234                 gd.horizontalIndent= indent;
235                                 
236                 Label labelControl= new Label(parent, SWT.LEFT | SWT.WRAP);
237                 labelControl.setText(label);
238                 labelControl.setLayoutData(gd);
239                 
240                 Combo comboBox= new Combo(parent, SWT.READ_ONLY);
241                 comboBox.setItems(valueLabels);
242                 comboBox.setData(data);
243                 comboBox.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL));
244                 comboBox.addSelectionListener(getSelectionListener());
245                 
246                 fLabels.put(comboBox, labelControl);
247                 
248                 Label placeHolder= new Label(parent, SWT.NONE);
249                 placeHolder.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
250                 
251                 String currValue= (String)fWorkingValues.get(key);      
252                 comboBox.select(data.getSelection(currValue));
253                 
254                 fComboBoxes.add(comboBox);
255                 return comboBox;
256         }
257         
258         protected void addInversedComboBox(Composite parent, String label, String key, String[] values, String[] valueLabels, int indent) {
259                 ControlData data= new ControlData(key, values);
260                 
261                 GridData gd= new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
262                 gd.horizontalIndent= indent;
263                 gd.horizontalSpan= 3;
264                 
265                 Composite composite= new Composite(parent, SWT.NONE);
266                 GridLayout layout= new GridLayout();
267                 layout.marginHeight= 0;
268                 layout.marginWidth= 0;
269                 layout.numColumns= 2;
270                 composite.setLayout(layout);
271                 composite.setLayoutData(gd);
272                 
273                 Combo comboBox= new Combo(composite, SWT.READ_ONLY);
274                 comboBox.setItems(valueLabels);
275                 comboBox.setData(data);
276                 comboBox.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL));
277                 comboBox.addSelectionListener(getSelectionListener());
278                 
279                 Label labelControl= new Label(composite, SWT.LEFT | SWT.WRAP);
280                 labelControl.setText(label);
281                 labelControl.setLayoutData(new GridData());
282                 
283                 fLabels.put(comboBox, labelControl);
284                 
285                 String currValue= (String)fWorkingValues.get(key);      
286                 comboBox.select(data.getSelection(currValue));
287                 
288                 fComboBoxes.add(comboBox);
289         }
290         
291         protected Text addTextField(Composite parent, String label, String key, int indent, int widthHint) {    
292                 Label labelControl= new Label(parent, SWT.NONE);
293                 labelControl.setText(label);
294                 labelControl.setLayoutData(new GridData());
295                                 
296                 Text textBox= new Text(parent, SWT.BORDER | SWT.SINGLE);
297                 textBox.setData(key);
298                 textBox.setLayoutData(new GridData());
299                 
300                 fLabels.put(textBox, labelControl);
301                 
302                 String currValue= (String) fWorkingValues.get(key);     
303                 if (currValue != null) {
304                         textBox.setText(currValue);
305                 }
306                 textBox.addModifyListener(getTextModifyListener());
307
308                 GridData data= new GridData(GridData.HORIZONTAL_ALIGN_FILL);
309                 if (widthHint != 0) {
310                         data.widthHint= widthHint;
311                 }
312                 data.horizontalIndent= indent;
313                 data.horizontalSpan= 2;
314                 textBox.setLayoutData(data);
315
316                 fTextBoxes.add(textBox);
317                 return textBox;
318         }       
319
320         protected SelectionListener getSelectionListener() {
321                 if (fSelectionListener == null) {
322                         fSelectionListener= new SelectionListener() {
323                                 public void widgetDefaultSelected(SelectionEvent e) {}
324         
325                                 public void widgetSelected(SelectionEvent e) {
326                                         controlChanged(e.widget);
327                                 }
328                         };
329                 }
330                 return fSelectionListener;
331         }
332         
333         protected ModifyListener getTextModifyListener() {
334                 if (fTextModifyListener == null) {
335                         fTextModifyListener= new ModifyListener() {
336                                 public void modifyText(ModifyEvent e) {
337                                         textChanged((Text) e.widget);
338                                 }
339                         };
340                 }
341                 return fTextModifyListener;
342         }               
343         
344         protected void controlChanged(Widget widget) {
345                 ControlData data= (ControlData) widget.getData();
346                 String newValue= null;
347                 if (widget instanceof Button) {
348                         newValue= data.getValue(((Button)widget).getSelection());                       
349                 } else if (widget instanceof Combo) {
350                         newValue= data.getValue(((Combo)widget).getSelectionIndex());
351                 } else {
352                         return;
353                 }
354                 fWorkingValues.put(data.getKey(), newValue);
355                 
356                 validateSettings(data.getKey(), newValue);
357         }
358         
359         protected void textChanged(Text textControl) {
360                 String key= (String) textControl.getData();
361                 String number= textControl.getText();
362                 fWorkingValues.put(key, number);
363                 validateSettings(key, number);
364         }       
365
366         protected boolean checkValue(String key, String value) {
367                 return value.equals(fWorkingValues.get(key));
368         }
369         
370         /* (non-javadoc)
371          * Update fields and validate.
372          * @param changedKey Key that changed, or null, if all changed.
373          */     
374         protected abstract void validateSettings(String changedKey, String newValue);
375         
376         
377         protected String[] getTokens(String text, String separator) {
378                 StringTokenizer tok= new StringTokenizer(text, separator); //$NON-NLS-1$
379                 int nTokens= tok.countTokens();
380                 String[] res= new String[nTokens];
381                 for (int i= 0; i < res.length; i++) {
382                         res[i]= tok.nextToken().trim();
383                 }
384                 return res;
385         }       
386
387         
388         public boolean performOk(boolean enabled) {
389                 String[] allKeys= fAllKeys;
390                 Map actualOptions= getOptions(false);
391                 
392                 // preserve other options
393                 boolean hasChanges= false;
394                 for (int i= 0; i < allKeys.length; i++) {
395                         String key= allKeys[i];
396                         String oldVal= (String) actualOptions.get(key);
397                         String val= null;
398                         if (enabled) {
399                                 val= (String) fWorkingValues.get(key);
400                                 if (val != null && !val.equals(oldVal)) {
401                                         hasChanges= true;
402                                         actualOptions.put(key, val);
403                                 }
404                         } else {
405                                 if (oldVal != null) {
406                                         actualOptions.remove(key);
407                                         hasChanges= true;
408                                 }
409                         }
410                 }
411                 
412                 
413                 if (hasChanges) {
414                         boolean doBuild= false;
415                         String[] strings= getFullBuildDialogStrings(fProject == null);
416                         if (strings != null) {
417                                 MessageDialog dialog= new MessageDialog(getShell(), strings[0], null, strings[1], MessageDialog.QUESTION, new String[] { IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL, IDialogConstants.CANCEL_LABEL }, 2);
418                                 int res= dialog.open();
419                                 if (res == 0) {
420                                         doBuild= true;
421                                 } else if (res != 1) {
422                                         return false; // cancel pressed
423                                 }
424                         }
425                         setOptions(actualOptions);
426                         if (doBuild) {
427                                 boolean res= doFullBuild();
428                                 if (!res) {
429                                         return false;
430                                 }
431                         }
432                 }
433                 return true;
434         }
435         
436         protected abstract String[] getFullBuildDialogStrings(boolean workspaceSettings);
437                 
438         protected boolean doFullBuild() {
439                 
440                 Job buildJob = new Job(PreferencesMessages.getString("OptionsConfigurationBlock.job.title")){  //$NON-NLS-1$
441                         /* (non-Javadoc)
442                          * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
443                          */
444                         protected IStatus run(IProgressMonitor monitor) {
445                                 try {
446                                         if (fProject != null) {
447                                                 monitor.setTaskName(PreferencesMessages.getFormattedString("OptionsConfigurationBlock.buildproject.taskname", fProject.getElementName())); //$NON-NLS-1$
448                                                 fProject.getProject().build(IncrementalProjectBuilder.FULL_BUILD, new SubProgressMonitor(monitor,1));
449                                                 PHPeclipsePlugin.getWorkspace().build(IncrementalProjectBuilder.INCREMENTAL_BUILD, new SubProgressMonitor(monitor,1));
450                                         } else {
451                                                 monitor.setTaskName(PreferencesMessages.getString("OptionsConfigurationBlock.buildall.taskname")); //$NON-NLS-1$
452                                                 PHPeclipsePlugin.getWorkspace().build(IncrementalProjectBuilder.FULL_BUILD, new SubProgressMonitor(monitor, 2));
453                                         }
454                                 } catch (CoreException e) {
455                                         return e.getStatus();
456                                 } catch (OperationCanceledException e) {
457                                         return Status.CANCEL_STATUS;
458                                 }
459                                 finally {
460                                         monitor.done();
461                                 }
462                                 return Status.OK_STATUS;
463                         }
464                         public boolean belongsTo(Object family) {
465                                 return ResourcesPlugin.FAMILY_MANUAL_BUILD == family;
466                         }
467                 };
468                 
469                 buildJob.setRule(ResourcesPlugin.getWorkspace().getRuleFactory().buildRule());
470                 buildJob.setUser(true); 
471                 buildJob.schedule();
472                 return true;
473         }               
474         
475         public void performDefaults() {
476                 fWorkingValues= getDefaultOptions();
477                 updateControls();
478                 validateSettings(null, null);
479         }
480         
481         protected void updateControls() {
482                 // update the UI
483                 for (int i= fCheckBoxes.size() - 1; i >= 0; i--) {
484                         updateCheckBox((Button) fCheckBoxes.get(i));
485                 }
486                 for (int i= fComboBoxes.size() - 1; i >= 0; i--) {
487                         updateCombo((Combo) fComboBoxes.get(i));
488                 }
489                 for (int i= fTextBoxes.size() - 1; i >= 0; i--) {
490                         updateText((Text) fTextBoxes.get(i));
491                 }
492         }
493         
494         protected void updateCombo(Combo curr) {
495                 ControlData data= (ControlData) curr.getData();
496                 
497                 String currValue= (String) fWorkingValues.get(data.getKey());   
498                 curr.select(data.getSelection(currValue));                                      
499         }
500         
501         protected void updateCheckBox(Button curr) {
502                 ControlData data= (ControlData) curr.getData();
503                 
504                 String currValue= (String) fWorkingValues.get(data.getKey());   
505                 curr.setSelection(data.getSelection(currValue) == 0);                                           
506         }
507         
508         protected void updateText(Text curr) {
509                 String key= (String) curr.getData();
510                 
511                 String currValue= (String) fWorkingValues.get(key);
512                 if (currValue != null) {
513                         curr.setText(currValue);
514                 }
515         }
516         
517         protected Button getCheckBox(String key) {
518                 for (int i= fCheckBoxes.size() - 1; i >= 0; i--) {
519                         Button curr= (Button) fCheckBoxes.get(i);
520                         ControlData data= (ControlData) curr.getData();
521                         if (key.equals(data.getKey())) {
522                                 return curr;
523                         }
524                 }
525                 return null;            
526         }
527         
528         protected Combo getComboBox(String key) {
529                 for (int i= fComboBoxes.size() - 1; i >= 0; i--) {
530                         Combo curr= (Combo) fComboBoxes.get(i);
531                         ControlData data= (ControlData) curr.getData();
532                         if (key.equals(data.getKey())) {
533                                 return curr;
534                         }
535                 }
536                 return null;            
537         }
538         
539         protected void setComboEnabled(String key, boolean enabled) {
540                 Combo combo= getComboBox(key);
541                 Label label= (Label) fLabels.get(combo);
542                 combo.setEnabled(enabled);
543                 label.setEnabled(enabled);
544         }
545         
546         
547         
548 }