Skip to content

Package: TableSelectionCompositeImpl$1

TableSelectionCompositeImpl$1

nameinstructionbranchcomplexitylinemethod
getImage(Object)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getText(Object)
M: 0 C: 5
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
{...}
M: 0 C: 9
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%

Coverage

1: /*******************************************************************************
2: * Copyright (c) 2019 Christian W. Damus and others.
3: *
4: * All rights reserved. This program and the accompanying materials
5: * are made available under the terms of the Eclipse Public License 2.0
6: * which accompanies this distribution, and is available at
7: * https://www.eclipse.org/legal/epl-2.0/
8: *
9: * SPDX-License-Identifier: EPL-2.0
10: *
11: * Contributors:
12: * Christian W. Damus - initial API and implementation
13: ******************************************************************************/
14: package org.eclipse.emfforms.swt.internal.reference.table;
15:
16: import java.util.Collection;
17: import java.util.Hashtable;
18: import java.util.List;
19:
20: import org.eclipse.core.databinding.observable.Observables;
21: import org.eclipse.core.databinding.observable.Realm;
22: import org.eclipse.core.databinding.observable.value.ComputedValue;
23: import org.eclipse.core.databinding.observable.value.IObservableValue;
24: import org.eclipse.emf.common.util.EList;
25: import org.eclipse.emf.ecore.EClass;
26: import org.eclipse.emf.ecore.EObject;
27: import org.eclipse.emf.ecore.EPackage;
28: import org.eclipse.emf.ecore.EReference;
29: import org.eclipse.emf.ecore.EStructuralFeature;
30: import org.eclipse.emf.ecp.spi.common.ui.composites.SelectModelElementCompositeImpl;
31: import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
32: import org.eclipse.emf.ecp.view.spi.context.ViewModelContextFactory;
33: import org.eclipse.emf.ecp.view.spi.model.VDomainModelReference;
34: import org.eclipse.emf.ecp.view.spi.model.VFeatureDomainModelReferenceSegment;
35: import org.eclipse.emf.ecp.view.spi.model.VFeaturePathDomainModelReference;
36: import org.eclipse.emf.ecp.view.spi.model.VView;
37: import org.eclipse.emf.ecp.view.spi.model.VViewFactory;
38: import org.eclipse.emf.ecp.view.spi.renderer.NoPropertyDescriptorFoundExeption;
39: import org.eclipse.emf.ecp.view.spi.renderer.NoRendererFoundException;
40: import org.eclipse.emf.ecp.view.spi.swt.reporting.InvalidGridDescriptionReport;
41: import org.eclipse.emf.ecp.view.spi.swt.reporting.RenderingFailedReport;
42: import org.eclipse.emf.ecp.view.spi.table.model.VTableControl;
43: import org.eclipse.emf.ecp.view.spi.table.model.VTableDomainModelReference;
44: import org.eclipse.emf.ecp.view.spi.table.swt.TableControlSWTRenderer;
45: import org.eclipse.emf.ecp.view.spi.util.swt.ImageRegistryService;
46: import org.eclipse.emf.ecp.view.template.model.VTViewTemplateProvider;
47: import org.eclipse.emfforms.common.Optional;
48: import org.eclipse.emfforms.spi.common.report.ReportService;
49: import org.eclipse.emfforms.spi.core.services.databinding.emf.DomainModelReferenceConverterEMF;
50: import org.eclipse.emfforms.spi.core.services.databinding.emf.DomainModelReferenceSegmentConverterEMF;
51: import org.eclipse.emfforms.spi.core.services.databinding.emf.EMFFormsDatabindingEMF;
52: import org.eclipse.emfforms.spi.core.services.domainexpander.EMFFormsDMRSegmentExpander;
53: import org.eclipse.emfforms.spi.core.services.editsupport.EMFFormsEditSupport;
54: import org.eclipse.emfforms.spi.core.services.label.EMFFormsLabelProvider;
55: import org.eclipse.emfforms.spi.core.services.label.NoLabelFoundException;
56: import org.eclipse.emfforms.spi.localization.EMFFormsLocalizationService;
57: import org.eclipse.emfforms.spi.swt.core.layout.GridDescriptionFactory;
58: import org.eclipse.emfforms.spi.swt.core.layout.SWTGridDescription;
59: import org.eclipse.emfforms.spi.swt.table.ColumnConfigurationBuilder;
60: import org.eclipse.emfforms.spi.swt.table.TableViewerCompositeBuilder;
61: import org.eclipse.emfforms.spi.swt.table.TableViewerSWTBuilder;
62: import org.eclipse.emfforms.view.spi.multisegment.model.MultiSegmentUtil;
63: import org.eclipse.emfforms.view.spi.multisegment.model.VMultiDomainModelReferenceSegment;
64: import org.eclipse.emfforms.view.spi.multisegment.model.VMultisegmentFactory;
65: import org.eclipse.jface.layout.GridDataFactory;
66: import org.eclipse.jface.viewers.CellLabelProvider;
67: import org.eclipse.jface.viewers.ColumnLabelProvider;
68: import org.eclipse.jface.viewers.ILabelProvider;
69: import org.eclipse.jface.viewers.TableViewer;
70: import org.eclipse.osgi.util.NLS;
71: import org.eclipse.swt.SWT;
72: import org.eclipse.swt.graphics.Color;
73: import org.eclipse.swt.graphics.Image;
74: import org.eclipse.swt.widgets.Composite;
75: import org.eclipse.swt.widgets.Control;
76: import org.eclipse.swt.widgets.Label;
77: import org.osgi.framework.BundleContext;
78: import org.osgi.framework.Constants;
79: import org.osgi.framework.FrameworkUtil;
80: import org.osgi.framework.ServiceRegistration;
81:
82: /**
83: * Implementation of a table selection composite that is described by a view model.
84: *
85: * @since 1.27
86: */
87: public class TableSelectionCompositeImpl extends SelectModelElementCompositeImpl {
88:
89:         /** Namespace URI of the dynamic selection extent package. */
90:         private static final String SELECTION_NS_URI = "http://www.eclipse.org/emf/ecp/view/reference/table/selection"; //$NON-NLS-1$
91:
92:         /** The selection extent package. */
93:         private static final EPackage SELECTION_PACKAGE = EPackage.Registry.INSTANCE.getEPackage(SELECTION_NS_URI);
94:
95:         /** The <em>Selection</em> class. */
96:         private static final EClass SELECTION_CLASS = (EClass) SELECTION_PACKAGE.getEClassifier("Selection"); //$NON-NLS-1$
97:
98:         /** The <em>extent</em> reference of the <em>Selection</em> class. */
99:         private static final EStructuralFeature SELECTION_EXTENT = SELECTION_CLASS.getEStructuralFeature("extent"); //$NON-NLS-1$
100:
101:         private final Collection<? extends EObject> extent;
102:         private final VTableControl tableControl;
103:         private final EObject owner;
104:
105:         private ViewModelContext context;
106:         /** Representation of the real DMR which can be used to get labels for the referenced feature. */
107:         private VDomainModelReference realDMR;
108:         private ServiceRegistration<DomainModelReferenceConverterEMF> dmrConverter;
109:         private ServiceRegistration<DomainModelReferenceSegmentConverterEMF> segmentConverter;
110:         private ServiceRegistration<EMFFormsDMRSegmentExpander> segmentExpander;
111:
112:         /**
113:          * Initializes me with the table view model to render.
114:          *
115:          * @param extent the collection of objects from which to make a selection
116:          * @param tableControl the view model description of the table
117:          * @param owner the owner of the {@code reference} being edited
118:          * @param reference the reference being edited
119:          */
120:         public TableSelectionCompositeImpl(Collection<? extends EObject> extent, VTableControl tableControl,
121:                 EObject owner, EReference reference) {
122:
123:                 super(extent, reference.isMany());
124:
125:                 this.extent = extent;
126:                 this.tableControl = tableControl;
127:                 this.owner = owner;
128:         }
129:
130:         @Override
131:         public void dispose() {
132:                 if (context != null) {
133:                         context.dispose();
134:                         context = null;
135:                 }
136:
137:                 if (dmrConverter != null) {
138:                         dmrConverter.unregister();
139:                 }
140:
141:                 if (segmentConverter != null) {
142:                         segmentConverter.unregister();
143:                 }
144:                 if (segmentExpander != null) {
145:                         segmentExpander.unregister();
146:                 }
147:
148:                 super.dispose();
149:         }
150:
151:         @Override
152:         protected TableViewer createViewer(Composite composite) {
153:                 final EObject selectionExtent = createSelectionExtent();
154:
155:                 // Put the table in a view by itself
156:                 final VView tableView = VViewFactory.eINSTANCE.createView();
157:                 tableView.getChildren().add(tableControl);
158:
159:                 // We don't want anything in this view to be editable: it's only for selection
160:                 tableView.setReadonly(true);
161:
162:                 // Set the extent DMR into the table model
163:                 if (tableControl.getDomainModelReference() == null) {
164:                         final VFeaturePathDomainModelReference extentDMR = createLegacyExtentDmr(selectionExtent);
165:                         // So we have no column DMRs. What then is the point?
166:                         realDMR = tableControl.getDomainModelReference();
167:                         tableControl.setDomainModelReference(extentDMR);
168:                         registerDMRConverter(selectionExtent, extentDMR);
169:                 } else if (!tableControl.getDomainModelReference().getSegments().isEmpty()) {
170:                         // Segment based table dmr must have a multi segment
171:                         final VMultiDomainModelReferenceSegment realMultiSeg = MultiSegmentUtil
172:                                 .getMultiSegment(tableControl.getDomainModelReference())
173:                                 .orElseThrow(() -> new IllegalArgumentException(
174:                                         "the table model's dmr is segment based but doesn't have a multi segment.")); //$NON-NLS-1$
175:                         adaptRealSegmentDmr(realMultiSeg);
176:                         // Create extent dmr and register necessary services
177:                         final VDomainModelReference ex = configureSegmentExtentDmr(selectionExtent, realMultiSeg);
178:                         tableControl.setDomainModelReference(ex);
179:                 } else if (tableControl.getDomainModelReference() instanceof VTableDomainModelReference) {
180:                         final VFeaturePathDomainModelReference extentDMR = createLegacyExtentDmr(selectionExtent);
181:                         final VTableDomainModelReference dmr = (VTableDomainModelReference) tableControl.getDomainModelReference();
182:                         realDMR = dmr.getDomainModelReference();
183:                         dmr.setDomainModelReference(extentDMR);
184:                         registerDMRConverter(selectionExtent, extentDMR);
185:                 } else {
186:                         throw new IllegalArgumentException("table model has no table DMR"); //$NON-NLS-1$
187:                 }
188:
189:                 context = ViewModelContextFactory.INSTANCE.createViewModelContext(tableView, owner);
190:
191:                 final PrivateTableRenderer renderer = new PrivateTableRenderer(context, tableControl);
192:
193:                 final Control control = renderer.render(composite);
194:
195:                 GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).minSize(0, 150).span(2, 1)
196:                         .applyTo(control);
197:
198:                 final TableViewer result = renderer.getTableViewer();
199:
200:                 return result;
201:         }
202:
203:         private VFeaturePathDomainModelReference createLegacyExtentDmr(final EObject selectionExtent) {
204:                 final VFeaturePathDomainModelReference extentDMR = VViewFactory.eINSTANCE
205:                         .createFeaturePathDomainModelReference();
206:                 extentDMR.setDomainModelEFeature(selectionExtent.eClass().getEReferences().get(0));
207:                 return extentDMR;
208:         }
209:
210:         /**
211:          * Replaces the original DMRs multi segment with a feature segment to allow databinding for single references. A
212:          * feature segment is sufficient as the dmr only needs to be resolved for labels.
213:          *
214:          * @param realMultiSeg The multi segment of the original table dmr in the view model
215:          */
216:         private void adaptRealSegmentDmr(final VMultiDomainModelReferenceSegment realMultiSeg) {
217:                 realDMR = tableControl.getDomainModelReference();
218:                 // replace multi with feature segment to avoid failed databinding for single references.
219:                 realDMR.getSegments().remove(realMultiSeg);
220:                 final VFeatureDomainModelReferenceSegment realReplace = VViewFactory.eINSTANCE
221:                         .createFeatureDomainModelReferenceSegment();
222:                 realReplace.setDomainModelFeature(realMultiSeg.getDomainModelFeature());
223:                 realDMR.getSegments().add(realReplace);
224:         }
225:
226:         /**
227:          * Create segment based extent dmr whose multi segment contains a copy of all real child dmrs. Also register
228:          * necessary services.
229:          *
230:          * @param selectionExtent The selection extent {@link EObject}
231:          * @param realMultiSegment The multi segment of the original table dmr in the view model
232:          * @return The extent segment DMR
233:          */
234:         private VDomainModelReference configureSegmentExtentDmr(EObject selectionExtent,
235:                 VMultiDomainModelReferenceSegment realMultiSegment) {
236:                 final VDomainModelReference extentDmr = VViewFactory.eINSTANCE.createDomainModelReference();
237:                 final VMultiDomainModelReferenceSegment extentSegment = VMultisegmentFactory.eINSTANCE
238:                         .createMultiDomainModelReferenceSegment();
239:                 final EReference extentRef = selectionExtent.eClass().getEReferences().get(0);
240:                 extentSegment.setDomainModelFeature(extentRef.getName());
241:                 // This moves the child DMRs but we do not need them in the "real" DMR. We need the original ones so they
242:                 // are still found by enablement configurations used to name the columns
243:                 extentSegment.getChildDomainModelReferences().addAll(realMultiSegment.getChildDomainModelReferences());
244:                 extentDmr.getSegments().add(extentSegment);
245:
246:                 // Configure services for the extent segment
247:                 registerSegmentServices(selectionExtent, extentSegment, extentRef);
248:
249:                 return extentDmr;
250:         }
251:
252:         /**
253:          * Create a column label provider that delegates to another label provider.
254:          *
255:          * @param delegate the label provider delegate
256:          * @return the column label provider
257:          */
258:         protected CellLabelProvider createColumnLabelProvider(ILabelProvider delegate) {
259:                 return new ColumnLabelProvider() {
260:                         @Override
261:                         public String getText(Object element) {
262:                                 return delegate.getText(element);
263:                         }
264:
265:                         @Override
266:                         public Image getImage(Object element) {
267:                                 return delegate.getImage(element);
268:                         }
269:                 };
270:         }
271:
272:         private EObject createSelectionExtent() {
273:                 final EObject result = SELECTION_PACKAGE.getEFactoryInstance().create(SELECTION_CLASS);
274:                 @SuppressWarnings("unchecked")
275:                 final EList<EObject> selectionExtent = (EList<EObject>) result.eGet(SELECTION_EXTENT);
276:                 selectionExtent.addAll(extent);
277:
278:                 return result;
279:         }
280:
281:         private void registerDMRConverter(EObject source, VFeaturePathDomainModelReference dmr) {
282:                 final DelegatingDomainModelReferenceConverter delegator = new DelegatingDomainModelReferenceConverter(dmr,
283:                         __ -> source);
284:                 final BundleContext ctx = FrameworkUtil.getBundle(TableSelectionCompositeImpl.class).getBundleContext();
285:
286:                 final Hashtable<String, Object> properties = new Hashtable<>();
287:                 properties.put(Constants.SERVICE_RANKING, Integer.MAX_VALUE);
288:                 dmrConverter = ctx.registerService(DomainModelReferenceConverterEMF.class, delegator, properties);
289:         }
290:
291:         private void registerSegmentServices(EObject source, VFeatureDomainModelReferenceSegment segment,
292:                 EStructuralFeature segmentFeature) {
293:
294:                 final DelegatingDmrSegmentConverter delegator = new DelegatingDmrSegmentConverter(segment, segmentFeature,
295:                         __ -> source);
296:                 final DummyDomainExpander expander = new DummyDomainExpander(segment);
297:                 final BundleContext ctx = FrameworkUtil.getBundle(TableSelectionCompositeImpl.class).getBundleContext();
298:
299:                 final Hashtable<String, Object> properties = new Hashtable<>();
300:                 properties.put(Constants.SERVICE_RANKING, Integer.MAX_VALUE);
301:                 segmentConverter = ctx.registerService(DomainModelReferenceSegmentConverterEMF.class, delegator, properties);
302:                 segmentExpander = ctx.registerService(EMFFormsDMRSegmentExpander.class, expander, properties);
303:         }
304:
305:         //
306:         // Nested types
307:         //
308:
309:         /**
310:          * Private subclass of the table renderer to access some protected API and customize
311:          * its presentation for our use case.
312:          */
313:         private final class PrivateTableRenderer extends TableControlSWTRenderer {
314:
315:                 private final EMFFormsLocalizationService l10n;
316:                 private final EMFFormsLabelProvider labels;
317:
318:                 /**
319:                  * Initializes me.
320:                  */
321:                 PrivateTableRenderer(ViewModelContext context, VTableControl vTable) {
322:                         super(vTable, context, context.getService(ReportService.class),
323:                                 context.getService(EMFFormsDatabindingEMF.class),
324:                                 context.getService(EMFFormsLabelProvider.class),
325:                                 context.getService(VTViewTemplateProvider.class),
326:                                 context.getService(ImageRegistryService.class),
327:                                 context.getService(EMFFormsEditSupport.class),
328:                                 context.getService(EMFFormsLocalizationService.class));
329:
330:                         l10n = context.getService(EMFFormsLocalizationService.class);
331:                         labels = context.getService(EMFFormsLabelProvider.class);
332:                 }
333:
334:                 @Override
335:                 protected TableViewer getTableViewer() {
336:                         return (TableViewer) super.getTableViewer();
337:                 }
338:
339:                 @Override
340:                 protected TableViewerCompositeBuilder createTableViewerCompositeBuilder() {
341:                         return new MinimalCompositeBuilder();
342:                 }
343:
344:                 @Override
345:                 protected int addAdditionalColumns(TableViewerSWTBuilder tableViewerSWTBuilder) {
346:                         // Inject the selection column
347:                         final ColumnConfigurationBuilder columnBuilder = ColumnConfigurationBuilder.usingDefaults()
348:                                 .labelProvider(createColumnLabelProvider(getLabelProvider()))
349:                                 .minWidth(50).weight(100).resizable(true)
350:                                 .text(getSelectionColumnTitle())
351:                                 .tooltip(getSelectionColumnTooltip());
352:                         tableViewerSWTBuilder.addColumn(columnBuilder.build());
353:
354:                         return 1;
355:                 }
356:
357:                 private IObservableValue<String> getSelectionColumnTitle() {
358:                         final String result = l10n.getString(TableSelectionCompositeImpl.class, "selectionColumn"); //$NON-NLS-1$
359:                         return Observables.constantObservableValue(Realm.getDefault(), result, String.class);
360:                 }
361:
362:                 private IObservableValue<String> getSelectionColumnTooltip() {
363:                         final IObservableValue<String> featureName = getRealDMRDisplayName();
364:                         return ComputedValue
365:                                 .create(() -> NLS.bind(l10n.getString(TableSelectionCompositeImpl.class, "selectionTooltip"), //$NON-NLS-1$
366:                                         featureName.getValue()));
367:                 }
368:
369:                 private IObservableValue<String> getRealDMRDisplayName() {
370:                         IObservableValue<String> result;
371:                         try {
372:                                 result = labels.getDisplayName(realDMR, owner);
373:                         } catch (final NoLabelFoundException e) {
374:                                 result = Observables.constantObservableValue(Realm.getDefault(),
375:                                         l10n.getString(TableSelectionCompositeImpl.class, "reference"), //$NON-NLS-1$
376:                                         String.class);
377:                         }
378:                         return result;
379:                 }
380:
381:                 Control render(Composite parent) {
382:                         Control result = null;
383:
384:                         init();
385:
386:                         final SWTGridDescription grid = getGridDescription(
387:                                 GridDescriptionFactory.INSTANCE.createEmptyGridDescription());
388:                         if (grid.getGrid().isEmpty()) {
389:                                 getReportService().report(
390:                                         new InvalidGridDescriptionReport("grid has no cells")); //$NON-NLS-1$
391:                         }
392:
393:                         try {
394:                                 result = renderTableControl(grid.getGrid().get(0), parent);
395:                                 finalizeRendering(parent);
396:                         } catch (final NoRendererFoundException | NoPropertyDescriptorFoundExeption e) {
397:                                 getReportService().report(new RenderingFailedReport(e));
398:                         }
399:
400:                         return result;
401:                 }
402:
403:                 //
404:                 // Nested types
405:                 //
406:
407:                 /**
408:                  * A composite builder that leaves the top composite empty.
409:                  */
410:                 private final class MinimalCompositeBuilder extends TableControlSWTRendererCompositeBuilder {
411:
412:                         @Override
413:                         protected Label createTitleLabel(Composite parentComposite, Color background) {
414:                                 return null;
415:                         }
416:
417:                         @Override
418:                         protected Label createValidationLabel(Composite topComposite) {
419:                                 return null;
420:                         }
421:
422:                         @Override
423:                         protected Composite createButtonComposite(Composite parentComposite) {
424:                                 return null;
425:                         }
426:
427:                         @Override
428:                         public Optional<Label> getTitleLabel() {
429:                                 return Optional.empty();
430:                         }
431:
432:                         @Override
433:                         public Optional<List<Control>> getValidationControls() {
434:                                 return Optional.empty();
435:                         }
436:
437:                         @Override
438:                         public Optional<Composite> getButtonComposite() {
439:                                 return Optional.empty();
440:                         }
441:
442:                 }
443:
444:         }
445:
446: }