Skip to content

Package: GridPasteKeyListener

GridPasteKeyListener

nameinstructionbranchcomplexitylinemethod
GridPasteKeyListener(Display, VControl, EMFFormsDatabindingEMF, EStructuralFeatureValueConverterService, EMFFormsLocalizationService, boolean)
M: 5 C: 44
90%
M: 1 C: 1
50%
M: 1 C: 1
50%
M: 0 C: 16
100%
M: 0 C: 1
100%
canBePasted(EStructuralFeature, String, EObject, EStructuralFeature.Setting)
M: 40 C: 0
0%
M: 8 C: 0
0%
M: 5 C: 0
0%
M: 9 C: 0
0%
M: 1 C: 0
0%
createSelectionMap(Point[])
M: 0 C: 48
100%
M: 0 C: 4
100%
M: 0 C: 3
100%
M: 0 C: 6
100%
M: 0 C: 1
100%
extractDiagnosticMessage(Diagnostic, EStructuralFeature, String)
M: 7 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
fillSelectionWithMultipleCopies(Point[], String)
M: 0 C: 78
100%
M: 0 C: 14
100%
M: 0 C: 8
100%
M: 0 C: 21
100%
M: 0 C: 1
100%
getControl()
M: 3 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getConverterService()
M: 3 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getFillStartPoints(Point[])
M: 0 C: 44
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 8
100%
M: 0 C: 1
100%
isEObjectReadOnly(EObject)
M: 2 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
isSettingReadOnly(EObject, EStructuralFeature, Object)
M: 2 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
keyPressed(KeyEvent)
M: 37 C: 0
0%
M: 8 C: 0
0%
M: 5 C: 0
0%
M: 10 C: 0
0%
M: 1 C: 0
0%
keyReleased(KeyEvent)
M: 1 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
modifyCellValue(String)
M: 2 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
pasteContents(Point, Grid, String)
M: 241 C: 0
0%
M: 34 C: 0
0%
M: 18 C: 0
0%
M: 58 C: 0
0%
M: 1 C: 0
0%
pasteSelection(Grid, String)
M: 93 C: 0
0%
M: 20 C: 0
0%
M: 11 C: 0
0%
M: 15 C: 0
0%
M: 1 C: 0
0%
postPasteContents()
M: 1 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
prePasteContents()
M: 1 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
setValue(IObservableValue, Object)
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
showDialog(Shell, String, String, List)
M: 37 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 7 C: 0
0%
M: 1 C: 0
0%
showErrors(List)
M: 23 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 7 C: 0
0%
M: 1 C: 0
0%

Coverage

1: /*******************************************************************************
2: * Copyright (c) 2011-2016 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: * Alexandra Buzila - initial API and implementation
13: ******************************************************************************/
14: package org.eclipse.emf.ecp.view.internal.table.nebula.grid;
15:
16: import java.util.ArrayList;
17: import java.util.Collections;
18: import java.util.Iterator;
19: import java.util.LinkedHashMap;
20: import java.util.LinkedHashSet;
21: import java.util.List;
22: import java.util.Map;
23: import java.util.Set;
24: import java.util.StringTokenizer;
25:
26: import org.eclipse.core.databinding.observable.IObserving;
27: import org.eclipse.core.databinding.observable.value.IObservableValue;
28: import org.eclipse.emf.common.util.Diagnostic;
29: import org.eclipse.emf.ecore.EEnum;
30: import org.eclipse.emf.ecore.EEnumLiteral;
31: import org.eclipse.emf.ecore.EObject;
32: import org.eclipse.emf.ecore.EStructuralFeature;
33: import org.eclipse.emf.ecore.EStructuralFeature.Setting;
34: import org.eclipse.emf.ecore.InternalEObject;
35: import org.eclipse.emf.ecore.util.EcoreUtil;
36: import org.eclipse.emf.ecp.view.spi.model.VControl;
37: import org.eclipse.emf.ecp.view.spi.model.VDomainModelReference;
38: import org.eclipse.emf.ecp.view.spi.model.VViewPackage;
39: import org.eclipse.emf.ecp.view.spi.table.model.VTableControl;
40: import org.eclipse.emfforms.spi.common.converter.EStructuralFeatureValueConverterService;
41: import org.eclipse.emfforms.spi.common.validation.PreSetValidationService;
42: import org.eclipse.emfforms.spi.core.services.databinding.emf.EMFFormsDatabindingEMF;
43: import org.eclipse.emfforms.spi.localization.EMFFormsLocalizationService;
44: import org.eclipse.emfforms.spi.swt.table.TableConfiguration;
45: import org.eclipse.jface.dialogs.MessageDialog;
46: import org.eclipse.nebula.widgets.grid.Grid;
47: import org.eclipse.swt.SWT;
48: import org.eclipse.swt.dnd.Clipboard;
49: import org.eclipse.swt.dnd.TextTransfer;
50: import org.eclipse.swt.events.KeyEvent;
51: import org.eclipse.swt.events.KeyListener;
52: import org.eclipse.swt.graphics.Point;
53: import org.eclipse.swt.widgets.Display;
54: import org.eclipse.swt.widgets.Shell;
55: import org.osgi.framework.BundleContext;
56: import org.osgi.framework.FrameworkUtil;
57: import org.osgi.framework.ServiceReference;
58:
59: /**
60: * {@link KeyListener} for the paste action on a {@link Grid} control.
61: *
62: * @author Alexandra Buzila
63: * @author Mathias Schaefer
64: * @since 1.10
65: *
66: */
67: public class GridPasteKeyListener implements KeyListener {
68:
69:         private static final String TAB = "\t"; //$NON-NLS-1$
70:         private static final String NT = "\n\t"; //$NON-NLS-1$
71:         private static final String IS_INPUTTABLE = "isInputtable"; //$NON-NLS-1$
72:         private final Clipboard clipboard;
73:         private final EMFFormsDatabindingEMF dataBinding;
74:         private final EStructuralFeatureValueConverterService converterService;
75:         private final VControl vControl;
76:
77:         private boolean selectPastedCells = true;
78:         private boolean alreadyPasted;
79:         private final PreSetValidationService preSetValidationService;
80:         private final Display display;
81:         private final EMFFormsLocalizationService localizationService;
82:
83:         /**
84:          * Constructor.
85:          *
86:          * @param display the {@link Display} on which to allocate this command's {@link Clipboard}.
87:          * @param vControl the {@link VTableControl}.
88:          * @param dataBinding {@link EMFFormsDatabindingEMF}
89:          * @param converterService {@link EStructuralFeatureValueConverterService}
90:          * @param localizationService {@link EMFFormsLocalizationService}
91:          * @param selectPastedCells whether to select the pasted cells
92:          */
93:         public GridPasteKeyListener(Display display, VControl vControl, EMFFormsDatabindingEMF dataBinding,
94:                 EStructuralFeatureValueConverterService converterService, EMFFormsLocalizationService localizationService,
95:                 boolean selectPastedCells) {
96:                 this.display = display;
97:                 this.localizationService = localizationService;
98:                 clipboard = new Clipboard(display);
99:                 this.vControl = vControl;
100:                 this.dataBinding = dataBinding;
101:                 this.converterService = converterService;
102:                 this.selectPastedCells = selectPastedCells;
103:
104:                 final BundleContext bundleContext = FrameworkUtil
105:                         .getBundle(getClass())
106:                         .getBundleContext();
107:
108:                 final ServiceReference<PreSetValidationService> serviceReference = bundleContext
109:                         .getServiceReference(PreSetValidationService.class);
110:
111:•                preSetValidationService = serviceReference != null ? bundleContext.getService(serviceReference) : null;
112:         }
113:
114:         @Override
115:         public void keyPressed(KeyEvent e) {
116:•                if ((e.stateMask & SWT.CTRL) != 0 && e.keyCode == 'v') {
117:•                        if (!alreadyPasted) {
118:                                 final Grid grid = (Grid) e.widget;
119:                                 final Object contents = clipboard.getContents(TextTransfer.getInstance());
120:•                                if (contents instanceof String) {
121:                                         pasteSelection(grid, (String) contents);
122:                                 }
123:                                 alreadyPasted = true;
124:                         }
125:                 } else {
126:                         alreadyPasted = false;
127:                 }
128:         }
129:
130:         @Override
131:         public void keyReleased(KeyEvent e) {
132:                 /* no op */
133:         }
134:
135:         /**
136:          * Pastes the given contents in the grid.
137:          *
138:          * @param grid the target {@link Grid}
139:          * @param contents the contents to paste
140:          */
141:         public void pasteSelection(Grid grid, String contents) {
142:
143:•                if (grid.getCellSelection().length == 0 || !getControl().isEffectivelyEnabled()
144:•                        || getControl().isEffectivelyReadonly()) {
145:                         return;
146:                 }
147:
148:                 final List<Point> pastedCells = new ArrayList<Point>();
149:•                if (grid.getCellSelection().length > 1 /* more than one cell selected */ &&
150:•                        new StringTokenizer(contents, NT, false).countTokens() == 1 /* contents are on one line */ &&
151:•                        fillSelectionWithMultipleCopies(grid.getCellSelection(), contents)) {
152:
153:                         // fill selection
154:•                        for (final Point startItem : getFillStartPoints(grid.getCellSelection())) {
155:                                 pastedCells.addAll(pasteContents(startItem, grid, contents));
156:                         }
157:
158:                 } else {
159:
160:                         // expand selection
161:                         final Point startItem = grid.getCellSelection()[0];
162:                         pastedCells.addAll(pasteContents(startItem, grid, contents));
163:                 }
164:
165:•                if (selectPastedCells && !pastedCells.isEmpty() && grid.isCellSelectionEnabled()) {
166:                         grid.setCellSelection(pastedCells.toArray(new Point[] {}));
167:                 }
168:
169:         }
170:
171:         /**
172:          * Extract the start points for filling paste from the grid's cell selection.
173:          *
174:          * @param cellSelection the cell selection
175:          * @return the start points
176:          */
177:         static Point[] getFillStartPoints(Point[] cellSelection) {
178:                 final Map<Integer, Set<Integer>> rowToSelectedColumns = createSelectionMap(cellSelection);
179:                 final Point[] result = new Point[rowToSelectedColumns.size()];
180:
181:                 final Set<Integer> columns = rowToSelectedColumns.values().iterator().next();
182:                 final int startColumn = Collections.min(columns);
183:
184:                 int i = 0;
185:•                for (final Integer startRow : rowToSelectedColumns.keySet()) {
186:                         result[i++] = new Point(startColumn, startRow);
187:                 }
188:
189:                 return result;
190:         }
191:
192:         /**
193:          * Whether the paste logic which will paste the same contents in every selected row is applicable.
194:          * Precondition is that multiple cells are selected and contents does not contain a new line.
195:          *
196:          * @param cellSelection the selected cells
197:          * @param contents the contents to paste
198:          * @return <code>true</code> if paste should happen, <code>false</code> otherwise
199:          */
200:         static boolean fillSelectionWithMultipleCopies(Point[] cellSelection, String contents) {
201:                 /* build up data struc to analyse selection */
202:                 final Map<Integer, Set<Integer>> rowToSelectedColumns = createSelectionMap(cellSelection);
203:
204:                 /* multiple rows have to be selected */
205:•                if (rowToSelectedColumns.size() < 2) {
206:                         return false;
207:                 }
208:
209:                 /* column selection has to be uniform */
210:                 final Iterator<Set<Integer>> columnSetInterator = rowToSelectedColumns.values().iterator();
211:                 final Set<Integer> referenceSet = columnSetInterator.next();
212:•                while (columnSetInterator.hasNext()) {
213:                         final Set<Integer> next = columnSetInterator.next();
214:•                        if (!referenceSet.equals(next)) {
215:                                 return false;
216:                         }
217:                 }
218:
219:                 /* if only one column selected, we are fine */
220:•                if (referenceSet.size() == 1) {
221:                         return true;
222:                 }
223:
224:                 /* otherwise selected column count and pasted column count has to match */
225:•                if (contents.split(TAB).length != referenceSet.size()) {
226:                         return false;
227:                 }
228:
229:                 /* and selected columns have to lie next to each other */
230:                 final ArrayList<Integer> selectedColumnIndices = new ArrayList<Integer>(referenceSet);
231:                 Collections.sort(selectedColumnIndices);
232:                 final Iterator<Integer> selectedColumnIndicesIterator = selectedColumnIndices.iterator();
233:                 Integer ref = selectedColumnIndicesIterator.next();
234:•                while (selectedColumnIndicesIterator.hasNext()) {
235:•                        if (++ref != selectedColumnIndicesIterator.next()) {
236:                                 return false;
237:                         }
238:                 }
239:
240:                 /* all fine */
241:                 return true;
242:         }
243:
244:         private static Map<Integer, Set<Integer>> createSelectionMap(Point[] cellSelection) {
245:                 final Map<Integer, Set<Integer>> rowToSelectedColumns = new LinkedHashMap<Integer, Set<Integer>>();
246:•                for (final Point point : cellSelection) {
247:•                        if (!rowToSelectedColumns.containsKey(point.y)) {
248:                                 rowToSelectedColumns.put(point.y, new LinkedHashSet<Integer>());
249:                         }
250:                         rowToSelectedColumns.get(point.y).add(point.x);
251:                 }
252:                 return rowToSelectedColumns;
253:         }
254:
255:         /**
256:          * Performs the paste operation.
257:          *
258:          * @param startItem the start item
259:          * @param grid the grid
260:          * @param contents the pasted contents
261:          * @return the pasted cells
262:          */
263:         // BEGIN COMPLEX CODE
264:         @SuppressWarnings("restriction")
265:         public List<Point> pasteContents(Point startItem, Grid grid, String contents) {
266:                 final int startColumn = startItem.x;
267:                 final int startRow = startItem.y;
268:
269:                 final List<Point> pastedCells = new ArrayList<Point>();
270:                 final List<String> invalidValues = new ArrayList<String>();
271:                 int relativeRow = 0;
272:                 final String[] rows = contents.split("\r\n|\n", -1); //$NON-NLS-1$
273:
274:                 prePasteContents();
275:
276:                 try {
277:•                        for (final String row : rows) {
278:
279:                                 int relativeColumn = 0;
280:
281:•                                for (final String cellValueSplit : row.split(TAB, -1)) {
282:
283:                                         final String cellValue = modifyCellValue(cellValueSplit);
284:
285:                                         final int insertionColumnIndex = startColumn + relativeColumn;
286:                                         final int insertionRowIndex = startRow + relativeRow;
287:
288:•                                        if (insertionColumnIndex >= grid.getColumnCount()) {
289:                                                 relativeColumn++;
290:                                                 continue;
291:                                         }
292:
293:                                         final VDomainModelReference dmr = (VDomainModelReference) grid.getColumn(insertionColumnIndex)
294:                                                 .getData(TableConfiguration.DMR);
295:
296:•                                        if (dmr == null || getControl() instanceof VTableControl
297:                                                 && org.eclipse.emf.ecp.view.internal.table.swt.TableConfigurationHelper
298:•                                                        .isReadOnly((VTableControl) getControl(), dmr)) {
299:                                                 relativeColumn++;
300:                                                 continue;
301:                                         }
302:
303:•                                        if (insertionRowIndex < grid.getItemCount()) {
304:
305:                                                 final EObject eObject = (EObject) grid.getItem(insertionRowIndex).getData();
306:
307:•                                                if (isEObjectReadOnly(eObject)) {
308:                                                         continue;
309:                                                 }
310:
311:                                                 IObservableValue value = null;
312:                                                 try {
313:                                                         value = dataBinding.getObservableValue(dmr, eObject);
314:                                                         final EStructuralFeature feature = (EStructuralFeature) value.getValueType();
315:                                                         final Object convertedValue = getConverterService().convertToModelValue(eObject,
316:                                                                 feature, cellValue);
317:
318:•                                                        if (isSettingReadOnly(eObject, feature, convertedValue)) {
319:                                                                 continue;
320:                                                         }
321:
322:•                                                        boolean valid = convertedValue != null;
323:
324:•                                                        if (preSetValidationService != null) {
325:                                                                 final Map<Object, Object> context = new LinkedHashMap<Object, Object>();
326:                                                                 context.put("rootEObject", IObserving.class.cast(value).getObserved());//$NON-NLS-1$
327:                                                                 final Diagnostic diag = preSetValidationService.validate(
328:•                                                                        feature, valid ? convertedValue : cellValue, context);
329:•                                                                valid = diag.getSeverity() == Diagnostic.OK;
330:•                                                                if (!valid) {
331:                                                                         invalidValues.add(extractDiagnosticMessage(diag, feature, cellValue));
332:                                                                 }
333:                                                         }
334:
335:                                                         final EObject observedEobject = (EObject) ((IObserving) value).getObserved();
336:                                                         final Setting setting = ((InternalEObject) observedEobject).eSetting(feature);
337:•                                                        if (!canBePasted(feature, cellValue, eObject, setting)) {
338:                                                                 invalidValues.add(cellValue);
339:•                                                        } else if (valid) {
340:                                                                 setValue(value, convertedValue);
341:                                                                 pastedCells.add(new Point(insertionColumnIndex, insertionRowIndex));
342:                                                         }
343:                                                 }
344:                                                 // BEGIN SUPRESS CATCH EXCEPTION
345:                                                 catch (final Exception ex) {// END SUPRESS CATCH EXCEPTION
346:                                                         // silently ignore this
347:                                                 } finally {
348:•                                                        if (value != null) {
349:                                                                 value.dispose();
350:                                                         }
351:                                                 }
352:
353:                                         }
354:                                         relativeColumn++;
355:                                 }
356:                                 relativeRow++;
357:                         }
358:                 } finally {
359:                         postPasteContents();
360:                 }
361:
362:                 showErrors(invalidValues);
363:
364:                 return pastedCells;
365:         }
366:         // END COMPLEX CODE
367:
368:         /**
369:          * This method gets called by {@link #pasteContents(Point, Grid, String)} before it will start to loop over the to
370:          * be pasted values and begins the pasting.
371:          * Clients may use this to notify the user about the paste, setting global variables, showing progress, etc.
372:          */
373:         protected void prePasteContents() {
374:                 /* default implementation does nothing */
375:         }
376:
377:         /**
378:          * This method get called by {@link #pasteContents(Point, Grid, String)} directely after the paste is done but
379:          * before {@link #showErrors(List)} is called. Please note that the call comes from a finally block meaning that the
380:          * paste process may have been stoped by an unhandled exception beforehand.
381:          * Clients may use this clean up things done in {@link #prePasteContents()} for example.
382:          */
383:         protected void postPasteContents() {
384:                 /* default implementation does nothing */
385:         }
386:
387:         /**
388:          * This method is called by {@link #pasteContents(Point, Grid, String)} with the determined value for a cell.
389:          * Clients may override this method in order to trim the string or change its formatting.
390:          *
391:          * @param cellValueSplit the cell value determined from the clipboard for the cell.
392:          * @return the string to use
393:          */
394:         protected String modifyCellValue(String cellValueSplit) {
395:                 return cellValueSplit;
396:         }
397:
398:         /**
399:          * Called by {@link #pasteContents(Point, Grid, String)} to determine whether the values of an EObject should be
400:          * changed at all.
401:          *
402:          * @param eObject the EObject in question
403:          * @return <code>true</code> if paste for this object should be skipped, <code>false</code> otherwise
404:          */
405:         protected boolean isEObjectReadOnly(EObject eObject) {
406:                 return false;
407:         }
408:
409:         /**
410:          * Called by {@link #pasteContents(Point, Grid, String)} to determine whether a setting should be changed at all.
411:          *
412:          * @param eObject the EObject in question
413:          * @param feature the Feature in question
414:          * @param convertedValue the converted cell value
415:          * @return <code>true</code> if paste for this setting should be skipped, <code>false</code> otherwise
416:          */
417:         protected boolean isSettingReadOnly(EObject eObject, EStructuralFeature feature, Object convertedValue) {
418:                 return false;
419:         }
420:
421:         /**
422:          * Shows the errors to the user.
423:          *
424:          * @param msgs the collected messages, may be empty if no errors
425:          */
426:         protected void showErrors(List<String> msgs) {
427:•                if (!msgs.isEmpty()) {
428:                         showDialog(
429:                                 display.getActiveShell(),
430:                                 localizationService.getString(FrameworkUtil.getBundle(getClass()), "InvalidPaste.Title"), //$NON-NLS-1$
431:                                 localizationService.getString(FrameworkUtil.getBundle(getClass()), "InvalidPaste.Message"), //$NON-NLS-1$
432:                                 msgs);
433:                 }
434:         }
435:
436:         /**
437:          * Checks whether a given value may be pasted.
438:          *
439:          * @param feature the feature of the {@code eObject}
440:          * @param cellValue the cell value to be pasted
441:          * @param eObject the parent {@link EObject}
442:          * @param setting {@link Setting}
443:          * @return {@code true}, if the value may be pasted, {@code false} otherwise
444:          */
445:         protected boolean canBePasted(EStructuralFeature feature, String cellValue,
446:                 EObject eObject, Setting setting) {
447:
448:•                if (!EEnum.class.isInstance(feature.getEType())) {
449:                         return true;
450:                 }
451:
452:                 final EEnum eEnum = (EEnum) feature.getEType();
453:•                for (final EEnumLiteral literal : eEnum.getELiterals()) {
454:                         final String isInputtable = EcoreUtil.getAnnotation(literal, VViewPackage.NS_URI_170,
455:                                 IS_INPUTTABLE);
456:
457:•                        if (literal.getLiteral().equals(cellValue) && isInputtable != null) {
458:                                 return Boolean.getBoolean(isInputtable);
459:                         }
460:                 }
461:
462:                 return true;
463:         }
464:
465:         /**
466:          * Sets the given converted value on the observable value.
467:          *
468:          * @param value the observable value
469:          * @param convertedValue the converted value
470:          */
471:         protected void setValue(IObservableValue value, final Object convertedValue) {
472:                 value.setValue(convertedValue);
473:         }
474:
475:         /**
476:          * Creates the message for the given {@link Diagnostic} which will be displayed to the user.
477:          *
478:          * @param diag the diagnostic with the original error message
479:          * @param feature the validated feature
480:          * @param value the validated value
481:          * @return the display string
482:          */
483:         protected String extractDiagnosticMessage(Diagnostic diag, EStructuralFeature feature, String value) {
484:                 return diag.getChildren().get(0).getMessage();
485:         }
486:
487:         private static void showDialog(Shell shell, String title, String msg, List<String> warnings) {
488:                 final StringBuilder builder = new StringBuilder();
489:                 builder.append(msg);
490:•                for (final String warning : warnings) {
491:                         builder.append("- " + warning) //$NON-NLS-1$
492:                                 .append("\n"); //$NON-NLS-1$
493:                 }
494:
495:                 MessageDialog.openWarning(shell, title, builder.toString());
496:         }
497:
498:         /**
499:          *
500:          * @return the {@link EStructuralFeatureValueConverterService}
501:          */
502:         protected EStructuralFeatureValueConverterService getConverterService() {
503:                 return converterService;
504:         }
505:
506:         /**
507:          * @return the {@link VControl}
508:          */
509:         protected VControl getControl() {
510:                 return vControl;
511:         }
512: }