19 * the distribution.
20 *
21 * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
22 * endorse or promote products derived from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
26 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
31 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33 package org.openjdk.jmc.flightrecorder.ui;
34
35 import java.text.MessageFormat;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.Collection;
39 import java.util.Comparator;
40 import java.util.HashSet;
41 import java.util.Iterator;
42 import java.util.LinkedList;
43 import java.util.List;
44 import java.util.Objects;
45 import java.util.Set;
46 import java.util.concurrent.CompletableFuture;
47 import java.util.function.Consumer;
48 import java.util.function.Function;
49 import java.util.function.Predicate;
50 import java.util.logging.Level;
51 import java.util.stream.Collectors;
52 import java.util.stream.Stream;
53
54 import org.eclipse.jface.viewers.ArrayContentProvider;
55 import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
56 import org.eclipse.jface.viewers.ISelection;
57 import org.eclipse.jface.viewers.IStructuredSelection;
58 import org.eclipse.jface.viewers.TableViewer;
59 import org.eclipse.osgi.util.NLS;
60 import org.eclipse.swt.SWT;
61 import org.eclipse.swt.graphics.Image;
62 import org.eclipse.swt.widgets.Composite;
63 import org.eclipse.swt.widgets.Control;
64 import org.eclipse.swt.widgets.Display;
65 import org.eclipse.ui.IWorkbenchPart;
66 import org.eclipse.ui.PlatformUI;
67 import org.eclipse.ui.part.Page;
68 import org.eclipse.ui.views.properties.IPropertySheetPage;
69 import org.openjdk.jmc.common.IDescribable;
70 import org.openjdk.jmc.common.IDisplayable;
71 import org.openjdk.jmc.common.IState;
72 import org.openjdk.jmc.common.collection.IteratorToolkit;
73 import org.openjdk.jmc.common.item.Aggregators;
74 import org.openjdk.jmc.common.item.IAttribute;
75 import org.openjdk.jmc.common.item.IItem;
76 import org.openjdk.jmc.common.item.IItemCollection;
77 import org.openjdk.jmc.common.item.IItemFilter;
78 import org.openjdk.jmc.common.item.IItemIterable;
79 import org.openjdk.jmc.common.item.IMemberAccessor;
80 import org.openjdk.jmc.common.item.IType;
81 import org.openjdk.jmc.common.unit.ContentType;
82 import org.openjdk.jmc.common.unit.IQuantity;
83 import org.openjdk.jmc.common.unit.IRange;
84 import org.openjdk.jmc.common.unit.KindOfQuantity;
85 import org.openjdk.jmc.common.unit.QuantitiesToolkit;
86 import org.openjdk.jmc.common.unit.QuantityRange;
87 import org.openjdk.jmc.common.unit.RangeContentType;
88 import org.openjdk.jmc.common.unit.UnitLookup;
89 import org.openjdk.jmc.common.util.TypeHandling;
90 import org.openjdk.jmc.flightrecorder.JfrAttributes;
91 import org.openjdk.jmc.flightrecorder.ui.common.DataPageToolkit;
92 import org.openjdk.jmc.flightrecorder.ui.messages.internal.Messages;
93 import org.openjdk.jmc.flightrecorder.ui.preferences.PreferenceKeys;
94 import org.openjdk.jmc.flightrecorder.ui.selection.FlavoredSelectionBase;
95 import org.openjdk.jmc.flightrecorder.ui.selection.IFilterFlavor;
96 import org.openjdk.jmc.flightrecorder.ui.selection.IFlavoredSelection;
97 import org.openjdk.jmc.flightrecorder.ui.selection.IItemStreamFlavor;
98 import org.openjdk.jmc.flightrecorder.ui.selection.IPropertyFlavor;
99 import org.openjdk.jmc.flightrecorder.ui.selection.ItemBackedSelection;
100 import org.openjdk.jmc.ui.TypeAppearance;
101 import org.openjdk.jmc.ui.UIPlugin;
102 import org.openjdk.jmc.ui.accessibility.FocusTracker;
103 import org.openjdk.jmc.ui.column.ColumnBuilder;
104 import org.openjdk.jmc.ui.column.ColumnManager;
105 import org.openjdk.jmc.ui.column.ColumnMenusFactory;
106 import org.openjdk.jmc.ui.column.IColumn;
107 import org.openjdk.jmc.ui.column.TableSettings;
108 import org.openjdk.jmc.ui.column.TableSettings.ColumnSettings;
109 import org.openjdk.jmc.ui.common.util.AdapterUtil;
110 import org.openjdk.jmc.ui.handlers.ActionToolkit;
111 import org.openjdk.jmc.ui.handlers.MCContextMenuManager;
112 import org.openjdk.jmc.ui.misc.DisplayToolkit;
113 import org.openjdk.jmc.ui.misc.TypedLabelProvider;
114
115 // FIXME: fields - units - filters - icons etc. should be handled more properly
116 public class JfrPropertySheet extends Page implements IPropertySheetPage {
117
118 private static final String HELP_CONTEXT_ID = FlightRecorderUI.PLUGIN_ID + ".JfrPropertiesView"; //$NON-NLS-1$
119 private static final Object TOO_MANY_VALUES = new Object();
120 private static final PropertySheetRow CALCULATING = new PropertySheetRow(null, null);
121
122 private static class PropertySheetRowSelection extends FlavoredSelectionBase {
123
124 private final PropertySheetRow row;
125
126 PropertySheetRowSelection(PropertySheetRow row) {
127 super(MessageFormat.format(Messages.JFR_PROPERTIES_PROPERTY_SELECTION, row.attribute.getName()));
128 this.row = row;
129 }
130
131 @Override
132 public Stream<IItemStreamFlavor> getFlavors(
133 IItemFilter dstFilter, IItemCollection items, List<IAttribute<?>> dstAttributes) {
134 /*
135 * FIXME: Is this the desired behavior? Discuss and change if necessary.
136 *
137 * This most likely need more thought and discussion, but the implemented order of
138 * flavors is currently:
139 *
140 * For chart selections:
141 *
281 @Override
282 protected Image getImageTyped(PropertySheetRow p) {
283 if (p.attribute != null) {
284 Image icon = TypeAppearance.getImage(p.attribute.getContentType().getIdentifier());
285 return icon == null ? UIPlugin.getDefault().getImage(UIPlugin.ICON_PROPERTY_OBJECT) : icon;
286 }
287 return null;
288 };
289 }).build();
290
291 private static final IColumn VALUE_COLUMN = new ColumnBuilder(Messages.JFR_PROPERTY_SHEET_VALUE, "value", //$NON-NLS-1$
292 new TypedLabelProvider<PropertySheetRow>(PropertySheetRow.class) {
293 @Override
294 protected String getTextTyped(PropertySheetRow p) {
295 Object value = p.getValue();
296 if (p == CALCULATING) {
297 return Messages.JFR_PROPERTIES_CALCULATING;
298 } else if (value == TOO_MANY_VALUES) {
299 return Messages.JFR_PROPERTIES_TOO_MANY_VALUES;
300 }
301 return getValueString(value);
302 };
303
304 // FIXME: Merge with TypeHandling.getValueString
305 private String getValueString(Object value) {
306 if (value instanceof IItemCollection) {
307 return itemCollectionDescription((IItemCollection) value);
308 } else if (value instanceof IDescribable) {
309 return ((IDescribable) value).getName();
310 } else if (value instanceof IDescribable[] && ((IDescribable[]) value).length > 0) {
311 IDescribable[] values = ((IDescribable[]) value);
312 return "[" + values[0].getName() + " ... " //$NON-NLS-1$ //$NON-NLS-2$
313 + values[values.length - 1].getName() + "]"; //$NON-NLS-1$
314 } else if (value instanceof Object[]) {
315
316 return limitedDeepToString((Object[]) value, this::getValueString);
317 } else if (value instanceof Collection) {
318 return limitedDeepToString(((Collection<?>) value).toArray(), this::getValueString);
319 }
320 return TypeHandling.getValueString(value);
321 }
322
323 @Override
324 protected String getToolTipTextTyped(PropertySheetRow p) {
325 Object value = p.getValue();
326 if (value instanceof IQuantity) {
327 return TypeHandling.getNumericString(((IQuantity) value).numberValue());
328 }
329 return JfrPropertySheet.getVerboseString(value);
330 };
331
332 }).build();
333
334 private static String limitedDeepToString(Object[] array, Function<Object, String> valueToStringProvider) {
335 return limitedDeepToString(array, new StringBuilder(), true, valueToStringProvider);
336 }
337
338 private static String limitedDeepToString(
339 Object[] array, StringBuilder builder, boolean isRootArray, Function<Object, String> valueToStringProvider) {
340 int maxCharacters = FlightRecorderUI.getDefault().getPreferenceStore()
341 .getInt(PreferenceKeys.PROPERTY_MAXIMUM_PROPERTIES_ARRAY_STRING_SIZE);
342 int omitted = 0;
343 builder.append('[');
344 for (int i = 0; i < array.length; i++) {
345 Object element = array[i];
346 if (element != null && element.getClass().isArray()) {
347 limitedDeepToString((Object[]) element, builder, false, valueToStringProvider);
348 } else {
349 builder.append(valueToStringProvider.apply(element));
350 }
351 if ((i < (array.length - 1)) && builder.length() < maxCharacters) {
352 builder.append(',');
353 builder.append(' ');
426 viewer = new TableViewer(parent, SWT.MULTI | SWT.FULL_SELECTION);
427 viewer.setContentProvider(ArrayContentProvider.getInstance());
428 // FIXME: Should we keep a state for the properties view?
429 ColumnManager manager = ColumnManager.build(viewer,
430 Arrays.asList(FIELD_COLUMN, VALUE_COLUMN, VERBOSE_VALUE_COLUMN), getTableSettings(null));
431 MCContextMenuManager mm = MCContextMenuManager.create(viewer.getControl());
432 ColumnMenusFactory.addDefaultMenus(manager, mm);
433 Function<Consumer<IFlavoredSelection>, Function<List<PropertySheetRow>, Runnable>> actionProvider = flavorConsumer -> selected -> {
434 if (selected.size() == 1 && selected.get(0).value != TOO_MANY_VALUES) {
435 if (selected.get(0).attribute != null) {
436 return () -> flavorConsumer.accept(new PropertySheetRowSelection(selected.get(0)));
437 } else if (selected.get(0).value instanceof IItemCollection) {
438 IItemCollection items = (IItemCollection) selected.get(0).value;
439 String selectionName = itemCollectionDescription(items);
440 return () -> flavorConsumer.accept(new ItemBackedSelection(items, selectionName));
441 }
442 }
443 return null;
444 };
445 // FIXME: Break out to other place where these actions are added to menus
446 mm.appendToGroup(MCContextMenuManager.GROUP_EDIT,
447 ActionToolkit.forListSelection(viewer, Messages.STORE_SELECTION_ACTION, false,
448 actionProvider.apply(controller.getSelectionStore()::addSelection)));
449 mm.appendToGroup(MCContextMenuManager.GROUP_EDIT,
450 ActionToolkit.forListSelection(viewer, Messages.STORE_AND_ACTIVATE_SELECTION_ACTION, false,
451 actionProvider.apply(controller.getSelectionStore()::addAndSetAsCurrentSelection)));
452 ColumnViewerToolTipSupport.enableFor(viewer);
453 PlatformUI.getWorkbench().getHelpSystem().setHelp(viewer.getControl(), HELP_CONTEXT_ID);
454
455 if (UIPlugin.getDefault().getAccessibilityMode()) {
456 FocusTracker.enableFocusTracking(viewer.getTable());
457 }
458 }
459
460 private static TableSettings getTableSettings(IState state) {
461 if (state == null) {
462 return new TableSettings(null,
463 Arrays.asList(new ColumnSettings(FIELD_COLUMN.getId(), false, 120, null),
464 new ColumnSettings(VALUE_COLUMN.getId(), false, 120, null),
465 new ColumnSettings(VERBOSE_VALUE_COLUMN.getId(), true, 400, null)));
|
19 * the distribution.
20 *
21 * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
22 * endorse or promote products derived from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
26 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
31 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33 package org.openjdk.jmc.flightrecorder.ui;
34
35 import java.text.MessageFormat;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.Collection;
39 import java.util.Collections;
40 import java.util.Comparator;
41 import java.util.HashSet;
42 import java.util.Iterator;
43 import java.util.LinkedList;
44 import java.util.List;
45 import java.util.Objects;
46 import java.util.Set;
47 import java.util.concurrent.CompletableFuture;
48 import java.util.function.Consumer;
49 import java.util.function.Function;
50 import java.util.function.Predicate;
51 import java.util.logging.Level;
52 import java.util.stream.Collectors;
53 import java.util.stream.Stream;
54
55 import org.eclipse.jface.action.IAction;
56 import org.eclipse.jface.resource.ImageDescriptor;
57 import org.eclipse.jface.viewers.ArrayContentProvider;
58 import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
59 import org.eclipse.jface.viewers.ISelection;
60 import org.eclipse.jface.viewers.IStructuredSelection;
61 import org.eclipse.jface.viewers.TableViewer;
62 import org.eclipse.osgi.util.NLS;
63 import org.eclipse.swt.SWT;
64 import org.eclipse.swt.graphics.Image;
65 import org.eclipse.swt.widgets.Composite;
66 import org.eclipse.swt.widgets.Control;
67 import org.eclipse.swt.widgets.Display;
68 import org.eclipse.ui.IWorkbenchPart;
69 import org.eclipse.ui.PlatformUI;
70 import org.eclipse.ui.part.Page;
71 import org.eclipse.ui.views.properties.IPropertySheetPage;
72 import org.openjdk.jmc.common.IDescribable;
73 import org.openjdk.jmc.common.IDisplayable;
74 import org.openjdk.jmc.common.IState;
75 import org.openjdk.jmc.common.collection.IteratorToolkit;
76 import org.openjdk.jmc.common.item.Aggregators;
77 import org.openjdk.jmc.common.item.IAttribute;
78 import org.openjdk.jmc.common.item.IItem;
79 import org.openjdk.jmc.common.item.IItemCollection;
80 import org.openjdk.jmc.common.item.IItemFilter;
81 import org.openjdk.jmc.common.item.IItemIterable;
82 import org.openjdk.jmc.common.item.IMemberAccessor;
83 import org.openjdk.jmc.common.item.IType;
84 import org.openjdk.jmc.common.unit.ContentType;
85 import org.openjdk.jmc.common.unit.IQuantity;
86 import org.openjdk.jmc.common.unit.IRange;
87 import org.openjdk.jmc.common.unit.KindOfQuantity;
88 import org.openjdk.jmc.common.unit.QuantitiesToolkit;
89 import org.openjdk.jmc.common.unit.QuantityRange;
90 import org.openjdk.jmc.common.unit.RangeContentType;
91 import org.openjdk.jmc.common.unit.UnitLookup;
92 import org.openjdk.jmc.common.util.TypeHandling;
93 import org.openjdk.jmc.flightrecorder.JfrAttributes;
94 import org.openjdk.jmc.flightrecorder.ui.common.DataPageToolkit;
95 import org.openjdk.jmc.flightrecorder.ui.common.ImageConstants;
96 import org.openjdk.jmc.flightrecorder.ui.messages.internal.Messages;
97 import org.openjdk.jmc.flightrecorder.ui.preferences.PreferenceKeys;
98 import org.openjdk.jmc.flightrecorder.ui.selection.FlavoredSelectionBase;
99 import org.openjdk.jmc.flightrecorder.ui.selection.IFilterFlavor;
100 import org.openjdk.jmc.flightrecorder.ui.selection.IFlavoredSelection;
101 import org.openjdk.jmc.flightrecorder.ui.selection.IItemStreamFlavor;
102 import org.openjdk.jmc.flightrecorder.ui.selection.IPropertyFlavor;
103 import org.openjdk.jmc.flightrecorder.ui.selection.ItemBackedSelection;
104 import org.openjdk.jmc.ui.TypeAppearance;
105 import org.openjdk.jmc.ui.UIPlugin;
106 import org.openjdk.jmc.ui.accessibility.FocusTracker;
107 import org.openjdk.jmc.ui.column.ColumnBuilder;
108 import org.openjdk.jmc.ui.column.ColumnManager;
109 import org.openjdk.jmc.ui.column.ColumnMenusFactory;
110 import org.openjdk.jmc.ui.column.IColumn;
111 import org.openjdk.jmc.ui.column.TableSettings;
112 import org.openjdk.jmc.ui.column.TableSettings.ColumnSettings;
113 import org.openjdk.jmc.ui.common.util.AdapterUtil;
114 import org.openjdk.jmc.ui.handlers.ActionToolkit;
115 import org.openjdk.jmc.ui.handlers.MCContextMenuManager;
116 import org.openjdk.jmc.ui.misc.DisplayToolkit;
117 import org.openjdk.jmc.ui.misc.TypedLabelProvider;
118
119 // FIXME: fields - units - filters - icons etc. should be handled more properly
120 public class JfrPropertySheet extends Page implements IPropertySheetPage {
121
122 private static final ImageDescriptor NEW_PAGE_ICON = FlightRecorderUI.getDefault()
123 .getMCImageDescriptor(ImageConstants.ICON_NEW_PAGE);
124 private static final String HELP_CONTEXT_ID = FlightRecorderUI.PLUGIN_ID + ".JfrPropertiesView"; //$NON-NLS-1$
125 private static final Object TOO_MANY_VALUES = new Object();
126 private static final PropertySheetRow CALCULATING = new PropertySheetRow(null, null);
127 private static Set<IType<?>> selectedTypes = Collections.emptySet();
128 private static class PropertySheetRowSelection extends FlavoredSelectionBase {
129
130 private final PropertySheetRow row;
131
132 PropertySheetRowSelection(PropertySheetRow row) {
133 super(MessageFormat.format(Messages.JFR_PROPERTIES_PROPERTY_SELECTION, row.attribute.getName()));
134 this.row = row;
135 }
136
137 @Override
138 public Stream<IItemStreamFlavor> getFlavors(
139 IItemFilter dstFilter, IItemCollection items, List<IAttribute<?>> dstAttributes) {
140 /*
141 * FIXME: Is this the desired behavior? Discuss and change if necessary.
142 *
143 * This most likely need more thought and discussion, but the implemented order of
144 * flavors is currently:
145 *
146 * For chart selections:
147 *
287 @Override
288 protected Image getImageTyped(PropertySheetRow p) {
289 if (p.attribute != null) {
290 Image icon = TypeAppearance.getImage(p.attribute.getContentType().getIdentifier());
291 return icon == null ? UIPlugin.getDefault().getImage(UIPlugin.ICON_PROPERTY_OBJECT) : icon;
292 }
293 return null;
294 };
295 }).build();
296
297 private static final IColumn VALUE_COLUMN = new ColumnBuilder(Messages.JFR_PROPERTY_SHEET_VALUE, "value", //$NON-NLS-1$
298 new TypedLabelProvider<PropertySheetRow>(PropertySheetRow.class) {
299 @Override
300 protected String getTextTyped(PropertySheetRow p) {
301 Object value = p.getValue();
302 if (p == CALCULATING) {
303 return Messages.JFR_PROPERTIES_CALCULATING;
304 } else if (value == TOO_MANY_VALUES) {
305 return Messages.JFR_PROPERTIES_TOO_MANY_VALUES;
306 }
307 updateSelectedTypes(value);
308 return getValueString(value);
309 };
310
311 // FIXME: Merge with TypeHandling.getValueString
312 @SuppressWarnings("unchecked")
313 private String getValueString(Object value) {
314 if (value instanceof IItemCollection) {
315 return itemCollectionDescription((IItemCollection) value);
316 } else if (value instanceof IDescribable) {
317 return ((IDescribable) value).getName();
318 } else if (value instanceof IDescribable[] && ((IDescribable[]) value).length > 0) {
319 IDescribable[] values = ((IDescribable[]) value);
320 return "[" + values[0].getName() + " ... " //$NON-NLS-1$ //$NON-NLS-2$
321 + values[values.length - 1].getName() + "]"; //$NON-NLS-1$
322 } else if (value instanceof Object[]) {
323
324 return limitedDeepToString((Object[]) value, this::getValueString);
325 } else if (value instanceof Collection<?>) {
326 System.out.println(value);
327 selectedTypes = new HashSet<IType<?>>((Collection<IType<?>>) value)
328 .stream().collect(Collectors.toSet());
329
330 return limitedDeepToString(((Collection<?>) value).toArray(), this::getValueString);
331 }
332 return TypeHandling.getValueString(value);
333 }
334
335 @Override
336 protected String getToolTipTextTyped(PropertySheetRow p) {
337 Object value = p.getValue();
338 if (value instanceof IQuantity) {
339 return TypeHandling.getNumericString(((IQuantity) value).numberValue());
340 }
341 return JfrPropertySheet.getVerboseString(value);
342 };
343
344 }).build();
345
346 private static void updateSelectedTypes(Object value) {
347 if (value instanceof IType<?>) {
348 selectedTypes = new HashSet<IType<?>>();
349 selectedTypes.add((IType<?>) value);
350 }
351 else if (value instanceof Collection<?>) {
352 selectedTypes = new HashSet<IType<?>>((Collection<IType<?>>) value)
353 .stream().collect(Collectors.toSet());
354 }
355
356 }
357
358 private static String limitedDeepToString(Object[] array, Function<Object, String> valueToStringProvider) {
359 return limitedDeepToString(array, new StringBuilder(), true, valueToStringProvider);
360 }
361
362 private static String limitedDeepToString(
363 Object[] array, StringBuilder builder, boolean isRootArray, Function<Object, String> valueToStringProvider) {
364 int maxCharacters = FlightRecorderUI.getDefault().getPreferenceStore()
365 .getInt(PreferenceKeys.PROPERTY_MAXIMUM_PROPERTIES_ARRAY_STRING_SIZE);
366 int omitted = 0;
367 builder.append('[');
368 for (int i = 0; i < array.length; i++) {
369 Object element = array[i];
370 if (element != null && element.getClass().isArray()) {
371 limitedDeepToString((Object[]) element, builder, false, valueToStringProvider);
372 } else {
373 builder.append(valueToStringProvider.apply(element));
374 }
375 if ((i < (array.length - 1)) && builder.length() < maxCharacters) {
376 builder.append(',');
377 builder.append(' ');
450 viewer = new TableViewer(parent, SWT.MULTI | SWT.FULL_SELECTION);
451 viewer.setContentProvider(ArrayContentProvider.getInstance());
452 // FIXME: Should we keep a state for the properties view?
453 ColumnManager manager = ColumnManager.build(viewer,
454 Arrays.asList(FIELD_COLUMN, VALUE_COLUMN, VERBOSE_VALUE_COLUMN), getTableSettings(null));
455 MCContextMenuManager mm = MCContextMenuManager.create(viewer.getControl());
456 ColumnMenusFactory.addDefaultMenus(manager, mm);
457 Function<Consumer<IFlavoredSelection>, Function<List<PropertySheetRow>, Runnable>> actionProvider = flavorConsumer -> selected -> {
458 if (selected.size() == 1 && selected.get(0).value != TOO_MANY_VALUES) {
459 if (selected.get(0).attribute != null) {
460 return () -> flavorConsumer.accept(new PropertySheetRowSelection(selected.get(0)));
461 } else if (selected.get(0).value instanceof IItemCollection) {
462 IItemCollection items = (IItemCollection) selected.get(0).value;
463 String selectionName = itemCollectionDescription(items);
464 return () -> flavorConsumer.accept(new ItemBackedSelection(items, selectionName));
465 }
466 }
467 return null;
468 };
469 // FIXME: Break out to other place where these actions are added to menus
470 IAction addPageAction = ActionToolkit.action(() -> DataPageToolkit.addPage(selectedTypes),
471 Messages.EventBrowserPage_NEW_PAGE_USING_TYPES_ACTION, NEW_PAGE_ICON);
472 mm.appendToGroup(MCContextMenuManager.GROUP_NEW, addPageAction);
473 mm.appendToGroup(MCContextMenuManager.GROUP_EDIT,
474 ActionToolkit.forListSelection(viewer, Messages.STORE_SELECTION_ACTION, false,
475 actionProvider.apply(controller.getSelectionStore()::addSelection)));
476 mm.appendToGroup(MCContextMenuManager.GROUP_EDIT,
477 ActionToolkit.forListSelection(viewer, Messages.STORE_AND_ACTIVATE_SELECTION_ACTION, false,
478 actionProvider.apply(controller.getSelectionStore()::addAndSetAsCurrentSelection)));
479 ColumnViewerToolTipSupport.enableFor(viewer);
480 PlatformUI.getWorkbench().getHelpSystem().setHelp(viewer.getControl(), HELP_CONTEXT_ID);
481
482 if (UIPlugin.getDefault().getAccessibilityMode()) {
483 FocusTracker.enableFocusTracking(viewer.getTable());
484 }
485 }
486
487 private static TableSettings getTableSettings(IState state) {
488 if (state == null) {
489 return new TableSettings(null,
490 Arrays.asList(new ColumnSettings(FIELD_COLUMN.getId(), false, 120, null),
491 new ColumnSettings(VALUE_COLUMN.getId(), false, 120, null),
492 new ColumnSettings(VERBOSE_VALUE_COLUMN.getId(), true, 400, null)));
|