Skip to content

Package: CreateSegmentDmrWizard$SegmentCreationPage$SelectedFeatureViewServiceImpl

CreateSegmentDmrWizard$SegmentCreationPage$SelectedFeatureViewServiceImpl

nameinstructionbranchcomplexitylinemethod
CreateSegmentDmrWizard.SegmentCreationPage.SelectedFeatureViewServiceImpl(CreateSegmentDmrWizard.SegmentCreationPage)
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%
dispose()
M: 0 C: 1
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getFeature()
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getPriority()
M: 0 C: 2
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
instantiate(ViewModelContext)
M: 0 C: 1
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: * Lucas Koehler - initial API and implementation
13: ******************************************************************************/
14: package org.eclipse.emf.ecp.view.internal.editor.handler;
15:
16: import java.util.Arrays;
17: import java.util.Collection;
18: import java.util.Collections;
19: import java.util.LinkedHashMap;
20: import java.util.LinkedList;
21: import java.util.List;
22: import java.util.Map;
23: import java.util.Objects;
24: import java.util.Optional;
25: import java.util.stream.Collectors;
26:
27: import org.eclipse.core.resources.IFile;
28: import org.eclipse.emf.common.notify.Adapter;
29: import org.eclipse.emf.common.notify.AdapterFactory;
30: import org.eclipse.emf.common.notify.Notification;
31: import org.eclipse.emf.common.notify.impl.AdapterImpl;
32: import org.eclipse.emf.common.util.Diagnostic;
33: import org.eclipse.emf.common.util.EList;
34: import org.eclipse.emf.common.util.URI;
35: import org.eclipse.emf.ecore.EClass;
36: import org.eclipse.emf.ecore.EObject;
37: import org.eclipse.emf.ecore.EPackage;
38: import org.eclipse.emf.ecore.EReference;
39: import org.eclipse.emf.ecore.EStructuralFeature;
40: import org.eclipse.emf.ecore.resource.Resource;
41: import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
42: import org.eclipse.emf.ecore.util.EcoreUtil;
43: import org.eclipse.emf.ecp.ui.view.ECPRendererException;
44: import org.eclipse.emf.ecp.ui.view.swt.ECPSWTViewRenderer;
45: import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
46: import org.eclipse.emf.ecp.view.spi.context.ViewModelContextFactory;
47: import org.eclipse.emf.ecp.view.spi.context.ViewModelService;
48: import org.eclipse.emf.ecp.view.spi.editor.controls.EStructuralFeatureSelectionValidator;
49: import org.eclipse.emf.ecp.view.spi.editor.controls.SegmentIdeDescriptor;
50: import org.eclipse.emf.ecp.view.spi.editor.controls.SelectedFeatureViewService;
51: import org.eclipse.emf.ecp.view.spi.model.VDomainModelReference;
52: import org.eclipse.emf.ecp.view.spi.model.VDomainModelReferenceSegment;
53: import org.eclipse.emf.ecp.view.spi.model.VFeatureDomainModelReferenceSegment;
54: import org.eclipse.emf.ecp.view.spi.model.VView;
55: import org.eclipse.emf.ecp.view.spi.model.VViewFactory;
56: import org.eclipse.emf.ecp.view.spi.model.VViewPackage;
57: import org.eclipse.emf.ecp.view.spi.model.util.SegmentResolvementUtil;
58: import org.eclipse.emf.ecp.view.spi.provider.ViewProviderHelper;
59: import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
60: import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory;
61: import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;
62: import org.eclipse.emfforms.common.internal.validation.ValidationServiceImpl;
63: import org.eclipse.jface.dialogs.MessageDialog;
64: import org.eclipse.jface.layout.GridDataFactory;
65: import org.eclipse.jface.layout.GridLayoutFactory;
66: import org.eclipse.jface.viewers.ISelection;
67: import org.eclipse.jface.viewers.ISelectionChangedListener;
68: import org.eclipse.jface.viewers.IStructuredSelection;
69: import org.eclipse.jface.viewers.SelectionChangedEvent;
70: import org.eclipse.jface.viewers.TableViewer;
71: import org.eclipse.jface.viewers.TreePath;
72: import org.eclipse.jface.viewers.TreeSelection;
73: import org.eclipse.jface.viewers.TreeViewer;
74: import org.eclipse.jface.wizard.IWizardPage;
75: import org.eclipse.jface.wizard.Wizard;
76: import org.eclipse.jface.wizard.WizardPage;
77: import org.eclipse.swt.SWT;
78: import org.eclipse.swt.widgets.Button;
79: import org.eclipse.swt.widgets.Combo;
80: import org.eclipse.swt.widgets.Composite;
81: import org.eclipse.swt.widgets.Event;
82: import org.eclipse.swt.widgets.Label;
83: import org.eclipse.swt.widgets.Listener;
84: import org.osgi.framework.BundleContext;
85: import org.osgi.framework.FrameworkUtil;
86: import org.osgi.framework.InvalidSyntaxException;
87: import org.osgi.framework.ServiceReference;
88:
89: /**
90: * An advanced DMR creation wizard that allows the user to create a segment based DMR.
91: * It allows to either create a simple reference path consisting only of feature segments or switching to an advanced
92: * mode. The advanced mode allows to create the reference path one segment at a time. Thereby, all available segment
93: * types can be created.
94: *
95: * @author Lucas Koehler
96: *
97: */
98: public class CreateSegmentDmrWizard extends Wizard {
99:
100:         private final VDomainModelReference existingDMR;
101:         private final EStructuralFeatureSelectionValidator selectionValidator;
102:         private final SegmentGenerator segmentGenerator;
103:         private final String lastSegmentTypeInfo;
104:         private final EClass lastSegmentType;
105:         /**
106:          * Contains all types of segments that can be created in the advanced mode. The types are mapped to their
107:          * corresponding {@link SegmentIdeDescriptor}.
108:          */
109:         private final Map<EClass, SegmentIdeDescriptor> segmentToIdeDescriptorMap = new LinkedHashMap<EClass, SegmentIdeDescriptor>();
110:         private final List<ServiceReference<SegmentIdeDescriptor>> serviceReferences = new LinkedList<ServiceReference<SegmentIdeDescriptor>>();
111:
112:         private SimpleModePage simpleModePage;
113:         private VDomainModelReference advancedDmr;
114:         private VDomainModelReference resultDmr;
115:         private EClass rootEClass;
116:
117:         private SelectEcorePage selectEcorePage;
118:         private SelectEClassWizardPage selectEClassPage;
119:         private Object selectedContainer;
120:
121:         /**
122:          * A wizard used for creating and configuring a DomainModelReference.
123:          *
124:          * @param rootEClass The root {@link EClass} of the DMR to create. IF this is <code>null</code>, the
125:          * wizard offers to chose a root EClass. To make this clear, you might want to use the constructor
126:          * {@link CreateSegmentDmrWizard#CreateSegmentDmrWizard(String, EStructuralFeatureSelectionValidator, SegmentGenerator, EClass, boolean)}.
127:          * @param windowTitle The title for the wizard window
128:          * @param existingDMR The domain model reference to configure. May be null. If this is given the initial selection
129:          * of the wizard is based on this but the given DMR will not be changed. A new one is returned.
130:          * @param selectionValidator Validates whether a selected structural feature is a valid selection (e.g. the
131:          * selection could be required to be a multi reference)
132:          * @param segmentGenerator The {@link SegmentGenerator} used in the simple dmr creation mode
133:          * @param lastSegmentType The type that the last segment in advanced creation mode must have, or
134:          * <strong>null</strong> if there is no restriction
135:          * @param ignoreSegmentIdeRestriction If <code>true</code>, all types of segments are available independently of the
136:          * configuration in their {@link SegmentIdeDescriptor}.
137:          */
138:         // CHECKSTYLE.OFF: ParameterNumber
139:         public CreateSegmentDmrWizard(final EClass rootEClass, final String windowTitle,
140:                 VDomainModelReference existingDMR, EStructuralFeatureSelectionValidator selectionValidator,
141:                 SegmentGenerator segmentGenerator, EClass lastSegmentType, boolean ignoreSegmentIdeRestriction) {
142:                 // CHECKSTYLE.ON: ParameterNumber
143:                 setWindowTitle(windowTitle);
144:                 this.rootEClass = rootEClass;
145:                 this.existingDMR = existingDMR;
146:                 this.selectionValidator = selectionValidator;
147:                 this.segmentGenerator = segmentGenerator;
148:                 this.lastSegmentType = lastSegmentType;
149:
150:                 setForcePreviousAndNextButtons(true);
151:                 advancedDmr = VViewFactory.eINSTANCE.createDomainModelReference();
152:                 if (lastSegmentType != null) {
153:                         lastSegmentTypeInfo = "\nNote: Cannot finish because the last segment must be a " //$NON-NLS-1$
154:                                 + lastSegmentType.getName();
155:                 } else {
156:                         lastSegmentTypeInfo = ""; //$NON-NLS-1$
157:                 }
158:
159:                 final Collection<SegmentIdeDescriptor> descriptors = collectSegmentIdeDescriptors(ignoreSegmentIdeRestriction);
160:                 for (final SegmentIdeDescriptor descriptor : descriptors) {
161:                         segmentToIdeDescriptorMap.put(descriptor.getSegmentType(), descriptor);
162:                 }
163:         }
164:
165:         /**
166:          * Collect all {@link SegmentIdeDescriptor SegmentIdeDescriptors} for the advanced mode.
167:          *
168:          * @param ignoreSegmentIdeRestriction Whether the descriptors' availability flag must be considered when collecting
169:          * the descriptors
170:          * @return The collection of {@link SegmentIdeDescriptor SegmentIdeDescriptors}; might be empty but never
171:          * <code>null</code>
172:          * @see SegmentIdeDescriptor#isAvailableInIde()
173:          */
174:         protected Collection<SegmentIdeDescriptor> collectSegmentIdeDescriptors(boolean ignoreSegmentIdeRestriction) {
175:                 try {
176:                         final BundleContext bundleContext = FrameworkUtil.getBundle(CreateSegmentDmrWizard.class)
177:                                 .getBundleContext();
178:                         final Collection<ServiceReference<SegmentIdeDescriptor>> references = bundleContext
179:                                 .getServiceReferences(SegmentIdeDescriptor.class, null);
180:                         serviceReferences.addAll(references);
181:                         return references.stream()
182:                                 .map(bundleContext::getService)
183:                                 .filter(Objects::nonNull)
184:                                 .filter(d -> ignoreSegmentIdeRestriction || d.isAvailableInIde())
185:                                 .collect(Collectors.toSet());
186:                 } catch (final InvalidSyntaxException ex) {
187:                         // Should never happen because no filter is used
188:                 }
189:                 return Collections.emptySet();
190:         }
191:
192:         /**
193:          * A wizard used for creating a new DomainModelReference. Before the DMR is created, the wizard allows
194:          * to select a root EClass for the new DMR.
195:          *
196:          * @param windowTitle The title for the wizard window
197:          * @param selectionValidator Validates whether a selected structural feature is a valid selection (e.g. the
198:          * selection could be required to be a multi reference)
199:          * @param segmentGenerator The {@link SegmentGenerator} used in the simple dmr creation mode
200:          * @param lastSegmentType The type that the last segment in advanced creation mode must have, or
201:          * <strong>null</strong> if there is no restriction
202:          * @param ignoreSegmentIdeRestriction If <code>true</code>, all types of segments are available independently of the
203:          * configuration in their {@link SegmentIdeDescriptor}.
204:          */
205:         // CHECKSTYLE.OFF: ParameterNumber
206:         public CreateSegmentDmrWizard(final String windowTitle, EStructuralFeatureSelectionValidator selectionValidator,
207:                 SegmentGenerator segmentGenerator, EClass lastSegmentType, boolean ignoreSegmentIdeRestriction) {
208:                 // CHECKSTYLE.ON: ParameterNumber
209:                 this(null, windowTitle, null, selectionValidator, segmentGenerator, lastSegmentType,
210:                         ignoreSegmentIdeRestriction);
211:
212:         }
213:
214:         /**
215:          * Creates a new dmr creation with default values for segment generation (= a simple feature segment path), segment
216:          * types (all none restricted types), and the required last segment type (all are allowed) in advanced mode.
217:          *
218:          * @param rootEClass The root {@link EClass} of the DMR to create. IF this is <code>null</code>, the
219:          * wizard offers to chose a root EClass. To make this clear, you might want to use the constructor
220:          * {@link CreateSegmentDmrWizard#CreateSegmentDmrWizard(String, EStructuralFeatureSelectionValidator, SegmentGenerator, EClass, boolean)}.
221:          * @param windowTitle The title for the wizard window
222:          * @param existingDMR The domain model reference to configure. May be null. If this is given the initial selection
223:          * of the wizard is based on this but the given DMR will not be changed. A new one is returned.
224:          * @param selectionValidator Validates whether a selected structural feature is a valid selection (e.g. the
225:          * selection could be required to be a multi reference)
226:          */
227:         public CreateSegmentDmrWizard(final EClass rootEClass, final String windowTitle,
228:                 VDomainModelReference existingDMR, EStructuralFeatureSelectionValidator selectionValidator) {
229:                 this(rootEClass, windowTitle, existingDMR, selectionValidator, new FeatureSegmentGenerator(), null, false);
230:         }
231:
232:         /**
233:          * Returns the configured {@link VDomainModelReference}. This is either a new DMR or the edited input DMR.
234:          * The return value is empty if the dialog was cancelled or the dialog is still open.
235:          *
236:          * @return The configured {@link VDomainModelReference} or an empty value if the dialog was cancelled
237:          */
238:         public Optional<VDomainModelReference> getDomainModelReference() {
239:                 return Optional.ofNullable(resultDmr);
240:         }
241:
242:         @Override
243:         public IWizardPage getNextPage(IWizardPage page) {
244:                 if (page == selectEcorePage) {
245:                         final EPackage p = getEPackage(selectEcorePage.getSelectedContainer());
246:                         selectEClassPage.setSelectedEPackage(p);
247:                         return selectEClassPage;
248:                 } else if (page == selectEClassPage) {
249:                         rootEClass = selectEClassPage.getSelectedEClasses().get(0);
250:                         simpleModePage.setRootEClass(rootEClass);
251:                         return simpleModePage;
252:                 }
253:                 return super.getNextPage(page);
254:         }
255:
256:         /**
257:          * @return The {@link EPackage} from the selected container
258:          */
259:         private EPackage getEPackage(Object selectedContainer) {
260:                 EPackage ePackage = null;
261:                 if (EPackage.class.isInstance(selectedContainer)) {
262:                         ePackage = EPackage.class.cast(selectedContainer);
263:                 } else if (IFile.class.isInstance(selectedContainer)) {
264:                         final ResourceSetImpl resourceSet = new ResourceSetImpl();
265:                         final String path = ((IFile) selectedContainer).getFullPath().toString();
266:                         final URI uri = URI.createPlatformResourceURI(path, true);
267:
268:                         final Resource resource = resourceSet.getResource(uri, true);
269:                         if (resource != null) {
270:
271:                                 final EList<EObject> contents = resource.getContents();
272:                                 if (contents.size() != 1) {
273:                                         return null;
274:                                 }
275:
276:                                 final EObject object = contents.get(0);
277:                                 if (!(object instanceof EPackage)) {
278:                                         return null;
279:                                 }
280:
281:                                 ePackage = (EPackage) object;
282:                         }
283:                 }
284:                 return ePackage;
285:         }
286:
287:         @Override
288:         public void dispose() {
289:                 final BundleContext bundleContext = FrameworkUtil.getBundle(CreateSegmentDmrWizard.class)
290:                         .getBundleContext();
291:                 for (final ServiceReference<SegmentIdeDescriptor> serviceRef : serviceReferences) {
292:                         bundleContext.ungetService(serviceRef);
293:                 }
294:                 segmentToIdeDescriptorMap.clear();
295:                 serviceReferences.clear();
296:                 super.dispose();
297:         }
298:
299:         @Override
300:         public void addPages() {
301:                 if (rootEClass == null) {
302:                         selectEcorePage = new SelectEcorePage("org.eclipse.emf.ecp.ui.view.editor.controls"); //$NON-NLS-1$
303:                         addPage(selectEcorePage);
304:
305:                         selectEClassPage = new SelectEClassWizardPage() {
306:                                 @Override
307:                                 protected boolean isMultiSelect() {
308:                                         return false;
309:                                 }
310:                         };
311:                         addPage(selectEClassPage);
312:                 }
313:                 simpleModePage = new SimpleModePage("Configure Domain Model Reference", //$NON-NLS-1$
314:                         "Select an EStructuralFeature", //$NON-NLS-1$
315:                         "Select a structural feature for the domain model reference or switch to the advanced creation mode\n to create the reference path one feature at a time.", //$NON-NLS-1$
316:                         rootEClass, getInitialSelection());
317:                 addPage(simpleModePage);
318:         }
319:
320:         /**
321:          * @return The selected ecore file, <strong>IF</strong> a root EClass was selected and it is located in a local
322:          * ecore file.
323:          */
324:         public Optional<IFile> getSelectedEcore() {
325:                 if (IFile.class.isInstance(selectedContainer)) {
326:                         return Optional.of((IFile) selectedContainer);
327:                 }
328:                 return Optional.empty();
329:         }
330:
331:         /**
332:          * Returns the root {@link EClass} of the domain model reference. IF a root EClass was given when this wizard was
333:          * created, it is returned. Otherwise, the EClass selected by the user is returned. If no EClass was given and the
334:          * dialog was cancelled before an EClass was selected, nothing is returned.
335:          *
336:          * @return The root {@link EClass} of the created/configured domain model reference or nothing if no EClass was
337:          * given and selected.
338:          */
339:         public Optional<EClass> getRootEClass() {
340:                 return Optional.ofNullable(rootEClass);
341:         }
342:
343:         /**
344:          * @return The initial selection for the {@link SelectFeaturePathWizardPage SelectFeaturePathWizardPage's} tree
345:          * viewer. Returns an empty selection if there is no existing dmr or it does not contain any segments.
346:          */
347:         private ISelection getInitialSelection() {
348:                 if (existingDMR == null || existingDMR.getSegments().isEmpty()) {
349:                         return TreeSelection.EMPTY;
350:                 }
351:
352:                 final List<EStructuralFeature> pathList = SegmentResolvementUtil
353:                         .resolveSegmentsToFeatureList(existingDMR.getSegments(), rootEClass);
354:                 if (pathList.size() == existingDMR.getSegments().size()) {
355:                         return new TreeSelection(new TreePath(pathList.toArray()));
356:                 }
357:                 return TreeSelection.EMPTY;
358:         }
359:
360:         @Override
361:         public boolean performFinish() {
362:                 if (!canFinish()) {
363:                         return false;
364:                 }
365:
366:                 if (selectEcorePage != null) {
367:                         selectedContainer = selectEcorePage.getSelectedContainer();
368:                 }
369:
370:                 if (simpleModePage.equals(getContainer().getCurrentPage())) {
371:                         // simple mode was used
372:                         resultDmr = simpleModePage.getDomainModelReference();
373:                 } else {
374:                         final SegmentCreationPage finalSegmentCreationPage = (SegmentCreationPage) getContainer().getCurrentPage();
375:                         final int lastSegmentIndex = finalSegmentCreationPage.index;
376:                         /*
377:                          * If there are segments in the dmr with a higher index than the final segment creation page,
378:                          * they origin from a page with a higher index where the "back" button has been clicked by the user.
379:                          * => They need to be removed.
380:                          */
381:                         for (int i = advancedDmr.getSegments().size() - 1; i > lastSegmentIndex; i--) {
382:                                 advancedDmr.getSegments().remove(i);
383:                         }
384:
385:                         // remove validation adapters - they are only needed during the dmr creation
386:                         for (final VDomainModelReferenceSegment segment : advancedDmr.getSegments()) {
387:                                 for (final Adapter adapter : segment.eAdapters()) {
388:                                         if (SegmentCreationPage.SegmentAdapter.class.isInstance(adapter)) {
389:                                                 segment.eAdapters().remove(adapter);
390:                                                 // only one is registered, avoid concurrent modification exception
391:                                                 break;
392:                                         }
393:                                 }
394:                         }
395:                         resultDmr = advancedDmr;
396:                 }
397:
398:                 return true;
399:         }
400:
401:         @Override
402:         public boolean canFinish() {
403:                 // "simple" mode
404:                 if (simpleModePage.equals(getContainer().getCurrentPage())) {
405:                         return simpleModePage.isPageComplete();
406:                 }
407:                 // "advanced" mode
408:                 if (advancedDmr.getSegments().isEmpty() || !getContainer().getCurrentPage().isPageComplete()) {
409:                         return false;
410:                 }
411:                 // TODO check with segment validator
412:                 final EList<VDomainModelReferenceSegment> segments = advancedDmr.getSegments();
413:                 final VDomainModelReferenceSegment lastSegment = segments.get(segments.size() - 1);
414:                 final SegmentIdeDescriptor descriptor = segmentToIdeDescriptorMap.get(lastSegment.eClass());
415:                 if (!descriptor.isAllowedAsLastElementInPath()) {
416:                         return false;
417:                 }
418:                 if (lastSegmentType == null) {
419:                         return true;
420:                 }
421:                 return lastSegmentType.isInstance(lastSegment);
422:         }
423:
424:         /**
425:          * The wizard's first page. It allows to either create a simple reference path consisting only of feature segments
426:          * or switching to an advanced mode. The advanced mode allows to create the reference path one reference at a time.
427:          * Thereby, all available segment types can be created.
428:          *
429:          * @author Lucas Koehler
430:          *
431:          */
432:         private class SimpleModePage extends SelectFeaturePathWizardPage {
433:
434:                 protected SimpleModePage(String pageName, String pageTitle, String pageDescription,
435:                         EClass rootEClass, ISelection firstSelection) {
436:                         super(pageName, pageTitle, pageDescription, rootEClass, firstSelection, segmentGenerator,
437:                                 selectionValidator, false);
438:                 }
439:
440:                 @Override
441:                 public void createControl(Composite parent) {
442:                         super.createControl(parent);
443:
444:                         final Composite controlComposite = (Composite) getControl();
445:
446:                         final Button advancedModeButton = new Button(controlComposite, SWT.NONE);
447:                         GridDataFactory.fillDefaults().align(SWT.FILL, SWT.END).grab(true, false).applyTo(advancedModeButton);
448:                         advancedModeButton.setText("Switch To Advanced DMR Creation"); //$NON-NLS-1$
449:                         if (!segmentToIdeDescriptorMap.isEmpty()) {
450:                                 advancedModeButton.setToolTipText(
451:                                         "Using the advanced Domain Model Reference creation allows to use all available kinds of segments, i.e. index segments."); //$NON-NLS-1$
452:                                 advancedModeButton.addListener(SWT.Selection, event -> {
453:                                         final IWizardPage advancedPage = new SegmentCreationPage("Select Segment Type", rootEClass, 0); //$NON-NLS-1$
454:                                         addPage(advancedPage);
455:                                         advancedDmr = VViewFactory.eINSTANCE.createDomainModelReference();
456:                                         getContainer().showPage(advancedPage);
457:                                 });
458:                         } else {
459:                                 // There are no registered segment types that can be used => Advanced mode is useless
460:                                 advancedModeButton.setEnabled(false);
461:                                 advancedModeButton.setToolTipText(
462:                                         "There are no segment types available for IDE usage. Therefore, the advanced creation mode is deactivated."); //$NON-NLS-1$
463:                         }
464:                 }
465:
466:                 @Override
467:                 protected TreeViewer createTreeViewer(Composite composite) {
468:                         // only allow single selection
469:                         return new TreeViewer(composite, SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER | SWT.SINGLE);
470:                 }
471:
472:                 /**
473:                  * Override method and never allow to flip to next page: Advanced mode should only be accessed by using the
474:                  * designated button.<br/>
475:                  * The next button would allow to go back to the advanced mode after it has been activated and the user pressed
476:                  * back on the first advanced page. This could lead to unexpected behavior. Therefore, it is not allowed.
477:                  * <hr>
478:                  * {@inheritDoc}
479:                  *
480:                  * @see org.eclipse.jface.wizard.WizardPage#canFlipToNextPage()
481:                  */
482:                 @Override
483:                 public boolean canFlipToNextPage() {
484:                         return false;
485:                 }
486:
487:         }
488:
489:         /**
490:          * WizardPage to create one segment. Allows to create all types of registered segments.
491:          *
492:          * @author Lucas Koehler
493:          *
494:          */
495:         private class SegmentCreationPage extends WizardPage {
496:                 private static final String PLEASE_SELECT_A_SEGMENT_TYPE_ERROR_MSG = "Please select a segment type."; //$NON-NLS-1$
497:                 private final int index;
498:                 private Composite pageComposite;
499:
500:                 private ComposedAdapterFactory composedAdapterFactory;
501:                 private AdapterFactoryLabelProvider labelProvider;
502:                 private EClass rootEClass;
503:
504:                 private EClass segmentType;
505:                 private VFeatureDomainModelReferenceSegment createdSegment;
506:                 private Composite renderComposite;
507:                 private SegmentCreationPage nextPage;
508:                 private TableViewer tableViewer;
509:                 private EStructuralFeature selectedFeature;
510:
511:                 /**
512:                  * Set to true, if the page is complete except for the successful validation of the current segment. Is still
513:                  * true when the validation is successful, too.
514:                  */
515:                 private boolean pageCompleteWithoutValidation;
516:
517:                 /**
518:                  * @param pageName
519:                  * @param rootEClass The root {@link EClass} to select the feature from for the path segment created by this
520:                  * page
521:                  * @param index The index of the created segment in the DMRs segment list. This is necessary to set the segment
522:                  * at the correct location in the DMR.
523:                  */
524:                 protected SegmentCreationPage(String pageName, EClass rootEClass, int index) {
525:                         super(pageName);
526:                         setTitle("Create a Segment"); //$NON-NLS-1$
527:                         setDescription("Create a segment by selecting its type and an appropriate feature."); //$NON-NLS-1$
528:                         this.rootEClass = rootEClass;
529:                         this.index = index;
530:                 }
531:
532:                 @Override
533:                 public boolean canFlipToNextPage() {
534:                         if (createdSegment == null) {
535:                                 return false;
536:                         }
537:                         final SegmentIdeDescriptor ideDescriptor = segmentToIdeDescriptorMap.get(createdSegment.eClass());
538:                         if (ideDescriptor.isLastElementInPath()) {
539:                                 return false;
540:                         }
541:                         final String domainModelFeature = createdSegment.getDomainModelFeature();
542:                         if (domainModelFeature != null && !domainModelFeature.isEmpty()) {
543:                                 final EStructuralFeature feature = rootEClass
544:                                         .getEStructuralFeature(createdSegment.getDomainModelFeature());
545:                                 if (EReference.class.isInstance(feature)) {
546:                                         // Feature segments can only contain a multi reference at the end of the reference path, never in
547:                                         // the beginning or the middle
548:                                         if (VViewPackage.eINSTANCE.getFeatureDomainModelReferenceSegment()
549:                                                 .equals(createdSegment.eClass()) && EReference.class.cast(feature).isMany()) {
550:                                                 return false;
551:                                         }
552:
553:                                         if (nextPage.rootEClass != null) {
554:                                                 return super.canFlipToNextPage();
555:                                         }
556:                                 }
557:                         }
558:                         return false;
559:                 }
560:
561:                 /**
562:                  * Sets the root {@link EClass} of this page and updates the input of the table viewer.
563:                  *
564:                  * @param rootEClass The new root {@link EClass}
565:                  */
566:                 public void setRootEClass(EClass rootEClass) {
567:                         this.rootEClass = rootEClass;
568:                         if (tableViewer != null) {
569:                                 tableViewer.setInput(rootEClass);
570:                         }
571:                 }
572:
573:                 @Override
574:                 public void createControl(final Composite parent) {
575:                         nextPage = new SegmentCreationPage(getName(), null, index + 1);
576:                         addPage(nextPage);
577:
578:                         pageComposite = new Composite(parent, SWT.NONE);
579:                         GridLayoutFactory.fillDefaults().applyTo(pageComposite);
580:                         GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(pageComposite);
581:
582:                         composedAdapterFactory = new ComposedAdapterFactory(new AdapterFactory[] {
583:                                 new ReflectiveItemProviderAdapterFactory(),
584:                                 new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE) });
585:                         labelProvider = new AdapterFactoryLabelProvider(composedAdapterFactory);
586:                         final EStructuralFeatureContentProvider contentProvider = new EStructuralFeatureContentProvider(
587:                                 false);
588:
589:                         // get subclasses
590:                         final Collection<EClass> segmentClasses = segmentToIdeDescriptorMap.keySet();
591:                         final Map<String, EClass> segmentClassesMap = new LinkedHashMap<String, EClass>();
592:                         for (final EClass segmentClass : segmentClasses) {
593:                                 segmentClassesMap.put(segmentClass.getName(), segmentClass);
594:                         }
595:
596:                         final Composite segmentTypeComposite = new Composite(pageComposite, SWT.NONE);
597:                         GridLayoutFactory.fillDefaults().numColumns(2).applyTo(segmentTypeComposite);
598:                         GridDataFactory.fillDefaults().align(SWT.FILL, SWT.BEGINNING).grab(true, false)
599:                                 .applyTo(segmentTypeComposite);
600:
601:                         // combo box label
602:                         final Label segmentSelectionLabel = new Label(segmentTypeComposite, SWT.NONE);
603:                         segmentSelectionLabel.setText("Select Segment Type:"); //$NON-NLS-1$
604:                         GridDataFactory.fillDefaults().align(SWT.BEGINNING, SWT.CENTER).grab(false, false)
605:                                 .applyTo(segmentSelectionLabel);
606:
607:                         // configure combo box
608:                         final Combo segmentTypeSelector = new Combo(segmentTypeComposite, SWT.READ_ONLY | SWT.DROP_DOWN);
609:                         GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false)
610:                                 .applyTo(segmentTypeSelector);
611:                         final String[] items = segmentClassesMap.keySet().toArray(new String[segmentClassesMap.size()]);
612:                         Arrays.sort(items);
613:                         segmentTypeSelector.setItems(items);
614:
615:                         // table viewer with single selection
616:                         tableViewer = new TableViewer(pageComposite, SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER | SWT.SINGLE);
617:                         GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(tableViewer.getControl());
618:                         tableViewer.setContentProvider(contentProvider);
619:                         tableViewer.setLabelProvider(labelProvider);
620:                         tableViewer.setInput(rootEClass);
621:                         tableViewer.addSelectionChangedListener(new FeatureSelectionChangedListerner());
622:                         segmentTypeSelector.addListener(SWT.Selection,
623:                                 new SegmentTypeSelectionListener(pageComposite, segmentClassesMap, tableViewer, segmentTypeSelector));
624:
625:                         // If available, select the feature segment by default
626:                         if (segmentClassesMap.values().contains(VViewPackage.eINSTANCE.getFeatureDomainModelReferenceSegment())) {
627:                                 final int index = Arrays.binarySearch(items,
628:                                         VViewPackage.eINSTANCE.getFeatureDomainModelReferenceSegment().getName());
629:                                 if (index >= 0) {
630:                                         segmentTypeSelector.select(index);
631:                                         segmentTypeSelector.notifyListeners(SWT.Selection, new Event());
632:                                 }
633:                         }
634:
635:                         setControl(pageComposite);
636:                 }
637:
638:                 /**
639:                  * Creates the {@link Composite} used to render the advanced properties of the current segment.
640:                  * If it already exists, the existing one is disposed and a new one created.
641:                  *
642:                  * @param parent The created {@link Composite Composite's} parent.
643:                  * @return The {@link Composite}
644:                  */
645:                 private Composite initRenderComposite() {
646:                         if (renderComposite != null) {
647:                                 renderComposite.dispose();
648:                         }
649:                         renderComposite = new Composite(pageComposite, SWT.NONE);
650:                         GridLayoutFactory.fillDefaults().applyTo(renderComposite);
651:                         GridDataFactory.fillDefaults().align(SWT.FILL, SWT.END).grab(true, false).applyTo(renderComposite);
652:                         return renderComposite;
653:                 }
654:
655:                 /**
656:                  * Listener to handle a segment type selection.
657:                  *
658:                  * @author Lucas Koehler
659:                  *
660:                  */
661:                 private final class SegmentTypeSelectionListener implements Listener {
662:                         private final Map<String, EClass> segmentClassesMap;
663:                         private final TableViewer tableViewer;
664:                         private final Combo segmentTypeSelector;
665:
666:                         private SegmentTypeSelectionListener(Composite composite, Map<String, EClass> segmentClassesMap,
667:                                 TableViewer tableViewer, Combo segmentTypeSelector) {
668:                                 this.segmentClassesMap = segmentClassesMap;
669:                                 this.tableViewer = tableViewer;
670:                                 this.segmentTypeSelector = segmentTypeSelector;
671:                         }
672:
673:                         @Override
674:                         public void handleEvent(Event event) {
675:                                 if (segmentTypeSelector.getText() == null || segmentTypeSelector.getText().isEmpty()) {
676:                                         setErrorMessage(PLEASE_SELECT_A_SEGMENT_TYPE_ERROR_MSG);
677:                                         createdSegment = null;
678:                                 }
679:                                 if (PLEASE_SELECT_A_SEGMENT_TYPE_ERROR_MSG.equals(getErrorMessage())) {
680:                                         setErrorMessage(null);
681:                                 }
682:
683:                                 segmentType = segmentClassesMap.get(segmentTypeSelector.getText());
684:                                 // (re)set last segment type info text
685:                                 if (lastSegmentType != null && !lastSegmentType.isSuperTypeOf(segmentType)
686:                                         && !getDescription().endsWith(lastSegmentTypeInfo)) {
687:                                         setDescription(getDescription() + lastSegmentTypeInfo);
688:                                 } else if (lastSegmentType != null && lastSegmentType.isSuperTypeOf(segmentType)
689:                                         && getDescription().endsWith(lastSegmentTypeInfo)) {
690:                                         final String desc = getDescription();
691:                                         setDescription(desc.substring(0, desc.length() - lastSegmentTypeInfo.length()));
692:                                 }
693:                                 // create segment and add to dmr
694:                                 initializeSegment();
695:
696:                                 // trigger selection changed event to apply current selection to the selected segment type
697:                                 tableViewer.setSelection(tableViewer.getSelection(), true);
698:                         }
699:                 }
700:
701:                 /**
702:                  * An adapter that is registered at the createdSegment to re-validate it on change.
703:                  * Additionally, it re-sets the root e class of the next page because it may be affected by the change (e.g. in
704:                  * case of a mapping segment).
705:                  *
706:                  * @author Lucas Koehler
707:                  *
708:                  */
709:                 private class SegmentAdapter extends AdapterImpl {
710:                         @Override
711:                         public void notifyChanged(Notification notification) {
712:                                 if (notification.isTouch()) {
713:                                         return;
714:                                 }
715:                                 if (validateSegment()) {
716:                                         if (pageCompleteWithoutValidation) {
717:                                                 setErrorMessage(null);
718:                                                 setPageComplete(true);
719:                                         }
720:                                 }
721:                                 if (EReference.class.isInstance(selectedFeature)) {
722:                                         final SegmentIdeDescriptor ideDescriptor = segmentToIdeDescriptorMap.get(createdSegment.eClass());
723:                                         nextPage.setRootEClass(ideDescriptor.getReferenceTypeResolver()
724:                                                 .resolveNextEClass(EReference.class.cast(selectedFeature), createdSegment));
725:                                 }
726:                         }
727:                 }
728:
729:                 /**
730:                  * Creates a new instance of the selected segment type and renders its features below the tree viewer.
731:                  * Also, the segment is added at the correct position in the wizard's DMR.
732:                  */
733:                 private void initializeSegment() {
734:                         if (segmentType == null) {
735:                                 return;
736:                         }
737:                         // create segment and add to dmr
738:                         createdSegment = (VFeatureDomainModelReferenceSegment) EcoreUtil.create(segmentType);
739:                         createdSegment.eAdapters().add(new SegmentAdapter());
740:                         if (advancedDmr.getSegments().size() > index) {
741:                                 advancedDmr.getSegments().set(index, createdSegment);
742:                         } else {
743:                                 advancedDmr.getSegments().add(createdSegment);
744:                         }
745:
746:                         // render segment properties
747:                         try {
748:                                 initRenderComposite();
749:                                 final VView segmentView = ViewProviderHelper.getView(createdSegment, null);
750:                                 final ViewModelContext context = ViewModelContextFactory.INSTANCE
751:                                         .createViewModelContext(segmentView, createdSegment, new SelectedFeatureViewServiceImpl());
752:
753:                                 ECPSWTViewRenderer.INSTANCE.render(renderComposite, context);
754:                                 pageComposite.layout();
755:                         } catch (final ECPRendererException ex) {
756:                                 MessageDialog.openError(getShell(), "Rendering Error", //$NON-NLS-1$
757:                                         "The current segment could not be rendered: " + ex.getMessage()); //$NON-NLS-1$
758:                         }
759:                 }
760:
761:                 /**
762:                  * Selection changed listener for the table containing the structural features to select for the current
763:                  * segment.
764:                  *
765:                  * @author Lucas Koehler
766:                  *
767:                  */
768:                 private class FeatureSelectionChangedListerner implements ISelectionChangedListener {
769:                         @Override
770:                         public void selectionChanged(SelectionChangedEvent event) {
771:                                 /*
772:                                  * If there are segments in the dmr with a higher index than the current page,
773:                                  * they origin from a page with a higher index where the "back" button has been clicked by the user.
774:                                  * => They need to be removed, because they are no longer valid when the selection changes
775:                                  */
776:                                 for (int i = advancedDmr.getSegments().size() - 1; i > index; i--) {
777:                                         advancedDmr.getSegments().remove(i);
778:                                 }
779:
780:                                 // Page is not complete until the opposite is proven
781:                                 setPageComplete(false);
782:                                 pageCompleteWithoutValidation = false;
783:
784:                                 // Get the selected element
785:                                 final IStructuredSelection structuredSelection = (IStructuredSelection) event.getSelection();
786:                                 if (structuredSelection.isEmpty()) {
787:                                         return;
788:                                 }
789:
790:                                 // Reset segment whenever a new feature is selected because segment configurations may depend on the
791:                                 // selected feature.
792:                                 initializeSegment();
793:
794:                                 // Set the selected feature to the page
795:                                 final EStructuralFeature structuralFeature = (EStructuralFeature) structuredSelection
796:                                         .getFirstElement();
797:                                 selectedFeature = structuralFeature;
798:
799:                                 if (createdSegment == null) {
800:                                         setErrorMessage(PLEASE_SELECT_A_SEGMENT_TYPE_ERROR_MSG);
801:                                         return;
802:                                 }
803:
804:                                 // Validate that a valid structural feature was selected
805:                                 final SegmentIdeDescriptor ideDescriptor = segmentToIdeDescriptorMap.get(createdSegment.eClass());
806:                                 final String errorMessage = ideDescriptor.getEStructuralFeatureSelectionValidator()
807:                                         .isValid(structuralFeature);
808:                                 setErrorMessage(errorMessage);
809:                                 if (errorMessage != null) {
810:                                         return;
811:                                 }
812:
813:                                 createdSegment.setDomainModelFeature(structuralFeature.getName());
814:                                 if (EReference.class.isInstance(structuralFeature)) {
815:                                         final EReference reference = (EReference) structuralFeature;
816:                                         nextPage.setRootEClass(
817:                                                 ideDescriptor.getReferenceTypeResolver().resolveNextEClass(reference, createdSegment));
818:                                         if (nextPage.rootEClass == null) {
819:                                                 return;
820:                                         }
821:                                 }
822:
823:                                 pageCompleteWithoutValidation = true;
824:
825:                                 if (validateSegment()) {
826:                                         setPageComplete(true);
827:                                 }
828:                         }
829:
830:                 }
831:
832:                 /**
833:                  * Validates this page's segment and sets the appropriate (error) message if necessary.
834:                  *
835:                  * @return <code>true</code> if the segment is valid, <code>false</code> otherwise
836:                  */
837:                 private boolean validateSegment() {
838:                         final ValidationServiceImpl validator = new ValidationServiceImpl();
839:                         final Diagnostic diagnostic = validator.validate(createdSegment);
840:                         if (diagnostic.getSeverity() == Diagnostic.INFO) {
841:                                 setMessage("Validation Information: " + diagnostic.getMessage()); //$NON-NLS-1$
842:                         } else if (diagnostic.getSeverity() == Diagnostic.WARNING) {
843:                                 setMessage("Validation Warning: " + diagnostic.getMessage()); //$NON-NLS-1$
844:                         } else if (diagnostic.getSeverity() == Diagnostic.ERROR) {
845:                                 setErrorMessage(diagnostic.getMessage());
846:                                 return false;
847:                         }
848:                         return true;
849:                 }
850:
851:                 /**
852:                  * {@link ViewModelService} returning the currently selected feature of this {@link SegmentCreationPage}.
853:                  *
854:                  * @author Lucas Koehler
855:                  */
856:                 private class SelectedFeatureViewServiceImpl implements SelectedFeatureViewService {
857:
858:                         @Override
859:                         public void instantiate(ViewModelContext context) {
860:                                 // not needed
861:                         }
862:
863:                         @Override
864:                         public void dispose() {
865:                                 // not needed
866:                         }
867:
868:                         @Override
869:                         public int getPriority() {
870:                                 return 0;
871:                         }
872:
873:                         @Override
874:                         public EStructuralFeature getFeature() {
875:                                 return selectedFeature;
876:                         }
877:                 }
878:         }
879: }