001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.pde.internal.core;
011:
012: import java.io.File;
013: import java.util.ArrayList;
014: import java.util.HashMap;
015: import java.util.HashSet;
016: import java.util.Iterator;
017: import java.util.Map;
018: import java.util.Stack;
019: import java.util.TreeMap;
020:
021: import org.eclipse.core.resources.IFile;
022: import org.eclipse.core.resources.IProject;
023: import org.eclipse.core.resources.IResource;
024: import org.eclipse.core.runtime.CoreException;
025: import org.eclipse.core.runtime.IPath;
026: import org.eclipse.core.runtime.Path;
027: import org.eclipse.core.runtime.Platform;
028: import org.eclipse.jdt.core.IClasspathContainer;
029: import org.eclipse.jdt.core.IClasspathEntry;
030: import org.eclipse.jdt.core.JavaCore;
031: import org.eclipse.osgi.service.resolver.BaseDescription;
032: import org.eclipse.osgi.service.resolver.BundleDescription;
033: import org.eclipse.osgi.service.resolver.BundleSpecification;
034: import org.eclipse.osgi.service.resolver.ExportPackageDescription;
035: import org.eclipse.osgi.service.resolver.HostSpecification;
036: import org.eclipse.osgi.service.resolver.ImportPackageSpecification;
037: import org.eclipse.osgi.service.resolver.StateHelper;
038: import org.eclipse.pde.core.build.IBuild;
039: import org.eclipse.pde.core.build.IBuildEntry;
040: import org.eclipse.pde.core.plugin.IPluginModelBase;
041: import org.eclipse.pde.core.plugin.PluginRegistry;
042: import org.eclipse.pde.internal.build.IBuildPropertiesConstants;
043: import org.eclipse.pde.internal.core.ibundle.IBundlePluginModelBase;
044:
045: public class RequiredPluginsClasspathContainer extends
046: PDEClasspathContainer implements IClasspathContainer {
047:
048: private IPluginModelBase fModel;
049: private IBuild fBuild;
050:
051: private static boolean DEBUG = false;
052:
053: private IClasspathEntry[] fEntries = null;
054:
055: static {
056: DEBUG = PDECore.getDefault().isDebugging()
057: && "true".equals(Platform.getDebugOption("org.eclipse.pde.core/classpath")); //$NON-NLS-1$ //$NON-NLS-2$
058: }
059:
060: /**
061: * Constructor for RequiredPluginsClasspathContainer.
062: */
063: public RequiredPluginsClasspathContainer(IPluginModelBase model) {
064: this (model, null);
065: }
066:
067: public RequiredPluginsClasspathContainer(IPluginModelBase model,
068: IBuild build) {
069: fModel = model;
070: fBuild = build;
071: }
072:
073: /*
074: * (non-Javadoc)
075: * @see org.eclipse.jdt.core.IClasspathContainer#getKind()
076: */
077: public int getKind() {
078: return K_APPLICATION;
079: }
080:
081: /*
082: * (non-Javadoc)
083: * @see org.eclipse.jdt.core.IClasspathContainer#getPath()
084: */
085: public IPath getPath() {
086: return PDECore.REQUIRED_PLUGINS_CONTAINER_PATH;
087: }
088:
089: /*
090: * (non-Javadoc)
091: * @see org.eclipse.jdt.core.IClasspathContainer#getDescription()
092: */
093: public String getDescription() {
094: return PDECoreMessages.RequiredPluginsClasspathContainer_description;
095: }
096:
097: /*
098: * (non-Javadoc)
099: * @see org.eclipse.jdt.core.IClasspathContainer#getClasspathEntries()
100: */
101: public IClasspathEntry[] getClasspathEntries() {
102: if (fModel == null) {
103: if (DEBUG) {
104: System.out
105: .println("********Returned an empty container"); //$NON-NLS-1$
106: System.out.println();
107: }
108: return new IClasspathEntry[0];
109: }
110: if (fEntries == null) {
111: fEntries = computePluginEntries();
112: }
113: if (DEBUG) {
114: System.out
115: .println("Dependencies for plugin '" + fModel.getPluginBase().getId() + "':"); //$NON-NLS-1$ //$NON-NLS-2$
116: for (int i = 0; i < fEntries.length; i++) {
117: System.out.println(fEntries[i]);
118: }
119: System.out.println();
120: }
121: return fEntries;
122: }
123:
124: private IClasspathEntry[] computePluginEntries() {
125: ArrayList entries = new ArrayList();
126: try {
127: BundleDescription desc = fModel.getBundleDescription();
128: if (desc == null)
129: return new IClasspathEntry[0];
130:
131: Map map = retrieveVisiblePackagesFromState(desc);
132:
133: HashSet added = new HashSet();
134:
135: // to avoid cycles, e.g. when a bundle imports a package it exports
136: added.add(desc);
137:
138: HostSpecification host = desc.getHost();
139: if (host != null) {
140: addHostPlugin(host, added, map, entries);
141: } else if ("true".equals(System.getProperty("pde.allowCycles"))) { //$NON-NLS-1$ //$NON-NLS-2$
142: BundleDescription[] fragments = desc.getFragments();
143: for (int i = 0; i < fragments.length; i++) {
144: if (fragments[i].isResolved()) {
145: addPlugin(fragments[i], false, map, entries);
146: }
147: }
148: }
149:
150: // add dependencies
151: BundleSpecification[] required = desc.getRequiredBundles();
152: for (int i = 0; i < required.length; i++) {
153: addDependency((BundleDescription) required[i]
154: .getSupplier(), added, map, entries);
155: }
156:
157: if (fBuild == null)
158: fBuild = ClasspathUtilCore.getBuild(fModel);
159: if (fBuild != null)
160: addSecondaryDependencies(desc, added, entries);
161:
162: // add Import-Package
163: // sort by symbolicName_version to get a consistent order
164: Map sortedMap = new TreeMap();
165: Iterator iter = map.keySet().iterator();
166: while (iter.hasNext()) {
167: BundleDescription bundle = (BundleDescription) iter
168: .next();
169: sortedMap.put(bundle.toString(), bundle);
170: }
171:
172: iter = sortedMap.values().iterator();
173: while (iter.hasNext()) {
174: BundleDescription bundle = (BundleDescription) iter
175: .next();
176: IPluginModelBase model = PluginRegistry
177: .findModel(bundle);
178: if (model != null && model.isEnabled())
179: addDependencyViaImportPackage(model
180: .getBundleDescription(), added, map,
181: entries);
182: }
183:
184: if (fBuild != null)
185: addExtraClasspathEntries(added, entries);
186:
187: } catch (CoreException e) {
188: }
189: return (IClasspathEntry[]) entries
190: .toArray(new IClasspathEntry[entries.size()]);
191: }
192:
193: private Map retrieveVisiblePackagesFromState(BundleDescription desc) {
194: Map visiblePackages = new HashMap();
195: StateHelper helper = Platform.getPlatformAdmin()
196: .getStateHelper();
197: addVisiblePackagesFromState(helper, desc, visiblePackages);
198: if (desc.getHost() != null)
199: addVisiblePackagesFromState(helper,
200: (BundleDescription) desc.getHost().getSupplier(),
201: visiblePackages);
202: return visiblePackages;
203: }
204:
205: private void addVisiblePackagesFromState(StateHelper helper,
206: BundleDescription desc, Map visiblePackages) {
207: if (desc == null)
208: return;
209: ExportPackageDescription[] exports = helper
210: .getVisiblePackages(desc);
211: for (int i = 0; i < exports.length; i++) {
212: BundleDescription exporter = exports[i].getExporter();
213: if (exporter == null)
214: continue;
215: ArrayList list = (ArrayList) visiblePackages.get(exporter);
216: if (list == null)
217: list = new ArrayList();
218: Rule rule = getRule(helper, desc, exports[i]);
219: if (!list.contains(rule))
220: list.add(rule);
221: visiblePackages.put(exporter, list);
222: }
223: }
224:
225: private Rule getRule(StateHelper helper, BundleDescription desc,
226: ExportPackageDescription export) {
227: Rule rule = new Rule();
228: rule.discouraged = helper.getAccessCode(desc, export) == StateHelper.ACCESS_DISCOURAGED;
229: String name = export.getName();
230: rule.path = (name.equals(".")) ? new Path("*") : new Path(name.replaceAll("\\.", "/") + "/*"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
231: return rule;
232: }
233:
234: protected void addDependencyViaImportPackage(
235: BundleDescription desc, HashSet added, Map map,
236: ArrayList entries) throws CoreException {
237: if (desc == null || !added.add(desc))
238: return;
239:
240: addPlugin(desc, true, map, entries);
241:
242: if (hasExtensibleAPI(desc) && desc.getContainingState() != null) {
243: BundleDescription[] fragments = desc.getFragments();
244: for (int i = 0; i < fragments.length; i++) {
245: if (fragments[i].isResolved())
246: addDependencyViaImportPackage(fragments[i], added,
247: map, entries);
248: }
249: }
250: }
251:
252: private void addDependency(BundleDescription desc, HashSet added,
253: Map map, ArrayList entries) throws CoreException {
254: addDependency(desc, added, map, entries, true);
255: }
256:
257: private void addDependency(BundleDescription desc, HashSet added,
258: Map map, ArrayList entries, boolean useInclusion)
259: throws CoreException {
260: if (desc == null || !added.add(desc))
261: return;
262:
263: BundleDescription[] fragments = hasExtensibleAPI(desc) ? desc
264: .getFragments() : new BundleDescription[0];
265:
266: // add fragment patches before host
267: for (int i = 0; i < fragments.length; i++) {
268: if (fragments[i].isResolved()
269: && ClasspathUtilCore.isPatchFragment(fragments[i])) {
270: addDependency(fragments[i], added, map, entries,
271: useInclusion);
272: }
273: }
274:
275: addPlugin(desc, useInclusion, map, entries);
276:
277: // add fragments that are not patches after the host
278: for (int i = 0; i < fragments.length; i++) {
279: if (fragments[i].isResolved()
280: && !ClasspathUtilCore.isPatchFragment(fragments[i])) {
281: addDependency(fragments[i], added, map, entries,
282: useInclusion);
283: }
284: }
285:
286: BundleSpecification[] required = desc.getRequiredBundles();
287: for (int i = 0; i < required.length; i++) {
288: if (required[i].isExported()) {
289: addDependency((BundleDescription) required[i]
290: .getSupplier(), added, map, entries,
291: useInclusion);
292: }
293: }
294: }
295:
296: private boolean addPlugin(BundleDescription desc,
297: boolean useInclusions, Map map, ArrayList entries)
298: throws CoreException {
299: IPluginModelBase model = PluginRegistry.findModel(desc);
300: if (model == null || !model.isEnabled())
301: return false;
302: IResource resource = model.getUnderlyingResource();
303: Rule[] rules = useInclusions ? getInclusions(map, model) : null;
304: if (resource != null) {
305: addProjectEntry(resource.getProject(), rules, entries);
306: } else {
307: addExternalPlugin(model, rules, entries);
308: }
309: return true;
310: }
311:
312: private Rule[] getInclusions(Map map, IPluginModelBase model) {
313: BundleDescription desc = model.getBundleDescription();
314: if (desc == null
315: || "false".equals(System.getProperty("pde.restriction")) //$NON-NLS-1$ //$NON-NLS-2$
316: || !(fModel instanceof IBundlePluginModelBase)
317: || TargetPlatformHelper.getTargetVersion() < 3.1)
318: return null;
319:
320: Rule[] rules;
321:
322: if (desc.getHost() != null)
323: rules = getInclusions(map, (BundleDescription) desc
324: .getHost().getSupplier());
325: else
326: rules = getInclusions(map, desc);
327:
328: return (rules.length == 0 && !ClasspathUtilCore
329: .hasBundleStructure(model)) ? null : rules;
330: }
331:
332: private Rule[] getInclusions(Map map, BundleDescription desc) {
333: ArrayList list = (ArrayList) map.get(desc);
334: return list != null ? (Rule[]) list.toArray(new Rule[list
335: .size()]) : new Rule[0];
336: }
337:
338: private void addHostPlugin(HostSpecification hostSpec,
339: HashSet added, Map map, ArrayList entries)
340: throws CoreException {
341: BaseDescription desc = hostSpec.getSupplier();
342:
343: if (desc instanceof BundleDescription) {
344: BundleDescription host = (BundleDescription) desc;
345:
346: // add host plug-in
347: if (added.add(host) && addPlugin(host, false, map, entries)) {
348: BundleSpecification[] required = host
349: .getRequiredBundles();
350: for (int i = 0; i < required.length; i++) {
351: addDependency((BundleDescription) required[i]
352: .getSupplier(), added, map, entries);
353: }
354:
355: // add Import-Package
356: ImportPackageSpecification[] imports = host
357: .getImportPackages();
358: for (int i = 0; i < imports.length; i++) {
359: BaseDescription supplier = imports[i].getSupplier();
360: if (supplier instanceof ExportPackageDescription) {
361: addDependencyViaImportPackage(
362: ((ExportPackageDescription) supplier)
363: .getExporter(), added, map,
364: entries);
365: }
366: }
367: }
368: }
369: }
370:
371: private boolean hasExtensibleAPI(BundleDescription desc) {
372: IPluginModelBase model = PluginRegistry.findModel(desc);
373: return model != null ? ClasspathUtilCore
374: .hasExtensibleAPI(model) : false;
375: }
376:
377: protected void addExtraClasspathEntries(HashSet added,
378: ArrayList entries) throws CoreException {
379: IBuildEntry[] buildEntries = fBuild.getBuildEntries();
380: for (int i = 0; i < buildEntries.length; i++) {
381: String name = buildEntries[i].getName();
382: if (name
383: .equals(IBuildPropertiesConstants.PROPERTY_JAR_EXTRA_CLASSPATH)
384: || name
385: .startsWith(IBuildPropertiesConstants.PROPERTY_EXTRAPATH_PREFIX)) {
386: addExtraClasspathEntries(added, entries,
387: buildEntries[i].getTokens());
388: }
389: }
390: }
391:
392: protected void addExtraClasspathEntries(HashSet added,
393: ArrayList entries, String[] tokens) throws CoreException {
394: for (int i = 0; i < tokens.length; i++) {
395: IPath path = Path.fromPortableString(tokens[i]);
396: if (!path.isAbsolute()) {
397: File file = new File(fModel.getInstallLocation(), path
398: .toString());
399: if (file.exists()) {
400: IFile resource = PDECore.getWorkspace().getRoot()
401: .getFileForLocation(
402: new Path(file.getAbsolutePath()));
403: if (resource != null
404: && resource.getProject().equals(
405: fModel.getUnderlyingResource()
406: .getProject())) {
407: addExtraLibrary(resource.getFullPath(), null,
408: entries);
409: continue;
410: }
411: }
412: if (path.segmentCount() >= 3
413: && "..".equals(path.segment(0))) { //$NON-NLS-1$
414: path = path.removeFirstSegments(1);
415: path = Path
416: .fromPortableString("platform:/plugin/").append(path); //$NON-NLS-1$
417: } else {
418: continue;
419: }
420: }
421:
422: if (!path.toPortableString().startsWith("platform:")) { //$NON-NLS-1$
423: addExtraLibrary(path, null, entries);
424: } else {
425: int count = path.getDevice() == null ? 4 : 3;
426: if (path.segmentCount() >= count) {
427: String pluginID = path.segment(count - 2);
428: if (added.contains(pluginID))
429: continue;
430: IPluginModelBase model = PluginRegistry
431: .findModel(pluginID);
432: if (model != null && model.isEnabled()) {
433: path = path.setDevice(null);
434: path = path.removeFirstSegments(count - 1);
435: if (model.getUnderlyingResource() == null) {
436: File file = new File(model
437: .getInstallLocation(), path
438: .toOSString());
439: if (file.exists()) {
440: addExtraLibrary(new Path(file
441: .getAbsolutePath()), model,
442: entries);
443: }
444: } else {
445: IProject project = model
446: .getUnderlyingResource()
447: .getProject();
448: IFile file = project.getFile(path);
449: if (file.exists()) {
450: addExtraLibrary(file.getFullPath(),
451: model, entries);
452: }
453: }
454: }
455: }
456: }
457: }
458: }
459:
460: private void addSecondaryDependencies(BundleDescription desc,
461: HashSet added, ArrayList entries) {
462: try {
463: IBuildEntry entry = fBuild
464: .getEntry(IBuildEntry.SECONDARY_DEPENDENCIES);
465: if (entry != null) {
466: String[] tokens = entry.getTokens();
467: for (int i = 0; i < tokens.length; i++) {
468: String pluginId = tokens[i];
469: if (added.contains(pluginId))
470: continue;
471: // Get PluginModelBase first to resolve system.bundle entry if it exists
472: IPluginModelBase model = PluginRegistry
473: .findModel(pluginId);
474: if (model != null) {
475: Map rules = new HashMap();
476: findExportedPackages(model
477: .getBundleDescription(), desc, rules);
478: if (model != null) {
479: addDependency(model.getBundleDescription(),
480: added, rules, entries, true);
481: }
482: }
483: }
484: }
485: } catch (CoreException e) {
486: return;
487: }
488: }
489:
490: protected final void findExportedPackages(BundleDescription desc,
491: BundleDescription projectDesc, Map map) {
492: if (desc != null) {
493: Stack stack = new Stack();
494: stack.add(desc);
495: while (!stack.isEmpty()) {
496: BundleDescription bdesc = (BundleDescription) stack
497: .pop();
498: ExportPackageDescription[] expkgs = bdesc
499: .getExportPackages();
500: ArrayList rules = new ArrayList();
501: for (int i = 0; i < expkgs.length; i++) {
502: Rule rule = new Rule();
503: rule.discouraged = restrictPackage(projectDesc,
504: expkgs[i]);
505: rule.path = new Path(expkgs[i].getName()
506: .replaceAll("\\.", "/") + "/*"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
507: rules.add(rule);
508: }
509: map.put(bdesc, rules);
510:
511: // Look at re-exported Require-Bundles for any other exported packages
512: BundleSpecification[] requiredBundles = bdesc
513: .getRequiredBundles();
514: for (int i = 0; i < requiredBundles.length; i++)
515: if (requiredBundles[i].isExported()) {
516: BaseDescription bd = requiredBundles[i]
517: .getSupplier();
518: if (bd != null
519: && bd instanceof BundleDescription)
520: stack.add(bd);
521: }
522: }
523: }
524: }
525:
526: private boolean restrictPackage(BundleDescription desc,
527: ExportPackageDescription pkg) {
528: String[] friends = (String[]) pkg
529: .getDirective(ICoreConstants.FRIENDS_DIRECTIVE);
530: if (friends != null) {
531: String symbolicName = desc.getSymbolicName();
532: for (int i = 0; i < friends.length; i++) {
533: if (symbolicName.equals(friends[i]))
534: return false;
535:
536: }
537: return true;
538: }
539: return (((Boolean) pkg
540: .getDirective(ICoreConstants.INTERNAL_DIRECTIVE))
541: .booleanValue());
542: }
543:
544: private void addExtraLibrary(IPath path, IPluginModelBase model,
545: ArrayList entries) throws CoreException {
546: IPath srcPath = null;
547: if (model != null) {
548: IPath shortPath = path.removeFirstSegments(path
549: .matchingFirstSegments(new Path(model
550: .getInstallLocation())));
551: srcPath = ClasspathUtilCore.getSourceAnnotation(model,
552: shortPath.toString());
553: } else {
554: String filename = ClasspathUtilCore.getSourceZipName(path
555: .lastSegment());
556: IPath candidate = path.removeLastSegments(1).append(
557: filename);
558: if (PDECore.getWorkspace().getRoot().getFile(candidate)
559: .exists())
560: srcPath = candidate;
561: }
562: IClasspathEntry clsEntry = JavaCore.newLibraryEntry(path,
563: srcPath, null);
564: if (!entries.contains(clsEntry))
565: entries.add(clsEntry);
566: }
567: }
|