Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: deeca832605783049eb2f9c481265cb3d0298a71 (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
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
/*******************************************************************************
 * Copyright (c) 2008, 2009 Code 9 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: 
 *   Code 9 - initial API and implementation
 *   EclipseSource - ongoing development
 *   IBM Corporation - ongoing development
 ******************************************************************************/
package org.eclipse.equinox.p2.publisher.eclipse;

import java.io.File;
import java.util.*;
import org.eclipse.equinox.internal.p2.core.helpers.CollectionUtils;
import org.eclipse.equinox.internal.p2.publisher.eclipse.DataLoader;
import org.eclipse.equinox.internal.p2.publisher.eclipse.IProductDescriptor;
import org.eclipse.equinox.internal.provisional.frameworkadmin.BundleInfo;
import org.eclipse.equinox.internal.provisional.frameworkadmin.ConfigData;
import org.eclipse.equinox.p2.metadata.IVersionedId;
import org.eclipse.equinox.p2.metadata.Version;
import org.eclipse.equinox.p2.publisher.AbstractAdvice;
import org.eclipse.equinox.p2.publisher.AbstractPublisherAction;
import org.eclipse.equinox.p2.publisher.actions.ILicenseAdvice;

/**
 * Provide advice derived from the .product file.  The product can give some info on 
 * launching as well as the configuration (bundles, properties, ...)
 */
public class ProductFileAdvice extends AbstractAdvice implements ILicenseAdvice, IExecutableAdvice, IConfigAdvice, IBrandingAdvice {
	private final static String EMPTY_VERSION = "0.0.0"; //$NON-NLS-1$
	private final static String OSGI_SPLASH_PATH = "osgi.splashPath"; //$NON-NLS-1$
	private final static String SPLASH_PREFIX = "platform:/base/plugins/"; //$NON-NLS-1$
	private IProductDescriptor product;
	private String configSpec;
	private String ws;
	private String os;
	private String arch;
	private ConfigData configData = null;

	protected String getId() {
		return product.getId();
	}

	protected Version getVersion() {
		return Version.parseVersion(product.getVersion());
	}

	/**
	 * Constructs a new ProductFileAdvice for a given product file and a
	 * particular configuration. Configurations are 
	 * specified as: ws.os.arch where:
	 *  ws is the windowing system
	 *  os is the operating system
	 *  arch is the architecture
	 */
	public ProductFileAdvice(IProductDescriptor product, String configSpec) {
		this.product = product;
		this.configSpec = configSpec;

		String[] config = AbstractPublisherAction.parseConfigSpec(configSpec);
		ws = config[0];
		if (ws == null)
			ws = AbstractPublisherAction.CONFIG_ANY;
		os = config[1];
		if (os == null)
			os = AbstractPublisherAction.CONFIG_ANY;
		arch = config[2];
		if (arch == null)
			arch = AbstractPublisherAction.CONFIG_ANY;

		configData = getConfigData();
	}

	/**
	 * Returns the program arguments for this product.  
	 */
	public String[] getProgramArguments() {
		String line = product.getProgramArguments(os);
		return AbstractPublisherAction.getArrayFromString(line, " "); //$NON-NLS-1$
	}

	/**
	 * Returns the VM arguments for this product.
	 */
	public String[] getVMArguments() {
		String line = product.getVMArguments(os);
		return AbstractPublisherAction.getArrayFromString(line, " "); //$NON-NLS-1$
	}

	/**
	 * Returns the Bundles that constitute this product.  These
	 * bundles may be specified in the .product file, .product file configuration
	 * area, config.ini file, or a combination of these three places.
	 */
	public BundleInfo[] getBundles() {
		return configData.getBundles();
	}

	/**
	 * Returns the properties associated with this product.  These
	 * properties may be defined in the .product file, the config.ini
	 * file, or both.
	 */
	public Map<String, String> getProperties() {
		Map<String, String> result = new HashMap<String, String>();
		CollectionUtils.putAll(configData.getProperties(), result);
		result.putAll(product.getConfigurationProperties());
		return result;
	}

	/**
	 * Returns the name of the launcher.  This should be the OS-independent
	 * name. That is, ".exe" etc. should not be included.
	 * 
	 * @return the name of the branded launcher or <code>null</code> if none.
	 */
	public String getExecutableName() {
		return product.getLauncherName();
	}

	/**
	 * Returns the product file parser that this advice is working on
	 */
	public IProductDescriptor getProductFile() {
		return product;
	}

	/**
	 * Returns the icons defined for this product
	 */
	public String[] getIcons() {
		return product.getIcons(os);
	}

	/**
	 * Returns the operating system that this advice is configured to work with.
	 */
	public String getOS() {
		return this.os;
	}

	/**
	 * Returns the license text for this product
	 */
	public String getLicenseURL() {
		return product.getLicenseURL();
	}

	/**
	 * Returns the license URL for this product
	 */
	public String getLicenseText() {
		return product.getLicenseText();
	}

	private ConfigData getConfigData() {
		DataLoader loader = createDataLoader();
		ConfigData result;
		if (loader != null) {
			result = loader.getConfigData();
			normalizeBundleVersions(result);
		} else
			result = generateConfigData();

		addProductFileBundles(result); // these are the bundles specified in the <plugins/> tag
		addProductFileConfigBundles(result); // these are the bundles specified in the <configurations> tag in the product file

		if (product.getProductId() != null)
			result.setProperty("eclipse.product", product.getProductId()); //$NON-NLS-1$
		if (product.getApplication() != null)
			result.setProperty("eclipse.application", product.getApplication()); //$NON-NLS-1$
		String location = getSplashLocation();
		if (location != null)
			result.setProperty(OSGI_SPLASH_PATH, SPLASH_PREFIX + location);
		return result;
	}

	private void normalizeBundleVersions(ConfigData data) {
		BundleInfo[] bundles = data.getBundles();
		for (int i = 0; i < bundles.length; i++) {
			// If the bundle doesn't have a version set it to 0.0.0
			if (bundles[i].getVersion() == null)
				bundles[i].setVersion(EMPTY_VERSION);
		}
	}

	private void addProductFileConfigBundles(ConfigData data) {
		Set<BundleInfo> versionBoundBundles = new HashSet<BundleInfo>();
		Map<String, List<BundleInfo>> unboundedBundles = new HashMap<String, List<BundleInfo>>();

		BundleInfo[] bundles = data.getBundles();
		for (int i = 0; i < bundles.length; i++) {
			// For each bundle we know about, cache it.  If the bundle doesn't have a version
			// add it to a list of bundles by name
			BundleInfo bundleInfo = bundles[i];
			if (bundleInfo.getVersion() == null || bundleInfo.getVersion().equals(EMPTY_VERSION)) {
				bundleInfo.setVersion(EMPTY_VERSION);
				addUnboundedBundle(unboundedBundles, bundleInfo);
			} else {
				versionBoundBundles.add(bundleInfo);
				addUnboundedBundle(unboundedBundles, bundleInfo);
			}
		}

		List<BundleInfo> bundleInfos = product.getBundleInfos();
		for (BundleInfo bundleInfo : bundleInfos) {
			// For each bundle that has configuration information, if the bundle is in the 
			// bundles bound by version, add the "configured" bundle instead
			// If the bundle is not bound to a version, then replace all bounded versions
			// with this one.  Otherwise, just add this one (don't replace)
			if (versionBoundBundles.contains(bundleInfo)) {
				// If we found a version with the same name and version, replace it with the "configured" bundle
				data.removeBundle(bundleInfo);
				data.addBundle(bundleInfo);
			} else if (bundleInfo.getVersion() == null || bundleInfo.getVersion().equals("0.0.0")) {//$NON-NLS-1$
				// If we don't have a version number, look for all bundles that match by name
				List<BundleInfo> list = unboundedBundles.get(bundleInfo.getSymbolicName());
				if (list == null)
					data.addBundle(bundleInfo);
				else
					for (BundleInfo target : list) {
						target.setStartLevel(bundleInfo.getStartLevel());
						target.setMarkedAsStarted(bundleInfo.isMarkedAsStarted());
					}
			} else {
				// Otherwise we have a version, but we could not match it, so just add this one.
				data.addBundle(bundleInfo);
			}

		}
	}

	private void addUnboundedBundle(Map<String, List<BundleInfo>> data, BundleInfo bundleInfo) {
		if (!data.containsKey(bundleInfo.getSymbolicName()))
			data.put(bundleInfo.getSymbolicName(), new LinkedList<BundleInfo>());
		data.get(bundleInfo.getSymbolicName()).add(bundleInfo);
	}

	private void addProductFileBundles(ConfigData data) {
		List<IVersionedId> bundles = product.getBundles(true);
		Set<BundleInfo> set = new HashSet<BundleInfo>();
		set.addAll(Arrays.asList(data.getBundles()));

		for (IVersionedId vid : bundles) {
			BundleInfo bundleInfo = new BundleInfo();
			bundleInfo.setSymbolicName(vid.getId());
			bundleInfo.setVersion(vid.getVersion().toString());
			if (!set.contains(bundleInfo))
				data.addBundle(bundleInfo);
		}
	}

	private ConfigData generateConfigData() {
		ConfigData result = new ConfigData(null, null, null, null);
		if (product.useFeatures())
			return result;

		// Add all the bundles here.  We replace / update them later
		// if we find configuration information
		List<IVersionedId> bundles = product.getBundles(true);
		for (IVersionedId vid : bundles) {
			BundleInfo bundleInfo = new BundleInfo();
			bundleInfo.setSymbolicName(vid.getId());
			bundleInfo.setVersion(vid.getVersion().toString());
			result.addBundle(bundleInfo);
		}
		return result;
	}

	private String getSplashLocation() {
		return product.getSplashLocation();
	}

	protected String getConfigSpec() {
		return configSpec;
	}

	protected boolean matchConfig(String spec, boolean includeDefault) {
		if (spec != null) {
			String targetWS = AbstractPublisherAction.parseConfigSpec(spec)[0];
			if (targetWS == null)
				targetWS = AbstractPublisherAction.CONFIG_ANY;
			if (!ws.equals(targetWS) && !ws.equals(AbstractPublisherAction.CONFIG_ANY) && !targetWS.equals(AbstractPublisherAction.CONFIG_ANY)) {
				return false;
			}

			String targetOS = AbstractPublisherAction.parseConfigSpec(spec)[1];
			if (targetOS == null)
				targetOS = AbstractPublisherAction.CONFIG_ANY;
			if (!os.equals(targetOS) && !os.equals(AbstractPublisherAction.CONFIG_ANY) && !targetOS.equals(AbstractPublisherAction.CONFIG_ANY)) {
				return false;
			}

			String targetArch = AbstractPublisherAction.parseConfigSpec(spec)[2];
			if (targetArch == null)
				targetArch = AbstractPublisherAction.CONFIG_ANY;
			if (!arch.equals(targetArch) && !arch.equals(AbstractPublisherAction.CONFIG_ANY) && !targetArch.equals(AbstractPublisherAction.CONFIG_ANY)) {
				return false;
			}
		}
		return true;
	}

	private DataLoader createDataLoader() {
		String location = product.getConfigIniPath(os);
		if (location == null)
			location = product.getConfigIniPath(null);
		if (location == null)
			return null;

		File configFile = new File(location);
		// We are assuming we are always relative from the product file
		// However PDE tooling puts us relative from the workspace, that "relative" path also looks like an absolute path on linux
		// Build may have copied the file to the correct place for us
		if (!configFile.isAbsolute() || !configFile.exists())
			configFile = new File(product.getLocation().getParentFile(), location);

		//We don't really have an executable location, get something reasonable based on the config.ini location
		File parent = configFile.getParentFile();
		if (parent.getName().equals("configuration") && parent.getParentFile() != null) //$NON-NLS-1$
			parent = parent.getParentFile();
		return new DataLoader(configFile, parent);
	}

}

Back to the top