Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 9c3613b80cfb05f381b7fcc012f917cbe5ad932c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
/*****************************************************************************
 * Copyright (c) 2010, 2014 CEA LIST and others.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *  Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
 *  Christian W. Damus (CEA) - bug 435103
 *  Christian W. Damus (CEA) - bug 417409
 *
 *****************************************************************************/
package org.eclipse.papyrus.views.properties.modelelement;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.papyrus.infra.properties.contexts.Context;
import org.eclipse.papyrus.infra.properties.contexts.DataContextElement;
import org.eclipse.papyrus.infra.properties.contexts.DataContextRoot;
import org.eclipse.papyrus.infra.properties.contexts.View;
import org.eclipse.papyrus.infra.properties.environment.ModelElementFactoryDescriptor;
import org.eclipse.papyrus.infra.tools.util.ClassLoaderHelper;
import org.eclipse.papyrus.infra.widgets.Activator;
import org.eclipse.papyrus.views.properties.util.PropertiesUtil;
import org.eclipse.papyrus.views.properties.xwt.XWTSection;

/**
 * A Factory to build and populate DataSource with the right ModelElements
 *
 * @author Camille Letavernier
 */
public class DataSourceFactory {

	/**
	 * Singleton instance for DataSourceFactory
	 */
	public static final DataSourceFactory instance = new DataSourceFactory();

	/**
	 * Creates a new DataSource from a selection and a view.
	 *
	 * @param selection
	 *            The selection of Objects
	 * @param view
	 *            The view to display
	 * @return The DataSource that can be passed to the DisplayEngine to display
	 *         the view
	 */
	public DataSource createDataSourceFromSelection(IStructuredSelection selection, View view) {
		SelectionEntry selectionEntry = new SelectionEntry(selection, view);

		if (!sources.containsKey(selectionEntry)) {
			DataSource source = new DataSource(view, selection);
			sources.put(selectionEntry, source);
		}

		return sources.get(selectionEntry);
	}

	public void removeFromCache(IStructuredSelection selection, View view) {
		if (selection == null || view == null) {
			return;
		}

		SelectionEntry entry = new SelectionEntry(selection, view);
		sources.remove(entry);
	}

	/**
	 * Returns the ModelElement corresponding to the given propertyPath and
	 * DataSource
	 *
	 * @param source
	 *            The DataSource used to retrieved informations such as the View
	 *            and the Selection
	 * @param propertyPath
	 *            The path describing the property for which we want a
	 *            ModelElement
	 * @return The matching modelElement
	 */
	public ModelElement getModelElementFromPropertyPath(DataSource source, String propertyPath) {
		String key = propertyPath.substring(0, propertyPath.lastIndexOf(":")); //$NON-NLS-1$
		for (Context context : PropertiesUtil.getDependencies(source.getView().getContext())) {
			DataContextElement element = PropertiesUtil.getContextElementByQualifiedName(key, context.getDataContexts());
			if (element != null) {
				ModelElement modelElement = DataSourceFactory.instance.createModelElement(element, source.getSelection());
				if (modelElement != null) {
					modelElement.setDataSource(source);
				}
				return modelElement;
			}
		}
		return null;
	}

	/**
	 * Creates a ModelElement from the given DataContextElement and Selection.
	 *
	 * @param contextElement
	 *            The contextElement for which we are creating a ModelElement
	 * @param selection
	 *            The list of objects currently selected
	 * @return The model element corresponding to the given contextElement and
	 *         selection
	 */
	private ModelElement createModelElement(final DataContextElement contextElement, IStructuredSelection selection) {
		if (selection.size() == 1) { // Single Selection
			ModelElement modelElement = createFromSource(selection.getFirstElement(), contextElement);
			return modelElement;
		} else { // MultiSelection
			// Bind the context element in a factory for the composite to create sub-elements
			CompositeModelElement composite = new CompositeModelElement(new CompositeModelElement.BoundModelElementFactory() {

				public ModelElement createModelElement(Object sourceElement) {
					return createFromSource(sourceElement, contextElement);
				}
			});

			Iterator<?> it = selection.iterator();
			while (it.hasNext()) {
				ModelElement element = createFromSource(it.next(), contextElement);
				if (element != null) {
					composite.addModelElement(element);
				}
			}

			return composite;
		}
	}

	/**
	 * Retrieves the ModelElementFactory for the given DataContextElement. The
	 * ModelElementFactory is declared by the DataContextRoot owning the given
	 * DataContextElement
	 *
	 * @param context
	 *            The DataContextElement for which we want to retrieve the
	 *            ModelElementFactory
	 * @return The ModelElementFactory corresponding to the given
	 *         DataContextElement
	 */
	private ModelElementFactory getFactory(DataContextElement context) {
		DataContextRoot rootPackage = getRootPackage(context);
		ModelElementFactoryDescriptor factoryDescriptor = rootPackage.getModelElementFactory();

		if (factoryDescriptor == null) {
			Activator.log.warn("No ModelElementFactory is attached to DataContextElement " + getQualifiedName(context)); //$NON-NLS-1$
			return null;
		}
		if (factoryDescriptor.eIsProxy()) {
			Activator.log.warn("Unresolved reference to the ModelElementFactory: " + EcoreUtil.getURI(factoryDescriptor)); //$NON-NLS-1$
			return null;
		}

		String factoryName = factoryDescriptor.getFactoryClass();
		ModelElementFactory factory = ClassLoaderHelper.newInstance(factoryName, ModelElementFactory.class);

		return factory;
	}

	private ModelElement createFromSource(Object source, DataContextElement context) {
		ModelElementFactory factory = getFactory(context);

		if (factory == null) {
			return null;
		}

		return factory.createFromSource(source, context);
	}

	private DataContextRoot getRootPackage(DataContextElement context) {
		if (context.getPackage() == null) {
			return (DataContextRoot) context;
		}
		return getRootPackage(context.getPackage());
	}

	private String getQualifiedName(DataContextElement context) {
		if (context.getPackage() == null) {
			return context.getName();
		}
		return getQualifiedName(context.getPackage()) + ":" + context.getName(); //$NON-NLS-1$
	}

	/**
	 * Singleton Constructor.
	 */
	private DataSourceFactory() {

	}

	private class SelectionEntry {

		private IStructuredSelection selection;

		private View view;

		public SelectionEntry(IStructuredSelection selection, View view) {
			if (selection == null) {
				throw new IllegalArgumentException("The selection must not be null");
			}
			if (view == null) {
				throw new IllegalArgumentException("The view must not be null");
			}
			this.selection = selection;
			this.view = view;
		}

		@Override
		public boolean equals(Object obj) {
			if (!(obj instanceof SelectionEntry)) {
				return false;
			}

			SelectionEntry other = (SelectionEntry) obj;
			return other.view.equals(view) && selection.equals(other.selection);
		}

		@Override
		public int hashCode() {
			return selection.hashCode() + view.hashCode();
		}
	}

	/**
	 * More than one {@link XWTSection} may share the same DataSource.
	 * They all need to listen on the same source, so that they can correctly
	 * refresh themselves. We maintain a cache for each Selection/View pair.
	 *
	 * The cache is cleaned when the sections are disposed.
	 */
	// TODO : More than one view can be displayed at the same time. The cache should only
	// rely on a selection ; not on a selection-view pair.
	// We may use a (ISelection, Context) key : the DataSource must be associated to a single context
	private Map<SelectionEntry, DataSource> sources = new HashMap<SelectionEntry, DataSource>();
}

Back to the top