refactor: simplify collection.toArray()
[egit/eclipse.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / internal / preferences / ConfigurationEditorComponent.java
blob8de641820264439dec67dfa7140350d96c821487
1 /*******************************************************************************
2 * Copyright (c) 2010, SAP AG.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License 2.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-2.0/
8 * SPDX-License-Identifier: EPL-2.0
10 * Contributors:
11 * Mathias Kinzler (SAP AG) - initial implementation
12 *******************************************************************************/
13 package org.eclipse.egit.ui.internal.preferences;
15 import java.io.File;
16 import java.io.IOException;
17 import java.util.ArrayList;
18 import java.util.Arrays;
19 import java.util.Collections;
20 import java.util.Comparator;
21 import java.util.List;
22 import java.util.Set;
23 import java.util.StringTokenizer;
25 import org.eclipse.core.filesystem.EFS;
26 import org.eclipse.core.filesystem.IFileStore;
27 import org.eclipse.core.runtime.Path;
28 import org.eclipse.egit.ui.Activator;
29 import org.eclipse.egit.ui.internal.UIText;
30 import org.eclipse.jface.dialogs.MessageDialog;
31 import org.eclipse.jface.layout.GridDataFactory;
32 import org.eclipse.jface.layout.GridLayoutFactory;
33 import org.eclipse.jface.resource.JFaceResources;
34 import org.eclipse.jface.viewers.BaseLabelProvider;
35 import org.eclipse.jface.viewers.CellEditor;
36 import org.eclipse.jface.viewers.EditingSupport;
37 import org.eclipse.jface.viewers.ICellEditorListener;
38 import org.eclipse.jface.viewers.ICellEditorValidator;
39 import org.eclipse.jface.viewers.IFontProvider;
40 import org.eclipse.jface.viewers.ISelectionChangedListener;
41 import org.eclipse.jface.viewers.IStructuredSelection;
42 import org.eclipse.jface.viewers.ITableLabelProvider;
43 import org.eclipse.jface.viewers.SelectionChangedEvent;
44 import org.eclipse.jface.viewers.TextCellEditor;
45 import org.eclipse.jface.viewers.TreeViewer;
46 import org.eclipse.jface.viewers.TreeViewerColumn;
47 import org.eclipse.jface.window.IShellProvider;
48 import org.eclipse.jface.window.SameShellProvider;
49 import org.eclipse.jface.window.Window;
50 import org.eclipse.jgit.errors.ConfigInvalidException;
51 import org.eclipse.jgit.lib.Config;
52 import org.eclipse.jgit.lib.StoredConfig;
53 import org.eclipse.jgit.storage.file.FileBasedConfig;
54 import org.eclipse.osgi.util.NLS;
55 import org.eclipse.swt.SWT;
56 import org.eclipse.swt.events.SelectionAdapter;
57 import org.eclipse.swt.events.SelectionEvent;
58 import org.eclipse.swt.graphics.Font;
59 import org.eclipse.swt.graphics.FontData;
60 import org.eclipse.swt.graphics.Image;
61 import org.eclipse.swt.layout.GridLayout;
62 import org.eclipse.swt.widgets.Button;
63 import org.eclipse.swt.widgets.Composite;
64 import org.eclipse.swt.widgets.Control;
65 import org.eclipse.swt.widgets.Display;
66 import org.eclipse.swt.widgets.Label;
67 import org.eclipse.swt.widgets.Shell;
68 import org.eclipse.swt.widgets.Text;
69 import org.eclipse.swt.widgets.Tree;
70 import org.eclipse.swt.widgets.TreeColumn;
71 import org.eclipse.ui.PartInitException;
72 import org.eclipse.ui.PlatformUI;
73 import org.eclipse.ui.editors.text.EditorsUI;
74 import org.eclipse.ui.ide.FileStoreEditorInput;
75 import org.eclipse.ui.ide.IDE;
76 import org.eclipse.ui.model.WorkbenchAdapter;
77 import org.eclipse.ui.model.WorkbenchContentProvider;
79 /**
80 * A reusable UI component to display and edit a Git configuration.
81 * <p>
82 * Concrete subclasses that are interested in displaying error messages should
83 * override {@link #setErrorMessage(String)}
84 * <p>
85 * TODO: do the changes in memory and offer methods to obtain dirty state, to
86 * save, and something like setDirty(boolean) to be implemented by subclasses so
87 * that proper save/revert can be implemented; we could also offer this for
88 * non-stored configurations
90 public class ConfigurationEditorComponent {
92 private final static String DOT = "."; //$NON-NLS-1$
94 private StoredConfig editableConfig;
96 private final IShellProvider shellProvider;
98 private final Composite parent;
100 private final boolean useDialogFont;
102 private Composite contents;
104 private Button newValue;
106 private Button remove;
108 private TreeViewer tv;
110 private Text location;
112 private boolean editable;
114 private int margin;
117 * @param parent
118 * the parent
119 * @param config
120 * to be used instead of the user configuration
121 * @param useDialogFont
122 * if <code>true</code>, the current dialog font is used
123 * @param margin
124 * horizontal margin to be used
126 public ConfigurationEditorComponent(Composite parent, StoredConfig config,
127 boolean useDialogFont, int margin) {
128 editableConfig = config;
129 this.shellProvider = new SameShellProvider(parent);
130 this.parent = parent;
131 this.useDialogFont = useDialogFont;
132 this.margin = margin;
135 void setConfig(FileBasedConfig config) throws IOException {
136 editableConfig = config;
137 try {
138 editableConfig.clear();
139 editableConfig.load();
140 } catch (ConfigInvalidException e) {
141 throw new IOException(e.getMessage());
143 initControlsFromConfig();
147 * Saves and (in case of success) reloads the current configuration
149 * @throws IOException
151 public void save() throws IOException {
152 editableConfig.save();
153 setDirty(false);
154 initControlsFromConfig();
158 * Restores and (in case of success) reloads the current configuration
160 * @throws IOException
162 public void restore() throws IOException {
163 try {
164 editableConfig.clear();
165 editableConfig.load();
166 } catch (ConfigInvalidException e) {
167 throw new IOException(e.getMessage());
169 initControlsFromConfig();
173 * @return the control being created
175 public Control createContents() {
176 final Composite main = new Composite(parent, SWT.NONE);
177 GridLayoutFactory.fillDefaults().numColumns(2).margins(margin, 0)
178 .applyTo(main);
179 GridDataFactory.fillDefaults().grab(true, true).applyTo(main);
181 if (editableConfig instanceof FileBasedConfig) {
182 Composite locationPanel = new Composite(main, SWT.NONE);
183 GridLayout locationLayout = new GridLayout(3, false);
184 locationLayout.marginWidth = 0;
185 locationPanel.setLayout(locationLayout);
186 GridDataFactory.fillDefaults().grab(true, false).span(2, 1)
187 .applyTo(locationPanel);
188 Label locationLabel = new Label(locationPanel, SWT.NONE);
189 locationLabel
190 .setText(UIText.ConfigurationEditorComponent_ConfigLocationLabel);
191 // GridDataFactory.fillDefaults().applyTo(locationLabel);
192 int locationStyle = SWT.BORDER|SWT.READ_ONLY;
193 location = new Text(locationPanel, locationStyle);
194 GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
195 .grab(true, false).applyTo(location);
196 Button openEditor = new Button(locationPanel, SWT.PUSH);
197 openEditor
198 .setText(UIText.ConfigurationEditorComponent_OpenEditorButton);
199 openEditor
200 .setToolTipText(UIText.ConfigurationEditorComponent_OpenEditorTooltip);
201 openEditor.addSelectionListener(new SelectionAdapter() {
202 @Override
203 public void widgetSelected(SelectionEvent e) {
204 IFileStore store = EFS.getLocalFileSystem().getStore(
205 new Path(((FileBasedConfig) editableConfig)
206 .getFile().getAbsolutePath()));
207 try {
208 IDE.openEditor(PlatformUI.getWorkbench()
209 .getActiveWorkbenchWindow().getActivePage(),
210 new FileStoreEditorInput(store),
211 EditorsUI.DEFAULT_TEXT_EDITOR_ID);
212 } catch (PartInitException ex) {
213 Activator.handleError(ex.getMessage(), ex, true);
217 openEditor
218 .setEnabled(((FileBasedConfig) editableConfig).getFile() != null);
220 tv = new TreeViewer(main, SWT.SINGLE | SWT.FULL_SELECTION | SWT.BORDER);
221 Tree tree = tv.getTree();
222 GridDataFactory.fillDefaults().hint(100, 60).grab(true, true)
223 .applyTo(tree);
224 TreeColumn key = new TreeColumn(tree, SWT.NONE);
225 key.setText(UIText.ConfigurationEditorComponent_KeyColumnHeader);
226 key.setWidth(150);
228 final TextCellEditor editor = new TextCellEditor(tree);
229 editor.setValidator(new ICellEditorValidator() {
231 @Override
232 public String isValid(Object value) {
233 String editedValue = value.toString();
234 return editedValue.length() > 0 ? null
235 : UIText.ConfigurationEditorComponent_EmptyStringNotAllowed;
238 editor.addListener(new ICellEditorListener() {
240 @Override
241 public void editorValueChanged(boolean oldValidState,
242 boolean newValidState) {
243 setErrorMessage(editor.getErrorMessage());
246 @Override
247 public void cancelEditor() {
248 setErrorMessage(null);
251 @Override
252 public void applyEditorValue() {
253 setErrorMessage(null);
257 TreeColumn value = new TreeColumn(tree, SWT.NONE);
258 value.setText(UIText.ConfigurationEditorComponent_ValueColumnHeader);
259 value.setWidth(250);
260 new TreeViewerColumn(tv, value)
261 .setEditingSupport(new EditingSupport(tv) {
263 @Override
264 protected void setValue(Object element, Object newValue) {
265 Entry entry = (Entry) element;
266 if (!entry.value.equals(newValue)) {
267 entry.changeValue(newValue.toString());
268 markDirty();
272 @Override
273 protected Object getValue(Object element) {
274 return ((Entry) element).value;
277 @Override
278 protected CellEditor getCellEditor(Object element) {
279 return editor;
282 @Override
283 protected boolean canEdit(Object element) {
284 return editable && element instanceof Entry;
288 tv.setContentProvider(new WorkbenchContentProvider());
289 Font defaultFont;
290 if (useDialogFont)
291 defaultFont = JFaceResources.getDialogFont();
292 else
293 defaultFont = JFaceResources.getDefaultFont();
294 tv.setLabelProvider(new ConfigEditorLabelProvider(defaultFont));
296 tree.setHeaderVisible(true);
297 tree.setLinesVisible(true);
299 Composite buttonPanel = new Composite(main, SWT.NONE);
300 GridLayoutFactory.fillDefaults().applyTo(buttonPanel);
301 GridDataFactory.fillDefaults().grab(false, false).applyTo(buttonPanel);
302 newValue = new Button(buttonPanel, SWT.PUSH);
303 GridDataFactory.fillDefaults().applyTo(newValue);
304 newValue.setText(UIText.ConfigurationEditorComponent_AddButton);
305 newValue.addSelectionListener(new SelectionAdapter() {
306 @Override
307 public void widgetSelected(SelectionEvent e) {
309 String suggestedKey;
310 IStructuredSelection sel = (IStructuredSelection) tv
311 .getSelection();
312 Object first = sel.getFirstElement();
313 if (first instanceof Section)
314 suggestedKey = ((Section) first).name + DOT;
315 else if (first instanceof SubSection) {
316 SubSection sub = (SubSection) first;
317 suggestedKey = sub.parent.name + DOT + sub.name + DOT;
318 } else if (first instanceof Entry) {
319 Entry entry = (Entry) first;
320 if (entry.sectionparent != null)
321 suggestedKey = entry.sectionparent.name + DOT;
322 else
323 suggestedKey = entry.subsectionparent.parent.name + DOT
324 + entry.subsectionparent.name + DOT;
325 } else
326 suggestedKey = null;
328 AddConfigEntryDialog dlg = new AddConfigEntryDialog(getShell(),
329 suggestedKey);
330 if (dlg.open() == Window.OK) {
331 String result = dlg.getKey();
332 if (result == null) {
333 // bug in swt bot, see
334 // https://bugs.eclipse.org/bugs/show_bug.cgi?id=472110
335 return;
337 StringTokenizer st = new StringTokenizer(result, DOT);
338 if (st.countTokens() == 2) {
339 String sectionName = st.nextToken();
340 String entryName = st.nextToken();
341 Entry entry = ((GitConfig) tv.getInput()).getEntry(
342 sectionName, null, entryName);
343 if (entry == null)
344 editableConfig.setString(sectionName, null,
345 entryName, dlg.getValue());
346 else
347 entry.addValue(dlg.getValue());
348 markDirty();
349 reveal(sectionName, null, entryName);
350 } else if (st.countTokens() > 2) {
351 int n = st.countTokens();
352 String sectionName = st.nextToken();
353 StringBuilder b = new StringBuilder(st.nextToken());
354 for (int i = 0; i < n - 3; i++) {
355 b.append(DOT);
356 b.append(st.nextToken());
358 String subSectionName = b.toString();
359 String entryName = st.nextToken();
360 Entry entry = ((GitConfig) tv.getInput()).getEntry(
361 sectionName, subSectionName, entryName);
362 if (entry == null)
363 editableConfig.setString(sectionName,
364 subSectionName, entryName, dlg.getValue());
365 else
366 entry.addValue(dlg.getValue());
367 markDirty();
368 reveal(sectionName, subSectionName, entryName);
369 } else
370 Activator
371 .handleError(
372 UIText.ConfigurationEditorComponent_WrongNumberOfTokensMessage,
373 null, true);
378 remove = new Button(buttonPanel, SWT.PUSH);
379 GridDataFactory.fillDefaults().applyTo(remove);
380 remove.setText(UIText.ConfigurationEditorComponent_RemoveButton);
381 remove.setToolTipText(UIText.ConfigurationEditorComponent_RemoveTooltip);
382 remove.addSelectionListener(new SelectionAdapter() {
383 @Override
384 public void widgetSelected(SelectionEvent e) {
385 IStructuredSelection sel = (IStructuredSelection) tv
386 .getSelection();
387 Object first = sel.getFirstElement();
388 if (first instanceof Section) {
389 Section section = (Section) first;
390 if (MessageDialog
391 .openConfirm(
392 getShell(),
393 UIText.ConfigurationEditorComponent_RemoveSectionTitle,
394 NLS.bind(
395 UIText.ConfigurationEditorComponent_RemoveSectionMessage,
396 section.name))) {
397 editableConfig.unsetSection(section.name, null);
398 markDirty();
400 } else if (first instanceof SubSection) {
401 SubSection section = (SubSection) first;
402 if (MessageDialog
403 .openConfirm(
404 getShell(),
405 UIText.ConfigurationEditorComponent_RemoveSubsectionTitle,
406 NLS.bind(
407 UIText.ConfigurationEditorComponent_RemoveSubsectionMessage,
408 section.parent.name + DOT
409 + section.name))) {
410 editableConfig.unsetSection(section.parent.name,
411 section.name);
412 markDirty();
414 } else if (first instanceof Entry) {
415 ((Entry) first).removeValue();
416 markDirty();
419 super.widgetSelected(e);
423 tv.addSelectionChangedListener(new ISelectionChangedListener() {
424 @Override
425 public void selectionChanged(SelectionChangedEvent event) {
426 updateEnablement();
430 initControlsFromConfig();
431 contents = main;
432 return contents;
436 * @return the composite containing all the controls
438 public Composite getContents() {
439 return contents;
442 private boolean isWriteable(final File f) {
443 if (f.exists())
444 if (f.isFile())
445 if (f.canWrite())
446 return true;
447 else
448 return false;
449 else
450 return false;
451 // no file, can we create one
452 for (File d = f.getParentFile(); d != null; d = d.getParentFile())
453 if (d.isDirectory())
454 if (d.canWrite())
455 return true;
456 else
457 return false;
458 else if (d.exists())
459 return false;
460 // else continue
461 return false;
464 private void initControlsFromConfig() {
465 try {
466 editableConfig.load();
467 tv.setInput(new GitConfig(editableConfig));
468 editable = true;
469 if (editableConfig instanceof FileBasedConfig) {
470 FileBasedConfig fileConfig = (FileBasedConfig) editableConfig;
471 File configFile = fileConfig.getFile();
472 if (configFile != null)
473 if (isWriteable(configFile))
474 location.setText(configFile.getPath());
475 else {
476 location.setText(NLS
477 .bind(UIText.ConfigurationEditorComponent_ReadOnlyLocationFormat,
478 configFile.getPath()));
479 editable = false;
481 else {
482 location.setText(UIText.ConfigurationEditorComponent_NoConfigLocationKnown);
483 editable = false;
486 } catch (IOException e) {
487 Activator.handleError(e.getMessage(), e, true);
488 } catch (ConfigInvalidException e) {
489 Activator.handleError(e.getMessage(), e, true);
491 tv.expandAll();
492 updateEnablement();
496 * @param message
497 * the error message to display
499 protected void setErrorMessage(String message) {
500 // the default implementation does nothing
504 * @param dirty
505 * the dirty flag
507 protected void setDirty(boolean dirty) {
508 // the default implementation does nothing
511 private void updateEnablement() {
512 remove.setEnabled(editable && !tv.getSelection().isEmpty());
513 newValue.setEnabled(editable);
516 private void markDirty() {
517 setDirty(true);
518 ((GitConfig) tv.getInput()).refresh();
519 tv.refresh();
522 private void reveal(String sectionName, String subSectionName,
523 String entryName) {
524 Entry entry = ((GitConfig) tv.getInput()).getEntry(sectionName,
525 subSectionName, entryName);
526 if (entry != null) {
527 tv.reveal(entry);
531 private final static class GitConfig extends WorkbenchAdapter {
533 private final Config config;
535 private Section[] children;
537 GitConfig(Config config) {
538 this.config = config;
541 GitConfig refresh() {
542 children = null;
543 return this;
546 @Override
547 public Object[] getChildren(Object o) {
548 if (children == null)
549 if (config != null) {
550 List<Section> sections = new ArrayList<>();
551 Set<String> sectionNames = config.getSections();
552 for (String sectionName : sectionNames)
553 sections.add(new Section(this, sectionName));
554 Collections.sort(sections, new Comparator<Section>() {
556 @Override
557 public int compare(Section o1, Section o2) {
558 return o1.name.compareTo(o2.name);
561 children = sections.toArray(new Section[0]);
562 } else
563 children = new Section[0];
564 return children;
567 public Entry getEntry(String sectionName, String subsectionName,
568 String entryName) {
569 for (Object child : getChildren(this)) {
570 Section section = (Section) child;
571 if (sectionName.equals(section.name))
572 return section.getEntry(subsectionName, entryName);
574 return null;
578 private final static class Section extends WorkbenchAdapter {
579 private final String name;
581 private final GitConfig parent;
583 private Object[] children;
585 Section(GitConfig parent, String name) {
586 this.parent = parent;
587 this.name = name;
590 @Override
591 public int hashCode() {
592 final int prime = 31;
593 int result = 1;
594 result = prime * result + name.hashCode();
595 return result;
598 @Override
599 public boolean equals(Object obj) {
600 if (this == obj)
601 return true;
602 if (obj == null)
603 return false;
604 if (getClass() != obj.getClass())
605 return false;
606 Section other = (Section) obj;
607 if (!name.equals(other.name))
608 return false;
609 return true;
612 @Override
613 public Object getParent(Object object) {
614 return parent;
617 @Override
618 public Object[] getChildren(Object o) {
619 if (children == null) {
620 List<Object> allChildren = new ArrayList<>();
621 Set<String> subSectionNames = parent.config
622 .getSubsections(name);
623 for (String subSectionName : subSectionNames)
624 allChildren.add(new SubSection(parent.config, this,
625 subSectionName));
627 Set<String> entryNames = parent.config.getNames(name);
628 for (String entryName : entryNames) {
629 String[] values = parent.config.getStringList(name, null,
630 entryName);
631 if (values.length == 1)
632 allChildren.add(new Entry(this, entryName, values[0],
633 -1));
634 else {
635 int index = 0;
636 for (String value : values)
637 allChildren.add(new Entry(this, entryName, value,
638 index++));
641 children = allChildren.toArray();
643 return children;
646 @Override
647 public String getLabel(Object o) {
648 return name;
651 public Entry getEntry(String subsectionName, String entryName) {
652 if (subsectionName != null) {
653 for (Object child : getChildren(this))
654 if (child instanceof SubSection
655 && ((SubSection) child).name.equals(subsectionName))
656 return ((SubSection) child).getEntry(entryName);
657 } else
658 for (Object child : getChildren(this))
659 if (child instanceof Entry
660 && ((Entry) child).name.equals(entryName))
661 return (Entry) child;
662 return null;
666 private final static class SubSection extends WorkbenchAdapter {
668 private final Config config;
670 private final Section parent;
672 private final String name;
674 private Entry[] children;
676 SubSection(Config config, Section parent, String name) {
677 this.config = config;
678 this.parent = parent;
679 this.name = name;
682 @Override
683 public int hashCode() {
684 final int prime = 31;
685 int result = 1;
686 result = prime * result + name.hashCode();
687 result = prime * result + parent.hashCode();
688 return result;
691 @Override
692 public boolean equals(Object obj) {
693 if (this == obj)
694 return true;
695 if (obj == null)
696 return false;
697 if (getClass() != obj.getClass())
698 return false;
699 SubSection other = (SubSection) obj;
700 if (!name.equals(other.name))
701 return false;
702 if (!parent.equals(other.parent))
703 return false;
704 return true;
707 @Override
708 public Object[] getChildren(Object o) {
709 if (children == null) {
710 List<Entry> entries = new ArrayList<>();
711 Set<String> entryNames = config.getNames(parent.name, name);
712 for (String entryName : entryNames) {
713 String[] values = config.getStringList(parent.name, name,
714 entryName);
715 if (values.length == 1)
716 entries.add(new Entry(this, entryName, values[0], -1));
717 else {
718 int index = 0;
719 for (String value : values)
720 entries.add(new Entry(this, entryName, value,
721 index++));
724 children = entries.toArray(new Entry[0]);
726 return children;
729 @Override
730 public String getLabel(Object o) {
731 return name;
734 @Override
735 public Object getParent(Object object) {
736 return parent;
739 public Entry getEntry(String entryName) {
740 for (Object child : getChildren(this))
741 if (entryName.equals(((Entry) child).name))
742 return (Entry) child;
743 return null;
747 private final static class Entry extends WorkbenchAdapter {
749 private final Section sectionparent;
751 private final SubSection subsectionparent;
753 private final String name;
755 private final String value;
757 private final int index;
759 Entry(Section parent, String name, String value, int index) {
760 this.sectionparent = parent;
761 this.subsectionparent = null;
762 this.name = name;
763 this.value = value;
764 this.index = index;
767 public void addValue(String newValue) {
768 if (newValue.length() == 0)
769 throw new IllegalArgumentException(
770 UIText.ConfigurationEditorComponent_EmptyStringNotAllowed);
771 Config config = getConfig();
773 List<String> entries;
774 if (sectionparent != null) {
775 // Arrays.asList returns a fixed-size list, so we need to copy
776 // over to a mutable list
777 entries = new ArrayList<>(Arrays.asList(config
778 .getStringList(sectionparent.name, null, name)));
779 entries.add(Math.max(index, 0), newValue);
780 config.setStringList(sectionparent.name, null, name, entries);
781 } else {
782 // Arrays.asList returns a fixed-size list, so we need to copy
783 // over to a mutable list
784 entries = new ArrayList<>(Arrays.asList(config
785 .getStringList(subsectionparent.parent.name,
786 subsectionparent.name, name)));
787 entries.add(Math.max(index, 0), newValue);
788 config.setStringList(subsectionparent.parent.name,
789 subsectionparent.name, name, entries);
794 Entry(SubSection parent, String name, String value, int index) {
795 this.sectionparent = null;
796 this.subsectionparent = parent;
797 this.name = name;
798 this.value = value;
799 this.index = index;
802 public void changeValue(String newValue)
803 throws IllegalArgumentException {
804 if (newValue.length() == 0)
805 throw new IllegalArgumentException(
806 UIText.ConfigurationEditorComponent_EmptyStringNotAllowed);
807 Config config = getConfig();
809 if (index < 0) {
810 if (sectionparent != null)
811 config.setString(sectionparent.name, null, name, newValue);
812 else
813 config.setString(subsectionparent.parent.name,
814 subsectionparent.name, name, newValue);
815 } else {
816 String[] entries;
817 if (sectionparent != null) {
818 entries = config.getStringList(sectionparent.name, null,
819 name);
820 entries[index] = newValue;
821 config.setStringList(sectionparent.name, null, name,
822 Arrays.asList(entries));
823 } else {
824 entries = config.getStringList(
825 subsectionparent.parent.name,
826 subsectionparent.name, name);
827 entries[index] = newValue;
828 config.setStringList(subsectionparent.parent.name,
829 subsectionparent.name, name, Arrays.asList(entries));
834 private Config getConfig() {
835 Config config;
836 if (sectionparent != null)
837 config = sectionparent.parent.config;
838 else
839 config = subsectionparent.parent.parent.config;
840 return config;
843 public void removeValue() {
844 Config config = getConfig();
846 if (index < 0) {
847 if (sectionparent != null)
848 config.unset(sectionparent.name, null, name);
849 else
850 config.unset(subsectionparent.parent.name,
851 subsectionparent.name, name);
852 } else {
853 List<String> entries;
854 if (sectionparent != null) {
855 // Arrays.asList returns a fixed-size list, so we need to
856 // copy over to a mutable list
857 entries = new ArrayList<>(Arrays.asList(config
858 .getStringList(sectionparent.name, null, name)));
860 entries.remove(index);
861 config.setStringList(sectionparent.name, null, name,
862 entries);
863 } else {
864 // Arrays.asList returns a fixed-size list, so we need to
865 // copy over to a mutable list
866 entries = new ArrayList<>(Arrays.asList(config
867 .getStringList(subsectionparent.parent.name,
868 subsectionparent.name, name)));
869 // the list is fixed-size, so we have to copy over
870 entries.remove(index);
871 config.setStringList(subsectionparent.parent.name,
872 subsectionparent.name, name, entries);
877 @Override
878 public int hashCode() {
879 final int prime = 31;
880 int result = 1;
881 result = prime * result + index;
882 result = prime * result + name.hashCode();
883 result = prime * result
884 + ((sectionparent == null) ? 0 : sectionparent.hashCode());
885 result = prime
886 * result
887 + ((subsectionparent == null) ? 0 : subsectionparent
888 .hashCode());
889 return result;
892 @Override
893 public boolean equals(Object obj) {
894 if (this == obj)
895 return true;
896 if (obj == null)
897 return false;
898 if (getClass() != obj.getClass())
899 return false;
900 Entry other = (Entry) obj;
901 // the index may change between 0 and -1 when values are added
902 if (index != other.index && (index > 0 || other.index > 0))
903 return false;
904 if (!name.equals(other.name))
905 return false;
906 if (sectionparent == null) {
907 if (other.sectionparent != null)
908 return false;
909 } else if (!sectionparent.equals(other.sectionparent))
910 return false;
911 if (subsectionparent == null) {
912 if (other.subsectionparent != null)
913 return false;
914 } else if (!subsectionparent.equals(other.subsectionparent))
915 return false;
916 return true;
919 @Override
920 public Object getParent(Object object) {
921 if (sectionparent != null)
922 return sectionparent;
923 else
924 return subsectionparent;
928 private static final class ConfigEditorLabelProvider extends
929 BaseLabelProvider implements ITableLabelProvider, IFontProvider {
930 private Font boldFont = null;
932 private final Font defaultFont;
934 public ConfigEditorLabelProvider(Font defaultFont) {
935 this.defaultFont = defaultFont;
938 @Override
939 public Image getColumnImage(Object element, int columnIndex) {
940 return null;
943 @Override
944 public String getColumnText(Object element, int columnIndex) {
945 switch (columnIndex) {
946 case 0:
947 if (element instanceof Section)
948 return ((Section) element).name;
949 if (element instanceof SubSection)
950 return ((SubSection) element).name;
951 if (element instanceof Entry) {
952 Entry entry = (Entry) element;
953 if (entry.index < 0)
954 return entry.name;
955 return entry.name + "[" + entry.index + "]"; //$NON-NLS-1$ //$NON-NLS-2$
957 return null;
958 case 1:
959 if (element instanceof Entry)
960 return ((Entry) element).value;
961 return null;
962 default:
963 return null;
967 @Override
968 public Font getFont(Object element) {
969 if (element instanceof Section || element instanceof SubSection)
970 return getBoldFont();
971 else
972 return null;
975 private Font getBoldFont() {
976 if (boldFont != null)
977 return boldFont;
978 FontData[] data = defaultFont.getFontData();
979 for (int i = 0; i < data.length; i++)
980 data[i].setStyle(data[i].getStyle() | SWT.BOLD);
982 boldFont = new Font(Display.getDefault(), data);
983 return boldFont;
986 @Override
987 public void dispose() {
988 if (boldFont != null)
989 boldFont.dispose();
990 super.dispose();
994 private Shell getShell() {
995 return shellProvider.getShell();