Skip to content

Package: ViewModelFileExtensionsManager$1

ViewModelFileExtensionsManager$1

nameinstructionbranchcomplexitylinemethod
localize(String)
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
{...}
M: 0 C: 9
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%

Coverage

1: /*******************************************************************************
2: * Copyright (c) 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: * Jonas - initial API and implementation
13: * Christian W. Damus - bug 547787
14: ******************************************************************************/
15: package org.eclipse.emf.ecp.view.model.provider.xmi;
16:
17: import java.io.File;
18: import java.io.FileOutputStream;
19: import java.io.IOException;
20: import java.io.InputStream;
21: import java.io.OutputStream;
22: import java.text.MessageFormat;
23: import java.util.ArrayList;
24: import java.util.Collection;
25: import java.util.Collections;
26: import java.util.HashSet;
27: import java.util.LinkedHashMap;
28: import java.util.LinkedHashSet;
29: import java.util.List;
30: import java.util.Map;
31: import java.util.Map.Entry;
32: import java.util.Set;
33:
34: import org.eclipse.core.runtime.IConfigurationElement;
35: import org.eclipse.core.runtime.IStatus;
36: import org.eclipse.core.runtime.Platform;
37: import org.eclipse.emf.common.util.EList;
38: import org.eclipse.emf.common.util.TreeIterator;
39: import org.eclipse.emf.common.util.URI;
40: import org.eclipse.emf.common.util.WrappedException;
41: import org.eclipse.emf.ecore.EClass;
42: import org.eclipse.emf.ecore.EObject;
43: import org.eclipse.emf.ecore.resource.Resource;
44: import org.eclipse.emf.ecore.resource.ResourceSet;
45: import org.eclipse.emf.ecore.resource.URIConverter;
46: import org.eclipse.emf.ecore.resource.impl.ExtensibleURIConverterImpl;
47: import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
48: import org.eclipse.emf.ecore.util.EcoreUtil;
49: import org.eclipse.emf.ecore.xmi.XMLResource;
50: import org.eclipse.emf.ecp.internal.view.model.provider.xmi.Activator;
51: import org.eclipse.emf.ecp.view.migrator.ViewModelMigrationException;
52: import org.eclipse.emf.ecp.view.migrator.ViewModelMigrator;
53: import org.eclipse.emf.ecp.view.migrator.ViewModelMigratorUtil;
54: import org.eclipse.emf.ecp.view.spi.model.LocalizationAdapter;
55: import org.eclipse.emf.ecp.view.spi.model.VElement;
56: import org.eclipse.emf.ecp.view.spi.model.VView;
57: import org.eclipse.emf.ecp.view.spi.model.VViewFactory;
58: import org.eclipse.emf.ecp.view.spi.model.VViewModelProperties;
59: import org.eclipse.emf.ecp.view.spi.model.VViewPackage;
60: import org.eclipse.emf.ecp.view.spi.model.util.VViewResourceFactoryImpl;
61: import org.eclipse.emf.ecp.view.spi.model.util.VViewResourceImpl;
62: import org.eclipse.emfforms.spi.common.report.AbstractReport;
63: import org.eclipse.emfforms.spi.common.report.ReportService;
64: import org.eclipse.emfforms.spi.localization.LocalizationServiceHelper;
65:
66: /**
67: * Manages the view models provided by the file extension point.
68: *
69: * @author Jonas Helming
70: *
71: *
72: */
73: public final class ViewModelFileExtensionsManager {
74:
75:         private static final String FILTER_VALUE_ATTRIBUTE = "value"; //$NON-NLS-1$
76:         private static final String FILTER_KEY_ATTRIBUTE = "key"; //$NON-NLS-1$
77:         private static final String FILTER_ELEMENT = "filter"; //$NON-NLS-1$
78:         private static final String FILE_EXTENSION = "org.eclipse.emf.ecp.view.model.provider.xmi.file"; //$NON-NLS-1$
79:         private static final String FILEPATH_ATTRIBUTE = "filePath"; //$NON-NLS-1$
80:
81:         private static final Map<Object, Object> LOAD_OPTIONS = new LinkedHashMap<Object, Object>();
82:         {
83:                 LOAD_OPTIONS.put(URIConverter.OPTION_TIMEOUT, 1);
84:                 LOAD_OPTIONS.put(XMLResource.OPTION_DEFER_ATTACHMENT, Boolean.TRUE);
85:                 LOAD_OPTIONS.put(XMLResource.OPTION_DEFER_IDREF_RESOLUTION, Boolean.TRUE);
86:                 LOAD_OPTIONS.put(XMLResource.OPTION_USE_DEPRECATED_METHODS, Boolean.TRUE);
87:         }
88:
89:         private final Map<EClass, Map<VView, Set<ExtensionDescription>>> map = new LinkedHashMap<EClass, Map<VView, Set<ExtensionDescription>>>();
90:         private static File viewModelFolder;
91:
92:         private ViewModelFileExtensionsManager() {
93:         }
94:
95:         private static ViewModelFileExtensionsManager instance;
96:
97:         /**
98:          * @return the iNSTANCE
99:          */
100:         public static synchronized ViewModelFileExtensionsManager getInstance() {
101:                 if (instance == null) {
102:                         instance = new ViewModelFileExtensionsManager();
103:                         instance.init();
104:                 }
105:                 return instance;
106:         }
107:
108:         private void init() {
109:                 final Map<URI, List<ExtensionDescription>> extensionURIS = getExtensionURIS();
110:                 for (final URI uri : extensionURIS.keySet()) {
111:                         /* load resource */
112:                         final VViewResourceImpl resource = loadResource(uri);
113:                         final EList<EObject> contents = resource.getContents();
114:
115:                         if (contents.size() == 0) {
116:                                 continue;
117:                         }
118:
119:                         /* check if content is a view model */
120:                         final EObject eObject = contents.get(0);
121:                         if (!(eObject instanceof VView)) {
122:                                 final ReportService reportService = Activator.getReportService();
123:                                 if (reportService != null) {
124:                                         reportService.report(new AbstractReport(String.format(
125:                                                 "The registered file '%1$s' doesn't point to a serialized view model.", uri.toString()))); //$NON-NLS-1$
126:                                 }
127:                                 continue;
128:                         }
129:                         final VView view = (VView) eObject;
130:
131:                         /* check if view model is valid */
132:                         if (view.getRootEClass() == null) {
133:                                 final ReportService reportService = Activator.getReportService();
134:                                 if (reportService != null) {
135:                                         reportService.report(new AbstractReport(String
136:                                                 .format("The registered view in file '%1$s' doesn't have a set root eclass.", uri.toString()))); //$NON-NLS-1$
137:                                 }
138:                                 continue;
139:                         }
140:
141:                         /* set uuid on view model */
142:                         setUUIDAsElementId(resource, view);
143:
144:                         /* register view */
145:                         for (final ExtensionDescription extensionDescription : extensionURIS.get(uri)) {
146:                                 registerView(view, extensionDescription);
147:                         }
148:                 }
149:
150:         }
151:
152:         /**
153:          * Sets the UUID mapping from the given resource as the {@link VElement#getUuid() element id} of all elements in the
154:          * given view model.
155:          *
156:          * @param resource the {@link VViewResourceImpl}-
157:          * @param view the view
158:          */
159:         public static void setUUIDAsElementId(final VViewResourceImpl resource, final VView view) {
160:                 view.setUuid(resource.getID(view));
161:                 final TreeIterator<EObject> allContents = view.eAllContents();
162:                 while (allContents.hasNext()) {
163:                         final EObject next = allContents.next();
164:                         if (!VElement.class.isInstance(next)) {
165:                                 continue;
166:                         }
167:                         VElement.class.cast(next).setUuid(resource.getID(next));
168:                 }
169:         }
170:
171:         /**
172:          * This registers a view.
173:          *
174:          * @param view The View to register
175:          * @param extensionDescription Additional information like the filters used when identifying the correct view for
176:          * the request
177:          */
178:         void registerView(final VView view, final ExtensionDescription extensionDescription) {
179:                 if (!map.containsKey(view.getRootEClass())) {
180:                         map.put(view.getRootEClass(), new LinkedHashMap<VView, Set<ExtensionDescription>>());
181:                 }
182:                 final Map<VView, Set<ExtensionDescription>> viewDescriptionMap = map.get(view.getRootEClass());
183:                 if (!viewDescriptionMap.containsKey(view)) {
184:                         viewDescriptionMap.put(view, new LinkedHashSet<ViewModelFileExtensionsManager.ExtensionDescription>());
185:                 }
186:                 viewDescriptionMap.get(view).add(extensionDescription);
187:         }
188:
189:         /**
190:          * Loads a resource containing a view model.
191:          *
192:          * @param uri a URI containing the path to the file
193:          * @return the loaded resource
194:          */
195:         public static VViewResourceImpl loadResource(URI uri) {
196:
197:                 final ViewModelMigrator viewModelMigrator = ViewModelMigratorUtil.getViewModelMigrator();
198:                 if (viewModelMigrator != null) {
199:                         uri = migrateViewModelIfNecesarry(viewModelMigrator, uri);
200:                 }
201:
202:                 final ResourceSet resourceSet = new ResourceSetImpl();
203:                 resourceSet.getLoadOptions().putAll(LOAD_OPTIONS);
204:                 final Map<String, Object> extensionToFactoryMap = resourceSet
205:                         .getResourceFactoryRegistry().getExtensionToFactoryMap();
206:                 extensionToFactoryMap.put(Resource.Factory.Registry.DEFAULT_EXTENSION,
207:                         new VViewResourceFactoryImpl());
208:                 resourceSet.getPackageRegistry().put(VViewPackage.eNS_URI, VViewPackage.eINSTANCE);
209:                 VViewResourceImpl resource;
210:                 try {
211:                         resource = (VViewResourceImpl) resourceSet.getResource(uri, true);
212:                 } catch (final WrappedException exception) {
213:                         final ReportService reportService = Activator.getReportService();
214:                         if (reportService != null) {
215:                                 reportService.report(new AbstractReport(exception,
216:                                         "Loading view model failed. Maybe a migration is needed. Please take a look at the migration guide at: http://www.eclipse.org/ecp/emfforms/documentation.html")); //$NON-NLS-1$
217:                         }
218:                         resource = (VViewResourceImpl) resourceSet.createResource(uri);
219:                 }
220:                 return resource;
221:         }
222:
223:         private static URI migrateViewModelIfNecesarry(ViewModelMigrator viewModelMigrator, URI uri) {
224:                 final ReportService reportService = Activator.getReportService();
225:                 try {
226:                         /* copy file from jar to disk */
227:                         final File dest = getFileDestination();
228:                         if (dest == null) {
229:                                 return uri;
230:                         }
231:                         final URIConverter uriConverter = new ExtensibleURIConverterImpl();
232:                         final InputStream inputStream = uriConverter.createInputStream(uri, LOAD_OPTIONS);
233:                         copy(inputStream, dest);
234:                         uri = URI.createFileURI(dest.getAbsolutePath());
235:
236:                         /*
237:                          * parse file and check if migration is needed. Because of limitations in edapt, the passed uri cannot be a
238:                          * platform plugin uri. I opened a BR.
239:                          * Otherwise we could delay the file copy process until we know that a migration is actually needed.
240:                          */
241:                         if (viewModelMigrator.checkMigration(uri)) {
242:                                 return uri;
243:                         }
244:                         if (reportService != null) {
245:                                 reportService.report(new AbstractReport(
246:                                         MessageFormat.format(
247:                                                 "The view model at {0} needs migration. Please take a look at the migration guide at: http://www.eclipse.org/ecp/emfforms/documentation.html", //$NON-NLS-1$
248:                                                 uri.toString()),
249:                                         IStatus.WARNING));
250:                         }
251:
252:                         viewModelMigrator.performMigration(uri);
253:                         return uri;
254:
255:                 } catch (final IOException ex) {
256:                         if (reportService != null) {
257:                                 reportService.report(new AbstractReport(ex,
258:                                         MessageFormat.format("The migration of view model at {0} failed.", uri.toString()))); //$NON-NLS-1$
259:                         }
260:                 } catch (final ViewModelMigrationException ex) {
261:                         if (reportService != null) {
262:                                 reportService.report(new AbstractReport(ex,
263:                                         MessageFormat.format("The migration of view model at {0} failed.", uri.toString()))); //$NON-NLS-1$
264:                         }
265:                 }
266:                 return uri;
267:         }
268:
269:         /**
270:          * Returns a new file where a view model may be exported to.
271:          *
272:          * @return the file location
273:          */
274:         private static synchronized File getFileDestination() {
275:                 if (viewModelFolder == null) {
276:                         final File stateLocation = Activator.getInstance().getStateLocation().toFile();
277:                         viewModelFolder = new File(stateLocation, "views"); //$NON-NLS-1$
278:                         if (!viewModelFolder.exists()) {
279:                                 viewModelFolder.mkdir();
280:                         }
281:                         for (final File file : viewModelFolder.listFiles()) {
282:                                 file.delete();
283:                         }
284:                 }
285:                 final File file = new File(viewModelFolder, System.currentTimeMillis() + ".view"); //$NON-NLS-1$
286:                 file.deleteOnExit();
287:                 return file;
288:         }
289:
290:         private static void copy(InputStream in, File file) throws IOException {
291:                 final OutputStream out = new FileOutputStream(file);
292:                 final byte[] buf = new byte[1024];
293:                 int len;
294:                 while ((len = in.read(buf)) > 0) {
295:                         out.write(buf, 0, len);
296:                 }
297:                 out.close();
298:                 in.close();
299:         }
300:
301:         /**
302:          * Disposed the instance.
303:          */
304:         public static void dispose() {
305:                 instance = null;
306:         }
307:
308:         /**
309:          *
310:          * @return a list of uris of all xmi files registered
311:          */
312:         public static Map<URI, List<ExtensionDescription>> getExtensionURIS() {
313:                 final Map<URI, List<ExtensionDescription>> ret = new LinkedHashMap<URI, List<ExtensionDescription>>();
314:                 final IConfigurationElement[] files = Platform.getExtensionRegistry().getConfigurationElementsFor(
315:                         FILE_EXTENSION);
316:                 final URIConverter converter = new ResourceSetImpl().getURIConverter();
317:                 for (final IConfigurationElement file : files) {
318:                         final String bundleId = file.getContributor().getName();
319:                         final String filePath = file.getAttribute(FILEPATH_ATTRIBUTE);
320:
321:                         final IConfigurationElement[] children = file.getChildren(FILTER_ELEMENT);
322:                         final Map<String, String> keyValuePairs = new LinkedHashMap<String, String>();
323:                         for (final IConfigurationElement child : children) {
324:                                 final String key = child.getAttribute(FILTER_KEY_ATTRIBUTE);
325:                                 final String value = child.getAttribute(FILTER_VALUE_ATTRIBUTE);
326:                                 keyValuePairs.put(key, value);
327:                         }
328:
329:                         URI uri;
330:                         final String bundleName = file.getContributor().getName();
331:                         final String path = bundleName + '/' + filePath;
332:                         uri = URI.createPlatformPluginURI(path, false);
333:                         if (!converter.exists(uri, LOAD_OPTIONS)) {
334:                                 uri = URI.createPlatformResourceURI(filePath, false);
335:                                 if (!converter.exists(uri, LOAD_OPTIONS)) {
336:                                         final ReportService reportService = Activator.getReportService();
337:                                         if (reportService != null) {
338:                                                 reportService.report(new AbstractReport(
339:                                                         String.format("The provided uri '%1$s' doesn't point to an existing file.", uri.toString()), //$NON-NLS-1$
340:                                                         IStatus.ERROR));
341:                                                 continue;
342:                                         }
343:                                 }
344:                         }
345:                         if (!ret.containsKey(uri)) {
346:                                 ret.put(uri, new ArrayList<ViewModelFileExtensionsManager.ExtensionDescription>());
347:                         }
348:                         ret.get(uri).add(new ExtensionDescription(keyValuePairs, bundleId));
349:                 }
350:                 return ret;
351:         }
352:
353:         /**
354:          * @param eObject the object to be rendered
355:          * @param properties the {@link VViewModelProperties properties}
356:          * @param requiredKeys the filter keys that are required to match
357:          * @return if there is a xmi file registered containing a view model for the given type
358:          */
359:         public boolean hasViewModelFor(EObject eObject, VViewModelProperties properties, Collection<String> requiredKeys) {
360:                 return !findBestFittingViews(eObject, properties, requiredKeys).isEmpty();
361:         }
362:
363:         /**
364:          * @param eObject The {@link EObject} to create a view for
365:          * @param properties the {@link VViewModelProperties properties}
366:          * @param requiredKeys the filter keys that are required to match
367:          * @return a view model for the given eObject
368:          */
369:         public VView createView(EObject eObject, VViewModelProperties properties, Collection<String> requiredKeys) {
370:                 final Map<VView, ExtensionDescription> bestFitting = findBestFittingViews(eObject, properties, requiredKeys);
371:
372:                 if (bestFitting.isEmpty()) {
373:                         final ReportService reportService = Activator.getReportService();
374:                         if (reportService != null) {
375:                                 reportService.report(new AbstractReport(
376:                                         "No view models have been found for the given View Model Loading Properties. This should have not been called!", //$NON-NLS-1$
377:                                         IStatus.ERROR));
378:                         }
379:                         return null;
380:                 }
381:
382:                 if (bestFitting.size() != 1) {
383:                         final ReportService reportService = Activator.getReportService();
384:                         if (reportService != null) {
385:                                 reportService.report(new AbstractReport(
386:                                         "Multiple view models have been found for the given View Model Loading Properties.", //$NON-NLS-1$
387:                                         IStatus.WARNING));
388:                         }
389:                 }
390:
391:                 final Entry<VView, ExtensionDescription> entry = bestFitting.entrySet().iterator().next();
392:
393:                 final VView copiedView = EcoreUtil.copy(entry.getKey());
394:                 final String bundleId = entry.getValue().getBundleId();
395:                 copiedView.eAdapters().add(new LocalizationAdapter() {
396:
397:                         @Override
398:                         public String localize(String key) {
399:                                 return LocalizationServiceHelper.getString(Platform.getBundle(bundleId),
400:                                         key);
401:                         }
402:                 });
403:                 copiedView.setLoadingProperties(EcoreUtil.copy(properties));
404:                 return copiedView;
405:         }
406:
407:         private static final int FILTER_NOT_MATCHED = Integer.MIN_VALUE;
408:
409:         private Map<VView, ExtensionDescription> findBestFittingViews(EObject eObject,
410:                 final VViewModelProperties properties, Collection<String> requiredKeys) {
411:
412:                 final Map<VView, Set<ExtensionDescription>> viewMap = new LinkedHashMap<VView, Set<ExtensionDescription>>();
413:                 final Set<EClass> allEClass = new LinkedHashSet<EClass>();
414:                 allEClass.add(eObject.eClass());
415:                 allEClass.addAll(eObject.eClass().getEAllSuperTypes());
416:                 for (final EClass eClass : allEClass) {
417:                         final Map<VView, Set<ExtensionDescription>> classMap = map.get(eClass);
418:                         if (classMap != null) {
419:                                 viewMap.putAll(classMap);
420:                         }
421:                 }
422:
423:                 final Map<VView, ExtensionDescription> bestFitting = new LinkedHashMap<VView, ViewModelFileExtensionsManager.ExtensionDescription>();
424:                 int maxNumberFittingKeyValues = -1;
425:                 VViewModelProperties propertiesToCheck = properties;
426:                 if (propertiesToCheck == null) {
427:                         propertiesToCheck = VViewFactory.eINSTANCE.createViewModelLoadingProperties();
428:                 }
429:                 for (final VView view : viewMap.keySet()) {
430:                         for (final ExtensionDescription description : viewMap.get(view)) {
431:                                 final int currentFittingKeyValues = computeFit(description, propertiesToCheck, requiredKeys);
432:                                 if (currentFittingKeyValues == FILTER_NOT_MATCHED) {
433:                                         continue;
434:                                 }
435:                                 if (currentFittingKeyValues > maxNumberFittingKeyValues) {
436:                                         maxNumberFittingKeyValues = currentFittingKeyValues;
437:                                         bestFitting.clear();
438:                                         bestFitting.put(view, description);
439:                                 } else if (currentFittingKeyValues == maxNumberFittingKeyValues) {
440:                                         bestFitting.put(view, description);
441:                                 }
442:                         }
443:                 }
444:                 return getViewMap(bestFitting, eObject.eClass());
445:         }
446:
447:         private int computeFit(final ExtensionDescription description, VViewModelProperties properties,
448:                 Collection<String> requiredKeys) {
449:
450:                 int currentFittingKeyValues = 0;
451:                 final Set<String> unmatchedKeys = requiredKeys == null || requiredKeys.isEmpty()
452:                         ? Collections.emptySet()
453:                         : new HashSet<>(requiredKeys);
454:
455:                 final Map<String, String> viewFilter = description.getKeyValuPairs();
456:                 for (final String viewFilterKey : viewFilter.keySet()) {
457:                         if (properties.containsKey(viewFilterKey)) {
458:                                 final Object contextValue = properties.get(viewFilterKey);
459:                                 final String viewFilterValue = viewFilter.get(viewFilterKey);
460:                                 if (contextValue.toString().equalsIgnoreCase(viewFilterValue)) {
461:                                         currentFittingKeyValues++;
462:                                         unmatchedKeys.remove(viewFilterKey);
463:                                 } else {
464:                                         currentFittingKeyValues = FILTER_NOT_MATCHED;
465:                                         break;
466:                                 }
467:                         } else {
468:                                 currentFittingKeyValues = FILTER_NOT_MATCHED;
469:                                 break;
470:                         }
471:                 }
472:                 if (!unmatchedKeys.isEmpty()) {
473:                         currentFittingKeyValues = FILTER_NOT_MATCHED;
474:                 }
475:                 return currentFittingKeyValues;
476:         }
477:
478:         private Map<VView, ExtensionDescription> getViewMap(Map<VView, ExtensionDescription> fullMap, EClass viewModelFor) {
479:                 final Map<VView, ExtensionDescription> viewMap = new LinkedHashMap<VView, ExtensionDescription>();
480:
481:                 final Set<EClass> checkedEClasses = new LinkedHashSet<EClass>();
482:                 Set<EClass> eClassesToGetViewModelsFor = new LinkedHashSet<EClass>();
483:                 eClassesToGetViewModelsFor.add(viewModelFor);
484:
485:                 while (!eClassesToGetViewModelsFor.isEmpty()) {
486:                         /* loop over all current eClasses and add view models for the current eClass to the map */
487:                         for (final EClass eClass : eClassesToGetViewModelsFor) {
488:                                 final Map<VView, ExtensionDescription> classMap = new LinkedHashMap<VView, ExtensionDescription>();
489:                                 for (final VView vView : fullMap.keySet()) {
490:                                         if (eClass == vView.getRootEClass()) {
491:                                                 classMap.put(vView, fullMap.get(vView));
492:                                         }
493:                                 }
494:                                 viewMap.putAll(classMap);
495:                                 checkedEClasses.add(eClass);
496:                         }
497:
498:                         /* if we found some views we are ready to return the map */
499:                         if (!viewMap.isEmpty()) {
500:                                 eClassesToGetViewModelsFor.clear();
501:                                 break;
502:                         }
503:
504:                         /*
505:                          * otherwise we will look at the direct super types of the eClasses we examined in this iteration. be
506:                          * careful to avoid checking the same EClass multiple times
507:                          */
508:                         final Set<EClass> superTypes = new LinkedHashSet<EClass>();
509:                         for (final EClass eClass : eClassesToGetViewModelsFor) {
510:                                 for (final EClass superType : eClass.getESuperTypes()) {
511:                                         if (checkedEClasses.contains(superType)) {
512:                                                 continue;
513:                                         }
514:                                         superTypes.add(superType);
515:                                 }
516:                         }
517:                         eClassesToGetViewModelsFor = superTypes;
518:                 }
519:                 return viewMap;
520:         }
521:
522:         /**
523:          *
524:          * Inner class to hold the relevant data of the extension point.
525:          *
526:          * @author Eugen Neufeld
527:          *
528:          */
529:         static final class ExtensionDescription {
530:                 private final Map<String, String> keyValuPairs;
531:                 private final String bundleId;
532:
533:                 /**
534:                  * Constructs an ExtensionDescription.
535:                  *
536:                  * @param keyValuPairs The Filter
537:                  * @param bundleId The Bundle that contributed the view model
538:                  */
539:                 ExtensionDescription(Map<String, String> keyValuPairs, String bundleId) {
540:                         this.keyValuPairs = keyValuPairs;
541:                         this.bundleId = bundleId;
542:                 }
543:
544:                 /**
545:                  * Return the KeyValuePairs defined in the extension point.
546:                  *
547:                  * @return The KeyValuePair Map
548:                  */
549:                 Map<String, String> getKeyValuPairs() {
550:                         return keyValuPairs;
551:                 }
552:
553:                 /**
554:                  * The Bundle Id of the bundle contributing the extension point.
555:                  *
556:                  * @return The BundleId
557:                  */
558:                 String getBundleId() {
559:                         return bundleId;
560:                 }
561:
562:                 @Override
563:                 public int hashCode() {
564:                         final int prime = 31;
565:                         int result = 1;
566:                         result = prime * result + (bundleId == null ? 0 : bundleId.hashCode());
567:                         result = prime * result + (keyValuPairs == null ? 0 : keyValuPairs.hashCode());
568:                         return result;
569:                 }
570:
571:                 @Override
572:                 public boolean equals(Object obj) {
573:                         if (this == obj) {
574:                                 return true;
575:                         }
576:                         if (obj == null) {
577:                                 return false;
578:                         }
579:                         if (getClass() != obj.getClass()) {
580:                                 return false;
581:                         }
582:                         final ExtensionDescription other = (ExtensionDescription) obj;
583:                         if (bundleId == null) {
584:                                 if (other.bundleId != null) {
585:                                         return false;
586:                                 }
587:                         } else if (!bundleId.equals(other.bundleId)) {
588:                                 return false;
589:                         }
590:                         if (keyValuPairs == null) {
591:                                 if (other.keyValuPairs != null) {
592:                                         return false;
593:                                 }
594:                         } else if (!keyValuPairs.equals(other.keyValuPairs)) {
595:                                 return false;
596:                         }
597:                         return true;
598:                 }
599:
600:         }
601:
602: }