Skip to content

Package: TemplateCreateNewModelElementStrategyProvider$Strategy

TemplateCreateNewModelElementStrategyProvider$Strategy

nameinstructionbranchcomplexitylinemethod
TemplateCreateNewModelElementStrategyProvider.Strategy(TemplateCreateNewModelElementStrategyProvider, EClassSelectionStrategy)
M: 0 C: 9
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 4
100%
M: 0 C: 1
100%
createNewModelElement(EObject, EReference)
M: 2 C: 74
97%
M: 1 C: 5
83%
M: 1 C: 3
75%
M: 1 C: 20
95%
M: 0 C: 1
100%
showSelectModelInstancesDialog(Set, Set)
M: 0 C: 28
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 7
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: * lucas - initial API and implementation
13: * Christian W. Damus - bugs 529138, 543461, 546974
14: ******************************************************************************/
15: package org.eclipse.emfforms.internal.core.services.datatemplate;
16:
17: import static org.osgi.service.component.annotations.ReferenceCardinality.MULTIPLE;
18: import static org.osgi.service.component.annotations.ReferencePolicy.DYNAMIC;
19:
20: import java.util.LinkedHashSet;
21: import java.util.Objects;
22: import java.util.Set;
23: import java.util.function.Function;
24: import java.util.function.Predicate;
25: import java.util.stream.Collectors;
26:
27: import org.eclipse.emf.ecore.EClass;
28: import org.eclipse.emf.ecore.EObject;
29: import org.eclipse.emf.ecore.EReference;
30: import org.eclipse.emf.ecore.util.EcoreUtil;
31: import org.eclipse.emf.ecp.common.spi.EMFUtils;
32: import org.eclipse.emf.ecp.ui.view.swt.reference.CreateNewModelElementStrategy;
33: import org.eclipse.emf.ecp.ui.view.swt.reference.CreateNewModelElementStrategy.Provider;
34: import org.eclipse.emf.ecp.ui.view.swt.reference.EClassSelectionStrategy;
35: import org.eclipse.emf.ecp.ui.view.swt.reference.ReferenceServiceCustomizationVendor;
36: import org.eclipse.emf.ecp.ui.view.swt.reference.ReferenceStrategyUtil;
37: import org.eclipse.emfforms.bazaar.Bazaar;
38: import org.eclipse.emfforms.bazaar.BazaarContext;
39: import org.eclipse.emfforms.bazaar.Create;
40: import org.eclipse.emfforms.common.Optional;
41: import org.eclipse.emfforms.core.services.datatemplate.TemplateFilterService;
42: import org.eclipse.emfforms.core.services.datatemplate.TemplateProvider;
43: import org.eclipse.emfforms.datatemplate.Template;
44: import org.eclipse.emfforms.spi.bazaar.BazaarUtil;
45: import org.eclipse.emfforms.spi.localization.EMFFormsLocalizationService;
46: import org.eclipse.jface.wizard.WizardDialog;
47: import org.eclipse.swt.widgets.Display;
48: import org.osgi.service.component.ComponentContext;
49: import org.osgi.service.component.annotations.Activate;
50: import org.osgi.service.component.annotations.Component;
51: import org.osgi.service.component.annotations.Deactivate;
52: import org.osgi.service.component.annotations.Reference;
53: import org.osgi.service.component.annotations.ReferenceCardinality;
54:
55: /**
56: * Provides a strategy to the {@link org.eclipse.emf.ecp.ui.view.swt.DefaultReferenceService DefaultReferenceService}
57: * that allows creating new model elements based on a template with pre-defined values.
58: *
59: * @author Lucas Koehler
60: *
61: */
62: // Ranking as it was for the TemplateReferenceService
63: @Component(name = "TemplateCreateNewModelElementStrategyProvider", property = "service.ranking:Integer=10")
64: public class TemplateCreateNewModelElementStrategyProvider
65:         extends ReferenceServiceCustomizationVendor<CreateNewModelElementStrategy> implements Provider {
66:
67:         private final Set<TemplateProvider> templateProviders = new LinkedHashSet<TemplateProvider>();
68:         private EMFFormsLocalizationService localizationService;
69:
70:         private final Bazaar<EClassSelectionStrategy> eclassSelectionStrategyBazaar = BazaarUtil.createBazaar(
71:                 EClassSelectionStrategy.NULL);
72:         private final Bazaar<TemplateFilterService> templateFilterBazaar = BazaarUtil.createBazaar(
73:                 TemplateFilterService.NULL);
74:         private ComponentContext context;
75:
76:         /**
77:          * Activates me.
78:          *
79:          * @param context my component context
80:          */
81:         @Activate
82:         void activate(ComponentContext context) {
83:                 this.context = context;
84:         }
85:
86:         /**
87:          * Deactivates me.
88:          */
89:         @Deactivate
90:         void deactivate() {
91:                 context = null;
92:         }
93:
94:         /**
95:          * Add an {@code EClass} selection strategy provider.
96:          *
97:          * @param provider the provider to add
98:          */
99:         @Reference(cardinality = MULTIPLE, policy = DYNAMIC)
100:         public void addEClassSelectionStrategyProvider(EClassSelectionStrategy.Provider provider) {
101:                 eclassSelectionStrategyBazaar.addVendor(provider);
102:         }
103:
104:         /**
105:          * Remove an {@code EClass} selection strategy provider.
106:          *
107:          * @param provider the provider to remove
108:          */
109:         void removeEClassSelectionStrategyProvider(EClassSelectionStrategy.Provider provider) {
110:                 eclassSelectionStrategyBazaar.removeVendor(provider);
111:         }
112:
113:         /**
114:          * Register a template provider implementation.
115:          *
116:          * @param templateProvider the {@link TemplateProvider} to add
117:          */
118:         @Reference(cardinality = ReferenceCardinality.MULTIPLE)
119:         void registerTemplateProvider(TemplateProvider templateProvider) {
120:                 templateProviders.add(templateProvider);
121:         }
122:
123:         /**
124:          * Unregister a template provider registration.
125:          *
126:          * @param templateProvider the {@link TemplateProvider} to remove
127:          */
128:         void unregisterTemplateProvider(TemplateProvider templateProvider) {
129:                 templateProviders.remove(templateProvider);
130:         }
131:
132:         /**
133:          * Add a template filter service provider.
134:          *
135:          * @param provider the provider to add
136:          *
137:          * @since 1.21
138:          */
139:         @Reference(cardinality = MULTIPLE, policy = DYNAMIC)
140:         public void addFilterServiceProvider(TemplateFilterService.Provider provider) {
141:                 templateFilterBazaar.addVendor(provider);
142:         }
143:
144:         /**
145:          * Remove a template filter service provider.
146:          *
147:          * @param provider the provider to remove
148:          */
149:         void removeFilterServiceProvider(TemplateFilterService.Provider provider) {
150:                 templateFilterBazaar.removeVendor(provider);
151:         }
152:
153:         /**
154:          * Called by the framework to set the {@link EMFFormsLocalizationService}.
155:          *
156:          * @param localizationService The {@link EMFFormsLocalizationService}
157:          */
158:         @Reference
159:         void setLocalizationService(EMFFormsLocalizationService localizationService) {
160:                 this.localizationService = localizationService;
161:         }
162:
163:         @Override
164:         protected boolean handles(EObject owner, EReference reference) {
165:                 for (final TemplateProvider provider : templateProviders) {
166:                         if (provider.canProvideTemplates(owner, reference)) {
167:                                 return true;
168:                         }
169:                 }
170:                 return false;
171:         }
172:
173:         /**
174:          * Collect a list of available templates for the given {@link EReference}.
175:          *
176:          * @param eObject the parent {@link EObject}
177:          * @param eReference the {@link EReference} to find templates for
178:          * @return list of available templates
179:          */
180:         protected Set<Template> collectAvailableTemplates(EObject eObject, EReference eReference) {
181:                 final Set<Template> templates = new LinkedHashSet<Template>();
182:
183:                 final Predicate<? super Template> filter = getFilter(eObject, eReference);
184:
185:                 for (final TemplateProvider provider : templateProviders) {
186:                         if (!provider.canProvideTemplates(eObject, eReference)) {
187:                                 continue;
188:                         }
189:
190:                         Set<Template> providedTemplates;
191:                         if (provider instanceof BlankTemplateProvider) {
192:                                 final EClassSelectionStrategy eClassSelectionStrategy = ReferenceStrategyUtil
193:                                         .createDynamicEClassSelectionStrategy(eclassSelectionStrategyBazaar, context);
194:                                 providedTemplates = ((BlankTemplateProvider) provider).provideTemplates(eObject, eReference,
195:                                         eClassSelectionStrategy);
196:                         } else {
197:                                 providedTemplates = provider.provideTemplates(eObject, eReference);
198:                         }
199:
200:                         if (filter == null) {
201:                                 templates.addAll(providedTemplates);
202:                         } else {
203:                                 providedTemplates.stream().filter(filter).forEach(templates::add);
204:                         }
205:                 }
206:
207:                 return templates;
208:         }
209:
210:         /**
211:          * Creates the {@link CreateNewModelElementStrategy}.
212:          *
213:          * @return The created {@link CreateNewModelElementStrategy}
214:          */
215:         @Create
216:         public CreateNewModelElementStrategy createCreateNewModelElementStrategy() {
217:                 final EClassSelectionStrategy eClassSelectionStrategy = ReferenceStrategyUtil
218:                         .createDynamicEClassSelectionStrategy(eclassSelectionStrategyBazaar, context);
219:                 return new Strategy(eClassSelectionStrategy);
220:         }
221:
222:         /**
223:          * Obtain a filter predicate for the templates provided for the given {@code reference}
224:          * of an {@code owner} object in the editor.
225:          *
226:          * @param owner the {@link EObject} in the model
227:          * @param reference the {@link EReference} in which templates are to be instantiated
228:          *
229:          * @return the filter, or {@code null} if none
230:          *
231:          * @since 1.21
232:          */
233:         protected Predicate<? super Template> getFilter(EObject owner, EReference reference) {
234:                 final BazaarContext bazaarContext = ReferenceStrategyUtil.createBazaarContext(context, owner, reference);
235:                 return templateFilterBazaar.createProducts(bazaarContext).stream()
236:                         .map(service -> service.getTemplateFilter(owner, reference))
237:                         .filter(Objects::nonNull)
238:                         .reduce(Predicate::and)
239:                         .orElse(null);
240:         }
241:
242:         /**
243:          * The actual {@link CreateNewModelElementStrategy strategy} that creates a new element based on a template selected
244:          * by the user.
245:          *
246:          * @author Lucas Koehler
247:          */
248:         class Strategy implements CreateNewModelElementStrategy {
249:                 private final EClassSelectionStrategy classSelectionStrategy;
250:
251:                 /**
252:                  * Initializes me with a strategy for selecting classes permitted in the particular form context.
253:                  *
254:                  * @param classSelectionStrategy my class selection strategy
255:                  */
256:                 Strategy(final EClassSelectionStrategy classSelectionStrategy) {
257:                         super();
258:                         this.classSelectionStrategy = classSelectionStrategy;
259:                 }
260:
261:                 /**
262:                  * {@inheritDoc}
263:                  * <p>
264:                  * Create the new model element based on a template.
265:                  */
266:                 @Override
267:                 public Optional<EObject> createNewModelElement(EObject owner, EReference reference) {
268:                         final Set<Template> availableTemplates = collectAvailableTemplates(owner, reference);
269:•                        if (availableTemplates.isEmpty()) {
270:                                 // This should not happen because in case of no available templates, the provider should not bid during
271:                                 // the strategy selection
272:                                 return Optional.empty();
273:                         }
274:
275:                         final Set<EClass> availableClasses = new LinkedHashSet<EClass>(
276:                                 EMFUtils.getSubClasses(reference.getEReferenceType()));
277:                         final Set<EClass> subClasses = new LinkedHashSet<EClass>(
278:                                 classSelectionStrategy.collectEClasses(owner, reference, availableClasses));
279:
280:                         Template selected = availableTemplates.iterator().next();
281:•                        if (availableTemplates.size() > 1) {
282:                                 // Don't show classes for which we don't have templates (bug 543461)
283:                                 final Set<EClass> templateClasses = availableTemplates.stream()
284:                                         .map(Template::getInstance).filter(Objects::nonNull).map(ignoreException(EObject::eClass))
285:                                         .filter(Objects::nonNull)
286:                                         .collect(Collectors.toSet());
287:                                 subClasses.retainAll(templateClasses);
288:
289:                                 final Optional<Template> selectedElement = showSelectModelInstancesDialog(subClasses,
290:                                         availableTemplates);
291:
292:•                                if (selectedElement.isPresent()) {
293:                                         selected = selectedElement.get();
294:                                 } else {
295:                                         return Optional.empty();
296:                                 }
297:                         }
298:
299:                         return Optional.of(EcoreUtil.copy(selected.getInstance()));
300:                 }
301:
302:                 /**
303:                  * Show a model instance selection dialog to first select a sub class and then a fitting template.
304:                  *
305:                  * @param subClasses the sub classes to choose from
306:                  * @param availableInstances the instances to choose from
307:                  * @return the instances selected by the user
308:                  */
309:                 protected Optional<Template> showSelectModelInstancesDialog(Set<EClass> subClasses,
310:                         Set<Template> availableInstances) {
311:                         final SelectSubclassAndTemplateWizard wizard = new SelectSubclassAndTemplateWizard(
312:                                 localizationService.getString(TemplateCreateNewModelElementStrategyProvider.class,
313:                                         MessageKeys.TemplateCreateNewModelElementStrategyProvider_wizardTitle),
314:                                 subClasses, availableInstances, localizationService);
315:
316:                         final WizardDialog wd = new WizardDialog(Display.getDefault().getActiveShell(), wizard);
317:                         wd.open();
318:
319:                         return wizard.getSelectedTemplate();
320:                 }
321:         }
322:
323:         // CHECKSTYLE.OFF: IllegalCatch
324:         private static <T, R> Function<T, R> ignoreException(Function<T, R> function) {
325:                 return t -> {
326:                         try {
327:                                 return function.apply(t);
328:                         } catch (final Exception e) {
329:                                 // ignore
330:                                 return null;
331:                         }
332:                 };
333:         }
334:         // CHECKSTYLE.ON: IllegalCatch
335: }