Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: e599bd5e695f038df03ddf5c13fbfb7bc954d626 (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
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
/*******************************************************************************
 * Copyright (c) 2005, 2017 IBM Corporation 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:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.equinox.metatype.impl;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.Map.Entry;
import org.eclipse.equinox.metatype.*;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.*;
import org.osgi.service.cm.ManagedService;
import org.osgi.service.cm.ManagedServiceFactory;
import org.osgi.service.log.LogService;
import org.osgi.service.metatype.*;
import org.osgi.util.tracker.ServiceTracker;

public class MetaTypeProviderTracker implements EquinoxMetaTypeInformation {
	private final Bundle _bundle;
	private final LogTracker log;
	private final ServiceTracker<Object, Object> _tracker;

	/**
	 * Constructs a MetaTypeProviderTracker which tracks all MetaTypeProviders
	 * registered by the specified bundle.
	 * @param context The BundleContext of the MetaTypeService implementation
	 * @param bundle The bundle to track all MetaTypeProviders for.
	 * @param log The {@code LogService} to use for logging messages.
	 */
	public MetaTypeProviderTracker(Bundle bundle, LogTracker log, ServiceTracker<Object, Object> tracker) {
		this._bundle = bundle;
		this._tracker = tracker;
		this.log = log;
	}

	private String[] getPids(boolean factory) {
		if (_bundle.getState() != Bundle.ACTIVE)
			return new String[0]; // return none if not active
		MetaTypeProviderWrapper[] wrappers = getMetaTypeProviders();
		ArrayList<String> results = new ArrayList<String>();
		for (int i = 0; i < wrappers.length; i++) {
			// return only the correct type of pids (regular or factory)
			if (factory == wrappers[i].factory)
				results.add(wrappers[i].pid);
		}
		return results.toArray(new String[results.size()]);
	}

	public String[] getPids() {
		return getPids(false);
	}

	public String[] getFactoryPids() {
		return getPids(true);
	}

	public Bundle getBundle() {
		return _bundle;
	}

	public EquinoxObjectClassDefinition getObjectClassDefinition(String id, String locale) {
		if (_bundle.getState() != Bundle.ACTIVE)
			return null; // return none if not active
		MetaTypeProviderWrapper[] wrappers = getMetaTypeProviders();
		for (int i = 0; i < wrappers.length; i++) {
			if (id.equals(wrappers[i].pid))
				// found a matching pid now call the actual provider
				return wrappers[i].getObjectClassDefinition(id, locale);
		}
		return null;
	}

	public String[] getLocales() {
		if (_bundle.getState() != Bundle.ACTIVE)
			return new String[0]; // return none if not active
		MetaTypeProviderWrapper[] wrappers = getMetaTypeProviders();
		ArrayList<String> locales = new ArrayList<String>();
		// collect all the unique locales from all providers we found
		for (int i = 0; i < wrappers.length; i++) {
			String[] wrappedLocales = wrappers[i].getLocales();
			if (wrappedLocales == null)
				continue;
			for (int j = 0; j < wrappedLocales.length; j++)
				if (!locales.contains(wrappedLocales[j]))
					locales.add(wrappedLocales[j]);
		}
		return locales.toArray(new String[locales.size()]);
	}

	private MetaTypeProviderWrapper[] getMetaTypeProviders() {
		Map<ServiceReference<Object>, Object> services = _tracker.getTracked();
		if (services.isEmpty())
			return new MetaTypeProviderWrapper[0];
		Set<MetaTypeProviderWrapper> result = new HashSet<MetaTypeProviderWrapper>();
		for (Entry<ServiceReference<Object>, Object> entry : services.entrySet()) {
			ServiceReference<Object> serviceReference = entry.getKey();
			if (serviceReference.getBundle() == _bundle) {
				Object service = entry.getValue();
				// If the service is not a MetaTypeProvider, we're not interested in it.
				if (service instanceof MetaTypeProvider) {
					// Include the METATYPE_PID, if present, to return as part of getPids(). Also, include the 
					// METATYPE_FACTORY_PID, if present, to return as part of getFactoryPids().
					// The filter ensures at least one of these properties was set for a standalone MetaTypeProvider.
					addMetaTypeProviderWrappers(MetaTypeProvider.METATYPE_PID, serviceReference, (MetaTypeProvider) service, false, result);
					addMetaTypeProviderWrappers(MetaTypeProvider.METATYPE_FACTORY_PID, serviceReference, (MetaTypeProvider) service, true, result);
					// If the service is a ManagedService, include the SERVICE_PID to return as part of getPids().
					// The filter ensures the SERVICE_PID property was set.
					if (service instanceof ManagedService) {
						addMetaTypeProviderWrappers(Constants.SERVICE_PID, serviceReference, (MetaTypeProvider) service, false, result);
					}
					// If the service is a ManagedServiceFactory, include the SERVICE_PID to return as part of getFactoryPids().
					// The filter ensures the SERVICE_PID property was set.
					else if (service instanceof ManagedServiceFactory) {
						addMetaTypeProviderWrappers(Constants.SERVICE_PID, serviceReference, (MetaTypeProvider) service, true, result);
					}
				}
			}
		}
		return result.toArray(new MetaTypeProviderWrapper[result.size()]);
	}

	private void addMetaTypeProviderWrappers(String servicePropertyName, ServiceReference<Object> serviceReference, MetaTypeProvider service, boolean factory, Set<MetaTypeProviderWrapper> wrappers) {
		String[] pids = getStringProperty(servicePropertyName, serviceReference.getProperty(servicePropertyName));
		for (String pid : pids) {
			wrappers.add(new MetaTypeProviderWrapper(service, pid, factory));
		}
	}

	private String[] getStringProperty(String name, Object value) {
		// Don't log a warning if the value is null. The filter guarantees at least one of the necessary properties
		// is there. If others are not, this method will get called with value equal to null.
		if (value == null)
			return new String[0];
		if (value instanceof String) {
			return new String[] {(String) value};
		}
		if (value instanceof String[]) {
			return (String[]) value;
		}
		Exception e = null;
		if (value instanceof Collection) {
			@SuppressWarnings("unchecked")
			Collection<String> temp = (Collection<String>) value;
			try {
				return temp.toArray(new String[temp.size()]);
			} catch (ArrayStoreException ase) {
				e = ase;
			}
		}
		log.log(LogService.LOG_WARNING, NLS.bind(MetaTypeMsg.INVALID_PID_METATYPE_PROVIDER_IGNORED, new Object[] {_bundle.getSymbolicName(), _bundle.getBundleId(), name, value}), e);
		return new String[0];
	}

	// this is a simple class just used to temporarily store information about a provider
	public class MetaTypeProviderWrapper implements MetaTypeProvider {
		private final MetaTypeProvider provider;
		final String pid;
		final boolean factory;

		MetaTypeProviderWrapper(MetaTypeProvider provider, String pid, boolean factory) {
			this.provider = provider;
			this.pid = pid;
			this.factory = factory;
		}

		@Override
		public boolean equals(Object object) {
			if (object == this)
				return true;
			if (!(object instanceof MetaTypeProviderWrapper))
				return false;
			MetaTypeProviderWrapper that = (MetaTypeProviderWrapper) object;
			return this.provider.equals(that.provider) && this.pid.equals(that.pid) && this.factory == that.factory;
		}

		@Override
		public int hashCode() {
			int result = 17;
			result = 31 * result + provider.hashCode();
			result = 31 * result + pid.hashCode();
			result = 31 * result + (factory ? 1 : 0);
			return result;
		}

		public EquinoxObjectClassDefinition getObjectClassDefinition(String id, String locale) {
			final ObjectClassDefinition ocd = provider.getObjectClassDefinition(id, locale);
			if (ocd == null)
				return null;
			return new EquinoxObjectClassDefinition() {
				public String getName() {
					return ocd.getName();
				}

				public String getID() {
					return ocd.getID();
				}

				public String getDescription() {
					return ocd.getDescription();
				}

				public InputStream getIcon(int size) throws IOException {
					return ocd.getIcon(size);
				}

				public Map<String, String> getExtensionAttributes(String schema) {
					return Collections.<String, String> emptyMap();
				}

				public Set<String> getExtensionUris() {
					return Collections.<String> emptySet();
				}

				public EquinoxAttributeDefinition[] getAttributeDefinitions(int filter) {
					AttributeDefinition[] ads = ocd.getAttributeDefinitions(filter);
					if (ads == null || ads.length == 0)
						return new EquinoxAttributeDefinition[0];
					Collection<EquinoxAttributeDefinition> result = new ArrayList<EquinoxAttributeDefinition>(ads.length);
					for (final AttributeDefinition ad : ads) {
						result.add(new EquinoxAttributeDefinition() {
							public String getName() {
								return ad.getName();
							}

							public String getID() {
								return ad.getID();
							}

							public String getDescription() {
								return ad.getDescription();
							}

							public int getCardinality() {
								return ad.getCardinality();
							}

							public int getType() {
								return ad.getType();
							}

							public String[] getOptionValues() {
								return ad.getOptionValues();
							}

							public String[] getOptionLabels() {
								return ad.getOptionLabels();
							}

							public String validate(String value) {
								return ad.validate(value);
							}

							public String[] getDefaultValue() {
								return ad.getDefaultValue();
							}

							public Map<String, String> getExtensionAttributes(String schema) {
								return Collections.<String, String> emptyMap();
							}

							public Set<String> getExtensionUris() {
								return Collections.<String> emptySet();
							}

							public String getMax() {
								return null;
							}

							public String getMin() {
								return null;
							}
						});
					}
					return result.toArray(new EquinoxAttributeDefinition[result.size()]);
				}
			};
		}

		public String[] getLocales() {
			return provider.getLocales();
		}
	}
}

Back to the top