Skip to content

Package: EnumComboViewerSWTRenderer$1

EnumComboViewerSWTRenderer$1

nameinstructionbranchcomplexitylinemethod
getText(Object)
M: 0 C: 15
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
{...}
M: 0 C: 6
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) 2011-2019 EclipseSource Muenchen GmbH 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: * Eugen - initial API and implementation
13: * Christian Damus - enum choice filtering based on ItemPropertyDescriptor
14: * Lucas Koehler - enum choice filtering based on ItemPropertyDescriptor
15: * Christian W. Damus - bug 547422
16: ******************************************************************************/
17: package org.eclipse.emf.ecp.view.internal.core.swt.renderer;
18:
19: import java.util.ArrayList;
20: import java.util.Collection;
21: import java.util.List;
22: import java.util.Optional;
23: import java.util.stream.Collectors;
24:
25: import javax.inject.Inject;
26:
27: import org.eclipse.core.databinding.Binding;
28: import org.eclipse.core.databinding.observable.sideeffect.ISideEffect;
29: import org.eclipse.core.databinding.observable.value.ComputedValue;
30: import org.eclipse.core.databinding.observable.value.IObservableValue;
31: import org.eclipse.core.databinding.property.value.IValueProperty;
32: import org.eclipse.emf.common.util.EList;
33: import org.eclipse.emf.common.util.Enumerator;
34: import org.eclipse.emf.databinding.IEMFObservable;
35: import org.eclipse.emf.ecore.EEnum;
36: import org.eclipse.emf.ecore.EEnumLiteral;
37: import org.eclipse.emf.ecore.EObject;
38: import org.eclipse.emf.ecore.EStructuralFeature;
39: import org.eclipse.emf.ecore.util.EcoreUtil;
40: import org.eclipse.emf.ecp.common.spi.EMFUtils;
41: import org.eclipse.emf.ecp.view.internal.core.swt.MessageKeys;
42: import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
43: import org.eclipse.emf.ecp.view.spi.core.swt.SimpleControlJFaceViewerSWTRenderer;
44: import org.eclipse.emf.ecp.view.spi.model.VControl;
45: import org.eclipse.emf.ecp.view.spi.model.VViewPackage;
46: import org.eclipse.emf.ecp.view.template.model.VTViewTemplateProvider;
47: import org.eclipse.emf.edit.provider.IChangeNotifier;
48: import org.eclipse.emf.edit.provider.IItemPropertyDescriptor;
49: import org.eclipse.emf.edit.provider.IItemPropertySource;
50: import org.eclipse.emf.edit.provider.INotifyChangedListener;
51: import org.eclipse.emfforms.spi.common.report.ReportService;
52: import org.eclipse.emfforms.spi.core.services.databinding.DatabindingFailedException;
53: import org.eclipse.emfforms.spi.core.services.databinding.EMFFormsDatabinding;
54: import org.eclipse.emfforms.spi.core.services.editsupport.EMFFormsEditSupport;
55: import org.eclipse.emfforms.spi.core.services.label.EMFFormsLabelProvider;
56: import org.eclipse.emfforms.spi.localization.LocalizationServiceHelper;
57: import org.eclipse.jface.databinding.swt.typed.WidgetProperties;
58: import org.eclipse.jface.databinding.viewers.IViewerValueProperty;
59: import org.eclipse.jface.databinding.viewers.typed.ViewerProperties;
60: import org.eclipse.jface.viewers.ArrayContentProvider;
61: import org.eclipse.jface.viewers.ComboViewer;
62: import org.eclipse.jface.viewers.LabelProvider;
63: import org.eclipse.jface.viewers.Viewer;
64: import org.eclipse.swt.widgets.Composite;
65:
66: /**
67: * Renderer for Enums.
68: *
69: * @author Eugen Neufeld
70: *
71: */
72: public class EnumComboViewerSWTRenderer extends SimpleControlJFaceViewerSWTRenderer {
73:
74:         private final EMFFormsEditSupport emfFormsEditSupport;
75:         private IObservableValue<Collection<?>> availableChoicesValue;
76:         private ISideEffect pushValue;
77:
78:         /**
79:          * Default constructor.
80:          *
81:          * @param vElement the view model element to be rendered
82:          * @param viewContext the view context
83:          * @param reportService The {@link ReportService}
84:          * @param emfFormsDatabinding The {@link EMFFormsDatabinding}
85:          * @param emfFormsLabelProvider The {@link EMFFormsLabelProvider}
86:          * @param vtViewTemplateProvider The {@link VTViewTemplateProvider}
87:          * @param emfFormsEditSupport The {@link EMFFormsEditSupport}
88:          */
89:         @Inject
90:         public EnumComboViewerSWTRenderer(VControl vElement, ViewModelContext viewContext,
91:                 ReportService reportService,
92:                 EMFFormsDatabinding emfFormsDatabinding, EMFFormsLabelProvider emfFormsLabelProvider,
93:                 VTViewTemplateProvider vtViewTemplateProvider, EMFFormsEditSupport emfFormsEditSupport) {
94:                 super(vElement, viewContext, reportService, emfFormsDatabinding, emfFormsLabelProvider, vtViewTemplateProvider);
95:                 this.emfFormsEditSupport = emfFormsEditSupport;
96:         }
97:
98:         @SuppressWarnings("unchecked")
99:         @Override
100:         protected Binding[] createBindings(Viewer viewer) throws DatabindingFailedException {
101:                 // This binding needs to fire before the value binding so that the value
102:                 // to be selected exists in the combo's items
103:                 final IViewerValueProperty<ComboViewer, Object> viewerValueProperty = ViewerProperties.input();
104:                 final IObservableValue<?> viewerInput = viewerValueProperty.observe(viewer);
105:                 final Binding inputBinding = getDataBindingContext().bindValue(
106:                         viewerInput,
107:                         getAvailableChoicesValue());
108:
109:                 final IObservableValue<?> modelValue = getModelValue();
110:
111:                 final Binding binding = getDataBindingContext().bindValue(ViewerProperties.singleSelection().observe(viewer),
112:                         modelValue);
113:
114:                 pushValue = ISideEffect.create(viewerInput::getValue, input -> {
115:                         if (input != null) {
116:                                 binding.updateModelToTarget();
117:                         }
118:                 });
119:
120:                 final Binding tooltipBinding = getDataBindingContext().bindValue(
121:                         WidgetProperties.tooltipText().observe(viewer.getControl()),
122:                         getModelValue());
123:                 return new Binding[] { inputBinding, binding, tooltipBinding };
124:         }
125:
126:         /**
127:          * Create a new {@link ComboViewer} instance. Overwrite this method in case you need a custom CCombo instance.
128:          *
129:          * @param parent the parent container
130:          * @param eEnum the enum being rendered
131:          * @return a {@link ComboViewer}
132:          */
133:         protected ComboViewer createComboViewer(Composite parent, EEnum eEnum) {
134:                 return new ComboViewer(parent);
135:         }
136:
137:         @SuppressWarnings("rawtypes")
138:         @Override
139:         protected Viewer createJFaceViewer(Composite parent) throws DatabindingFailedException {
140:                 final IValueProperty valueProperty = getEMFFormsDatabinding()
141:                         .getValueProperty(getVElement().getDomainModelReference(), getViewModelContext().getDomainModel());
142:                 final EStructuralFeature structuralFeature = (EStructuralFeature) valueProperty.getValueType();
143:                 final EEnum eEnum = EEnum.class.cast(structuralFeature.getEType());
144:
145:                 final ComboViewer combo = createComboViewer(parent, eEnum);
146:                 combo.setContentProvider(new ArrayContentProvider());
147:                 combo.setLabelProvider(new LabelProvider() {
148:
149:                         @Override
150:                         public String getText(Object element) {
151:                                 return getEMFFormsEditSupport()
152:                                         .getText(getVElement().getDomainModelReference(), getViewModelContext().getDomainModel(), element);
153:                         }
154:
155:                 });
156:                 combo.setData(CUSTOM_VARIANT, "org_eclipse_emf_ecp_control_enum"); //$NON-NLS-1$
157:
158:                 return combo;
159:         }
160:
161:         /**
162:          * Returns the list of literals of the enum.
163:          *
164:          * @param eEnum the enum to get the literals for
165:          * @return a list of literals
166:          *
167:          */
168:         public List<EEnumLiteral> getELiterals(EEnum eEnum) {
169:                 final List<EEnumLiteral> filtered = new ArrayList<EEnumLiteral>();
170:                 final EList<EEnumLiteral> eLiterals = eEnum.getELiterals();
171:                 for (final EEnumLiteral literal : eLiterals) {
172:
173:                         final String isInputtable = EcoreUtil.getAnnotation(literal,
174:                                 VViewPackage.NS_URI_170,
175:                                 "isInputtable"); //$NON-NLS-1$
176:
177:                         if (isInputtable == null || Boolean.getBoolean(isInputtable)) {
178:                                 filtered.add(literal);
179:                         }
180:                 }
181:                 return filtered;
182:         }
183:
184:         /**
185:          * Obtains the combo viewer input as an observable value.
186:          * This is an observable value, not an observable collection, because
187:          * <ul>
188:          * <li>it is not to be treated as a mutable collection, and</li>
189:          * <li>it is used as a viewer input, which is an opaque object</li>
190:          * </ul>
191:          *
192:          * @return the available-choices value
193:          *
194:          * @throws DatabindingFailedException on failure to get the {@linkplain #getModelValue() model value}
195:          */
196:         protected IObservableValue<Collection<?>> getAvailableChoicesValue() throws DatabindingFailedException {
197:                 if (availableChoicesValue == null) {
198:                         // It makes no sense to use this renderer with a different kind of property than this
199:                         final IEMFObservable emfObservable = (IEMFObservable) getModelValue();
200:
201:                         // the domain object must be the same as the object of the feature
202:                         final EObject domainObject = (EObject) emfObservable.getObserved();
203:
204:                         final EStructuralFeature feature = emfObservable.getStructuralFeature();
205:
206:                         final Optional<IItemPropertySource> propertySource = EMFUtils.adapt(domainObject,
207:                                 IItemPropertySource.class);
208:                         final Optional<IItemPropertyDescriptor> propertyDescriptor = propertySource
209:                                 .map(source -> source.getPropertyDescriptor(domainObject, feature));
210:
211:                         availableChoicesValue = new ComputedValue<Collection<?>>(Collection.class) {
212:                                 // maybe better subscribe to all changes in the current context and update?
213:                                 private final Optional<IChangeNotifier> changeNotifier = propertySource
214:                                         .filter(IChangeNotifier.class::isInstance).map(IChangeNotifier.class::cast);
215:                                 private final INotifyChangedListener listener = __ -> getRealm().exec(this::makeDirty);
216:                                 {
217:                                         changeNotifier.ifPresent(cn -> cn.addListener(listener));
218:                                 }
219:
220:                                 @Override
221:                                 public synchronized void dispose() {
222:                                         changeNotifier.ifPresent(cn -> cn.removeListener(listener));
223:                                         super.dispose();
224:                                 }
225:
226:                                 @Override
227:                                 protected Collection<?> calculate() {
228:                                         final List<EEnumLiteral> allLiterals = getELiterals((EEnum) feature.getEType());
229:                                         // We have two filter mechanisms: a) a custom annotation and b) filters defined in the property
230:                                         // descriptor. The latter is only used if a property descriptor is available. In this case, we use
231:                                         // the intersection of both enumerator sets
232:                                         final List<Enumerator> filteredByAnnotation = allLiterals.stream()
233:                                                 .map(EEnumLiteral::getInstance)
234:                                                 .collect(Collectors.toList());
235:                                         if (propertyDescriptor.isPresent()) {
236:                                                 filteredByAnnotation.retainAll(propertyDescriptor.get().getChoiceOfValues(domainObject));
237:                                         }
238:                                         return filteredByAnnotation;
239:                                 }
240:                         };
241:                 }
242:
243:                 return availableChoicesValue;
244:         }
245:
246:         @Override
247:         protected void rootDomainModelChanged() throws DatabindingFailedException {
248:                 if (availableChoicesValue != null) {
249:                         availableChoicesValue.dispose();
250:                 }
251:                 availableChoicesValue = null;
252:                 super.rootDomainModelChanged();
253:         }
254:
255:         @Override
256:         protected void dispose() {
257:                 super.dispose();
258:                 if (pushValue != null) {
259:                         pushValue.dispose();
260:                 }
261:         }
262:
263:         /**
264:          * {@inheritDoc}
265:          *
266:          * @see org.eclipse.emf.ecp.view.spi.core.swt.SimpleControlSWTRenderer#getUnsetText()
267:          */
268:         @Override
269:         protected String getUnsetText() {
270:                 return LocalizationServiceHelper
271:                         .getString(getClass(), MessageKeys.EEnumControl_NoValueSetClickToSetValue);
272:         }
273:
274:         /**
275:          * Return the {@link EMFFormsEditSupport}.
276:          *
277:          * @return the {@link EMFFormsEditSupport}
278:          */
279:         protected EMFFormsEditSupport getEMFFormsEditSupport() {
280:                 return emfFormsEditSupport;
281:         }
282:
283: }