Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 223be3218d44f0f225ef95741f673a69e2f71840 (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
/*******************************************************************************
 * Copyright (c) 2009, 2013 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.pde.core.target;

import java.io.*;
import java.util.Map;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.spi.RegistryContributor;
import org.eclipse.equinox.frameworkadmin.BundleInfo;
import org.eclipse.osgi.util.ManifestElement;
import org.eclipse.osgi.util.NLS;
import org.eclipse.pde.internal.core.ICoreConstants;
import org.eclipse.pde.internal.core.PDECore;
import org.eclipse.pde.internal.core.target.Messages;
import org.eclipse.pde.internal.core.util.ManifestUtils;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;

/**
 * Describes a single bundle in a target definition. Also used to represent
 * content in the target that is missing or invalid.
 * 
 * @since 3.8
 */
public class TargetBundle {

	/**
	 * Status code indicating that this target bundle represents a required plug-in that is missing from a target definition
	 */
	public static final int STATUS_PLUGIN_DOES_NOT_EXIST = 100;

	/**
	 * Status code indicating that this target bundle represents a required feature that is missing from a target definition
	 */
	public static final int STATUS_FEATURE_DOES_NOT_EXIST = 110;

	/**
	 * Status code indicating that a required bundle version does not exist (a bundle
	 * with the correct symbolic name is present, but the specified version was not
	 * found).
	 */
	public static final int STATUS_VERSION_DOES_NOT_EXIST = 101;

	/**
	 * Status code indicating that a bundle's manifest could not be read, or did not exist. 
	 */
	public static final int STATUS_INVALID_MANIFEST = 102;

	protected BundleInfo fInfo;
	protected boolean fIsFragment = false;
	protected BundleInfo fSourceTarget;
	protected String fSourcePath = null;

	/**
	 * Constructs a target bundle for a local bundle.  The bundle may be a directory or
	 * an archive file. The manifest of the bundle will be read to collect the additional
	 * information.
	 * 
	 * @param bundleLocation the location of the bundle (directory or archive) to open
	 * @throws CoreException if there is a problem opening the bundle or its manifest
	 */
	public TargetBundle(File bundleLocation) throws CoreException {
		initialize(bundleLocation);
	}

	/**
	 * Constructs an empty target bundle with no information.
	 */
	protected TargetBundle() {
		fInfo = new BundleInfo();
	}

	/**
	 * Returns a {@link BundleInfo} object containing additional information about the bundle
	 * this target bundle represents. It is not guaranteed that the bundle info will have any
	 * fields set.  The base implementation of {@link TargetBundle} will fill in the location,
	 * symbolic name and version if that information was available in the bundle's manifest. 
	 * 
	 * @return a bundle info object with information on the bundle this target bundle represents
	 */
	public BundleInfo getBundleInfo() {
		return fInfo;
	}

	/**
	 * Returns a status object describing any problems with this target bundle. The base 
	 * implementation of {@link TargetBundle} will always return an OK status.
	 * 
	 * @return status of this bundle
	 */
	public IStatus getStatus() {
		// The status will always be ok as the constructor would throw an exception for any issues. 
		return Status.OK_STATUS;
	}

	/**
	 * Returns <code>true</code> if this bundle is a source bundle and 
	 * <code>false</code> if this bundle is an executable bundle.
	 * 
	 * @return whether the resolved bundle is a source bundle
	 */
	public boolean isSourceBundle() {
		return fSourceTarget != null;
	}

	/**
	 * If this bundle is a source bundle this method returns a bundle info
	 * representing the executable bundle that this bundle provides source for.
	 * The returned bundle info may not have a symbolic name and version set if
	 * this source bundle is an old style source plug-in.
	 * 
	 * @return bundle info representing bundle this bundle provides source for or <code>null</code>
	 */
	public BundleInfo getSourceTarget() {
		return fSourceTarget;
	}

	/**
	 * Returns whether this bundle is a fragment.
	 * 
	 * @return whether this bundle is a fragment
	 */
	public boolean isFragment() {
		return fIsFragment;
	}

	/**
	 * Returns bundle relative path to old-style source folders, or <code>null</code>
	 * if not applicable.
	 * 
	 * @return bundle relative path to old-style source folders, or <code>null</code>
	 */
	public String getSourcePath() {
		return fSourcePath;
	}

	/**
	 * Initializes the contents of this target bundle from the provided local bundle
	 * 
	 * @param file the bundle to initialize from
	 */
	private void initialize(File file) throws CoreException {
		if (file == null || !file.exists()) {
			throw new CoreException(new Status(IStatus.ERROR, PDECore.PLUGIN_ID, NLS.bind(Messages.TargetFeature_FileDoesNotExist, file)));
		}
		Map<String, String> manifest = ManifestUtils.loadManifest(file);
		try {
			fInfo = new BundleInfo(file.toURI());
			// Attempt to retrieve additional bundle information from the manifest
			String header = manifest.get(Constants.BUNDLE_SYMBOLICNAME);
			if (header != null) {
				ManifestElement[] elements = ManifestElement.parseHeader(Constants.BUNDLE_SYMBOLICNAME, header);
				if (elements != null) {
					String name = elements[0].getValue();
					if (name != null) {
						fInfo.setSymbolicName(name);
						header = manifest.get(Constants.BUNDLE_VERSION);
						if (header != null) {
							elements = ManifestElement.parseHeader(Constants.BUNDLE_VERSION, header);
							if (elements != null) {
								fInfo.setVersion(elements[0].getValue());
							}
						}
					}
					fSourceTarget = getProvidedSource(file, name, manifest);
				}
			}
			fIsFragment = manifest.containsKey(Constants.FRAGMENT_HOST);
		} catch (BundleException e) {
			throw new CoreException(new Status(IStatus.ERROR, PDECore.PLUGIN_ID, STATUS_INVALID_MANIFEST, NLS.bind(Messages.TargetBundle_ErrorReadingManifest, file.getAbsolutePath()), e));
		}
	}

	/**
	 * If the given bundle is a source bundle, the bundle that this bundle provides source for will be returned.
	 * If the given bundle is not a source bundle or there was a problem getting the source target, <code>null</code>
	 * will be returned.
	 * 
	 * @param bundle location of the bundle in the file system, can be <code>null</code> to skip searching plugin.xml
	 * @param symbolicName symbolic name of the bundle, can be <code>null</code> to skip searching of plugin.xml
	 * @param manifest the bundle's manifest, can be <code>null</code> to skip searching of manifest entries
	 * @return bundle for provided source or <code>null</code> if not a source bundle
	 */
	private BundleInfo getProvidedSource(File bundle, String symbolicName, Map<String, String> manifest) {
		fSourcePath = null;
		if (manifest != null) {
			if (manifest.containsKey(ICoreConstants.ECLIPSE_SOURCE_BUNDLE)) {
				try {
					ManifestElement[] manifestElements = ManifestElement.parseHeader(ICoreConstants.ECLIPSE_SOURCE_BUNDLE, manifest.get(ICoreConstants.ECLIPSE_SOURCE_BUNDLE));
					if (manifestElements != null) {
						for (int j = 0; j < manifestElements.length; j++) {
							ManifestElement currentElement = manifestElements[j];
							String binaryPluginName = currentElement.getValue();
							String versionEntry = currentElement.getAttribute(Constants.VERSION_ATTRIBUTE);
							// Currently the version attribute is required
							if (binaryPluginName != null && binaryPluginName.length() > 0 && versionEntry != null && versionEntry.length() > 0) {
								return new BundleInfo(binaryPluginName, versionEntry, null, BundleInfo.NO_LEVEL, false);
							}
						}
					}
				} catch (BundleException e) {
					PDECore.log(e);
					return null;
				}
			}
			// source bundles never have a class path
			if (manifest.containsKey(Constants.BUNDLE_CLASSPATH)) {
				return null;
			}
		}

		if (bundle != null && symbolicName != null) {
			// old source bundles were never jar'd
			if (bundle.isFile()) {
				return null;
			}

			// check for an "org.eclipse.pde.core.source" extension 
			File pxml = new File(bundle, ICoreConstants.PLUGIN_FILENAME_DESCRIPTOR);
			if (!pxml.exists()) {
				pxml = new File(bundle, ICoreConstants.FRAGMENT_FILENAME_DESCRIPTOR);
			}
			if (pxml.exists()) {
				IExtensionRegistry registry = RegistryFactory.createRegistry(null, this, this);
				// Contribute PDE source extension point
				String bogusDef = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<?eclipse version=\"3.2\"?>\n<plugin><extension-point id=\"source\" name=\"source\"/>\n</plugin>"; //$NON-NLS-1$
				RegistryContributor pointContributor = new RegistryContributor(PDECore.PLUGIN_ID, PDECore.PLUGIN_ID, null, null);
				registry.addContribution(new ByteArrayInputStream(bogusDef.getBytes()), pointContributor, false, null, null, this);
				// Search for extensions to the extension point
				RegistryContributor contributor = new RegistryContributor(symbolicName, symbolicName, null, null);
				try {
					registry.addContribution(new BufferedInputStream(new FileInputStream(pxml)), contributor, false, null, null, this);
					IExtension[] extensions = registry.getExtensions(contributor);
					for (int i = 0; i < extensions.length; i++) {
						IExtension extension = extensions[i];
						if (ICoreConstants.EXTENSION_POINT_SOURCE.equals(extension.getExtensionPointUniqueIdentifier())) {
							IConfigurationElement[] elements = extension.getConfigurationElements();
							if (elements.length == 1) {
								fSourcePath = elements[0].getAttribute("path"); //$NON-NLS-1$
							}
							return new BundleInfo(null, null, bundle.toURI(), BundleInfo.NO_LEVEL, false);
						}
					}
				} catch (FileNotFoundException e) {
				}
			}
		}
		return null;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		StringBuffer result = new StringBuffer().append(getBundleInfo().toString());
		IStatus status = getStatus();
		if (status != null && !status.isOK()) {
			result.append(' ').append(status.toString());
		}
		return result.toString();
	}
}

Back to the top