| 1 | /******************************************************************************* | 
| 2 |  * Copyright (c) 2007, 2009 IBM Corporation and others. | 
| 3 |  * All rights reserved. This program and the accompanying materials | 
| 4 |  * are made available under the terms of the Eclipse Public License v1.0 | 
| 5 |  * which accompanies this distribution, and is available at | 
| 6 |  * http://www.eclipse.org/legal/epl-v10.html | 
| 7 |  * | 
| 8 |  * Contributors: | 
| 9 |  *     IBM Corporation - initial API and implementation | 
| 10 |  *******************************************************************************/ | 
| 11 | package org.eclipse.pde.api.tools.internal.model; | 
| 12 |   | 
| 13 | import java.io.File; | 
| 14 | import java.io.IOException; | 
| 15 | import java.io.InputStream; | 
| 16 | import java.net.URL; | 
| 17 | import java.net.URLConnection; | 
| 18 | import java.util.ArrayList; | 
| 19 | import java.util.Arrays; | 
| 20 | import java.util.Collection; | 
| 21 | import java.util.Dictionary; | 
| 22 | import java.util.HashMap; | 
| 23 | import java.util.HashSet; | 
| 24 | import java.util.Hashtable; | 
| 25 | import java.util.Iterator; | 
| 26 | import java.util.List; | 
| 27 | import java.util.Map; | 
| 28 | import java.util.Properties; | 
| 29 | import java.util.Set; | 
| 30 | import java.util.Map.Entry; | 
| 31 |   | 
| 32 | import org.eclipse.core.resources.IProject; | 
| 33 | import org.eclipse.core.runtime.CoreException; | 
| 34 | import org.eclipse.core.runtime.FileLocator; | 
| 35 | import org.eclipse.core.runtime.IStatus; | 
| 36 | import org.eclipse.core.runtime.MultiStatus; | 
| 37 | import org.eclipse.core.runtime.Platform; | 
| 38 | import org.eclipse.core.runtime.Status; | 
| 39 | import org.eclipse.jdt.core.JavaCore; | 
| 40 | import org.eclipse.jdt.launching.IVMInstall; | 
| 41 | import org.eclipse.jdt.launching.IVMInstall2; | 
| 42 | import org.eclipse.jdt.launching.IVMInstallChangedListener; | 
| 43 | import org.eclipse.jdt.launching.JavaRuntime; | 
| 44 | import org.eclipse.jdt.launching.PropertyChangeEvent; | 
| 45 | import org.eclipse.jdt.launching.VMStandin; | 
| 46 | import org.eclipse.jdt.launching.environments.ExecutionEnvironmentDescription; | 
| 47 | import org.eclipse.jdt.launching.environments.IExecutionEnvironment; | 
| 48 | import org.eclipse.jdt.launching.environments.IExecutionEnvironmentsManager; | 
| 49 | import org.eclipse.osgi.service.resolver.BundleDescription; | 
| 50 | import org.eclipse.osgi.service.resolver.ExportPackageDescription; | 
| 51 | import org.eclipse.osgi.service.resolver.HostSpecification; | 
| 52 | import org.eclipse.osgi.service.resolver.ResolverError; | 
| 53 | import org.eclipse.osgi.service.resolver.State; | 
| 54 | import org.eclipse.osgi.service.resolver.StateHelper; | 
| 55 | import org.eclipse.osgi.service.resolver.StateObjectFactory; | 
| 56 | import org.eclipse.pde.api.tools.internal.AnyValue; | 
| 57 | import org.eclipse.pde.api.tools.internal.ApiBaselineManager; | 
| 58 | import org.eclipse.pde.api.tools.internal.CoreMessages; | 
| 59 | import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin; | 
| 60 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiBaseline; | 
| 61 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent; | 
| 62 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiElement; | 
| 63 | import org.eclipse.pde.api.tools.internal.util.Util; | 
| 64 | import org.osgi.framework.Bundle; | 
| 65 | import org.osgi.framework.Constants; | 
| 66 |   | 
| 67 | import com.ibm.icu.text.MessageFormat; | 
| 68 |   | 
| 69 | /** | 
| 70 |  * Implementation of an {@link IApiBaseline} | 
| 71 |  *  | 
| 72 |  * @since 1.0 | 
| 73 |  */ | 
| 74 | public class ApiBaseline extends ApiElement implements IApiBaseline, IVMInstallChangedListener { | 
| 75 |          | 
| 76 |         /** | 
| 77 |          * Empty array of component | 
| 78 |          */ | 
| 79 |         private static final IApiComponent[] EMPTY_COMPONENTS = new IApiComponent[0]; | 
| 80 |          | 
| 81 |         /** | 
| 82 |          * OSGi bundle state | 
| 83 |          */ | 
| 84 |         private State fState; | 
| 85 |          | 
| 86 |         /** | 
| 87 |          * Execution environment identifier | 
| 88 |          */ | 
| 89 |         private String fExecutionEnvironment; | 
| 90 |          | 
| 91 |         /** | 
| 92 |          * Component representing the system library | 
| 93 |          */ | 
| 94 |         private IApiComponent fSystemLibraryComponent; | 
| 95 |          | 
| 96 |         /** | 
| 97 |          * Whether an execution environment should be automatically resolved  | 
| 98 |          * as API components are added. | 
| 99 |          */ | 
| 100 |         private boolean fAutoResolve = false; | 
| 101 |          | 
| 102 |         /** | 
| 103 |          * Contains the location of the baseline if the baseline was created with a location. | 
| 104 |          */ | 
| 105 |         private String fLocation; | 
| 106 |         /** | 
| 107 |          * Execution environment status | 
| 108 |          */ | 
| 109 |         private IStatus fEEStatus = null; | 
| 110 |   | 
| 111 |         /** | 
| 112 |          * Constant to match any value for ws, os, arch. | 
| 113 |          */ | 
| 114 |         private AnyValue ANY_VALUE = new AnyValue("*"); //$NON-NLS-1$ | 
| 115 |          | 
| 116 |         /** | 
| 117 |          * Cache of resolved packages.  | 
| 118 |          * <p>Map of <code>PackageName -> Map(componentName -> IApiComponent[])</code></p> | 
| 119 |          * For each package the cache contains a map of API components that provide that package, | 
| 120 |          * by source component name (including the <code>null</code> component name). | 
| 121 |          */ | 
| 122 |         private HashMap fComponentsProvidingPackageCache = null; | 
| 123 |          | 
| 124 |         /** | 
| 125 |          * Maps component id's to components. | 
| 126 |          * <p>Map of <code>componentId -> {@link IApiComponent}</code></p> | 
| 127 |          */ | 
| 128 |         private HashMap fComponentsById = null; | 
| 129 |         /** | 
| 130 |          * Maps project name's to components. | 
| 131 |          * <p>Map of <code>project name -> {@link IApiComponent}</code></p> | 
| 132 |          */ | 
| 133 |         private HashMap fComponentsByProjectNames = null; | 
| 134 |         /** | 
| 135 |          * Cache of system package names | 
| 136 |          */ | 
| 137 |         private HashSet fSystemPackageNames = null; | 
| 138 |          | 
| 139 |         /** | 
| 140 |          * The VM install this baseline is bound to for system libraries or <code>null</code>. | 
| 141 |          * Only used in the IDE when OSGi is running. | 
| 142 |          */ | 
| 143 |         private IVMInstall fVMBinding = null; | 
| 144 |   | 
| 145 |         /** | 
| 146 |          * Constructs a new API baseline with the given name. | 
| 147 |          *  | 
| 148 |          * @param name baseline name | 
| 149 |          */ | 
| 150 |         public ApiBaseline(String name) { | 
| 151 |                 super(null, IApiElement.BASELINE, name); | 
| 152 |                 fAutoResolve = true; | 
| 153 |                 fEEStatus = new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, CoreMessages.ApiBaseline_0); | 
| 154 |         } | 
| 155 |   | 
| 156 |         /** | 
| 157 |          * Constructs a new API baseline with the given attributes. | 
| 158 |          *  | 
| 159 |          * @param name baseline name | 
| 160 |          * @param eeDescriptoin execution environment description file | 
| 161 |          * @throws CoreException if unable to create a baseline with the given attributes | 
| 162 |          */ | 
| 163 |         public ApiBaseline(String name, File eeDescription) throws CoreException { | 
| 164 |                 this(name, eeDescription, null); | 
| 165 |         } | 
| 166 |   | 
| 167 |         /** | 
| 168 |          * Constructs a new API baseline with the given attributes. | 
| 169 |          *  | 
| 170 |          * @param name baseline name | 
| 171 |          * @param eeDescriptoin execution environment description file | 
| 172 |          * @param location the given baseline location | 
| 173 |          * @throws CoreException if unable to create a baseline with the given attributes | 
| 174 |          */ | 
| 175 |         public ApiBaseline(String name, File eeDescription, String location) throws CoreException { | 
| 176 |                 this(name); | 
| 177 |                 if (eeDescription != null) { | 
| 178 |                         fAutoResolve = false; | 
| 179 |                         ExecutionEnvironmentDescription ee = new ExecutionEnvironmentDescription(eeDescription); | 
| 180 |                         String profile = ee.getProperty(ExecutionEnvironmentDescription.CLASS_LIB_LEVEL); | 
| 181 |                         initialize(ee); | 
| 182 |                         fEEStatus = new Status(IStatus.OK, ApiPlugin.PLUGIN_ID, | 
| 183 |                                         MessageFormat.format(CoreMessages.ApiBaseline_1, new String[]{profile})); | 
| 184 |                 } | 
| 185 |                 this.fLocation = location; | 
| 186 |         } | 
| 187 |   | 
| 188 |         /** | 
| 189 |          * Initializes this baseline to resolve in the execution environment | 
| 190 |          * associated with the given description. | 
| 191 |          *  | 
| 192 |          * @param ee execution environment description | 
| 193 |          * @throws CoreException if unable to initialize based on the given id | 
| 194 |          */ | 
| 195 |         private void initialize(ExecutionEnvironmentDescription ee) throws CoreException { | 
| 196 |                 Properties properties = null; | 
| 197 |                 String environmentId = ee.getProperty(ExecutionEnvironmentDescription.CLASS_LIB_LEVEL); | 
| 198 |                 if (ApiPlugin.isRunningInFramework()) { | 
| 199 |                         properties = getJavaProfileProperties(environmentId); | 
| 200 |                 } else { | 
| 201 |                         properties = Util.getEEProfile(environmentId); | 
| 202 |                 } | 
| 203 |                 if (properties == null) { | 
| 204 |                         abort("Unknown execution environment: " + environmentId, null); //$NON-NLS-1$ | 
| 205 |                 } else { | 
| 206 |                         initialize(properties, ee); | 
| 207 |                 } | 
| 208 |         } | 
| 209 |   | 
| 210 |         /** | 
| 211 |          * Returns the property file for the given environment or <code>null</code>. | 
| 212 |          *  | 
| 213 |          * @param ee execution environment symbolic name | 
| 214 |          * @return properties file or <code>null</code> if none | 
| 215 |          */ | 
| 216 |         public static Properties getJavaProfileProperties(String ee) throws CoreException { | 
| 217 |                 Bundle osgiBundle = Platform.getBundle("org.eclipse.osgi"); //$NON-NLS-1$ | 
| 218 |                 if (osgiBundle == null)  | 
| 219 |                         return null; | 
| 220 |                 URL profileURL = osgiBundle.getEntry(ee.replace('/', '_') + ".profile"); //$NON-NLS-1$ | 
| 221 |                 if (profileURL != null) { | 
| 222 |                         InputStream is = null; | 
| 223 |                         try { | 
| 224 |                                 profileURL = FileLocator.resolve(profileURL); | 
| 225 |                                 URLConnection openConnection = profileURL.openConnection(); | 
| 226 |                                 openConnection.setUseCaches(false); | 
| 227 |                                 is = openConnection.getInputStream(); | 
| 228 |                                 if (is != null) { | 
| 229 |                                         Properties profile = new Properties(); | 
| 230 |                                         profile.load(is); | 
| 231 |                                         return profile; | 
| 232 |                                 } | 
| 233 |                         } catch (IOException e) { | 
| 234 |                                 ApiPlugin.log(e); | 
| 235 |                         } finally { | 
| 236 |                                 try { | 
| 237 |                                         if (is != null) { | 
| 238 |                                                 is.close(); | 
| 239 |                                         } | 
| 240 |                                 } catch (IOException e) { | 
| 241 |                                         ApiPlugin.log(e); | 
| 242 |                                 } | 
| 243 |                         } | 
| 244 |                 } | 
| 245 |                 return null; | 
| 246 |         }                 | 
| 247 |          | 
| 248 |         /** | 
| 249 |          * Initializes this baseline from the given properties. | 
| 250 |          *  | 
| 251 |          * @param profile OGSi profile properties | 
| 252 |          * @param description execution environment description | 
| 253 |          * @throws CoreException if unable to initialize | 
| 254 |          */ | 
| 255 |         private void initialize(Properties profile, ExecutionEnvironmentDescription description) throws CoreException { | 
| 256 |                 String value = profile.getProperty(Constants.FRAMEWORK_SYSTEMPACKAGES); | 
| 257 |                 Dictionary dictionary = new Hashtable(); | 
| 258 |                 String[] systemPackages = null; | 
| 259 |                 if (value != null) { | 
| 260 |                         systemPackages = value.split(","); //$NON-NLS-1$ | 
| 261 |                         dictionary.put(Constants.FRAMEWORK_SYSTEMPACKAGES, value); | 
| 262 |                 } | 
| 263 |                 value = profile.getProperty(Constants.FRAMEWORK_EXECUTIONENVIRONMENT); | 
| 264 |                 if (value != null) { | 
| 265 |                         dictionary.put(Constants.FRAMEWORK_EXECUTIONENVIRONMENT, value); | 
| 266 |                 } | 
| 267 |                 fExecutionEnvironment = profile.getProperty("osgi.java.profile.name"); //$NON-NLS-1$ | 
| 268 |                 if (fExecutionEnvironment == null) { | 
| 269 |                         abort("Profile file missing 'osgi.java.profile.name'" , null); //$NON-NLS-1$ | 
| 270 |                 } | 
| 271 |                 dictionary.put("osgi.os", ANY_VALUE); //$NON-NLS-1$ | 
| 272 |                 dictionary.put("osgi.arch", ANY_VALUE); //$NON-NLS-1$ | 
| 273 |                 dictionary.put("osgi.ws", ANY_VALUE); //$NON-NLS-1$ | 
| 274 |                 dictionary.put("osgi.nl", ANY_VALUE); //$NON-NLS-1$ | 
| 275 |                 getState().setPlatformProperties(dictionary); | 
| 276 |                 // clean up previous system library | 
| 277 |                 if (fSystemLibraryComponent != null && fComponentsById != null) { | 
| 278 |                         fComponentsById.remove(fSystemLibraryComponent.getId()); | 
| 279 |                 } | 
| 280 |                 if(fSystemPackageNames != null) { | 
| 281 |                         fSystemPackageNames.clear(); | 
| 282 |                         fSystemPackageNames = null; | 
| 283 |                 } | 
| 284 |                 clearComponentsCache(); | 
| 285 |                 // set new system library | 
| 286 |                 fSystemLibraryComponent = new SystemLibraryApiComponent(this, description, systemPackages); | 
| 287 |                 addComponent(fSystemLibraryComponent); | 
| 288 |         } | 
| 289 |   | 
| 290 |         /** | 
| 291 |          * Clears the package -> components cache and sets it to <code>null</code> | 
| 292 |          */ | 
| 293 |         private synchronized void clearComponentsCache() { | 
| 294 |                 if(fComponentsProvidingPackageCache != null) { | 
| 295 |                         fComponentsProvidingPackageCache.clear(); | 
| 296 |                         fComponentsProvidingPackageCache = null; | 
| 297 |                 } | 
| 298 |         } | 
| 299 |          | 
| 300 |         /** | 
| 301 |          * Adds an {@link IApiComponent} to the fComponentsById mapping | 
| 302 |          * @param component | 
| 303 |          */ | 
| 304 |         private void addComponent(IApiComponent component) throws CoreException { | 
| 305 |                 if(component == null) { | 
| 306 |                         return; | 
| 307 |                 } | 
| 308 |                 if(fComponentsById == null) { | 
| 309 |                         fComponentsById = new HashMap(); | 
| 310 |                 } | 
| 311 |                 fComponentsById.put(component.getId(), component); | 
| 312 |                 if (component instanceof PluginProjectApiComponent) { | 
| 313 |                         PluginProjectApiComponent projectApiComponent = (PluginProjectApiComponent) component; | 
| 314 |                         if (this.fComponentsByProjectNames == null) { | 
| 315 |                                 this.fComponentsByProjectNames = new HashMap(); | 
| 316 |                         } | 
| 317 |                         this.fComponentsByProjectNames.put(projectApiComponent.getJavaProject().getProject().getName(), component); | 
| 318 |                 } | 
| 319 |         } | 
| 320 |          | 
| 321 |         /* (non-Javadoc) | 
| 322 |          * @see IApiBaseline#addApiComponents(org.eclipse.pde.api.tools.model.component.IApiComponent[], boolean) | 
| 323 |          */ | 
| 324 |         public void addApiComponents(IApiComponent[] components) throws CoreException { | 
| 325 |                 HashSet ees = new HashSet(); | 
| 326 |                 for (int i = 0; i < components.length; i++) { | 
| 327 |                         BundleApiComponent component = (BundleApiComponent) components[i]; | 
| 328 |                         if (component.isSourceComponent()) { | 
| 329 |                                 continue; | 
| 330 |                         } | 
| 331 |                         BundleDescription description = component.getBundleDescription(); | 
| 332 |                         getState().addBundle(description); | 
| 333 |                         addComponent(component); | 
| 334 |                         ees.addAll(Arrays.asList(component.getExecutionEnvironments())); | 
| 335 |                 } | 
| 336 |                 resolveSystemLibrary(ees); | 
| 337 |                 getState().resolve(); | 
| 338 |         } | 
| 339 |   | 
| 340 |         /** | 
| 341 |          * Resolves and initializes the system library to use based on API component requirements. | 
| 342 |          * Only works when running in the framework. Has no effect if not running in the framework. | 
| 343 |          */ | 
| 344 |         private void resolveSystemLibrary(HashSet ees) { | 
| 345 |                 if (ApiPlugin.isRunningInFramework() && fAutoResolve) { | 
| 346 |                         IStatus error = null; | 
| 347 |                         IExecutionEnvironmentsManager manager = JavaRuntime.getExecutionEnvironmentsManager(); | 
| 348 |                         Iterator iterator = ees.iterator(); | 
| 349 |                         Map VMsToEEs = new HashMap(); | 
| 350 |                         while (iterator.hasNext()) { | 
| 351 |                                 String ee = (String) iterator.next(); | 
| 352 |                                 IExecutionEnvironment environment = manager.getEnvironment(ee); | 
| 353 |                                 if (environment != null) { | 
| 354 |                                         IVMInstall[] compatibleVMs = environment.getCompatibleVMs(); | 
| 355 |                                         for (int i = 0; i < compatibleVMs.length; i++) { | 
| 356 |                                                 IVMInstall vm = compatibleVMs[i]; | 
| 357 |                                                 Set EEs = (Set) VMsToEEs.get(vm); | 
| 358 |                                                 if (EEs == null) { | 
| 359 |                                                         EEs = new HashSet(); | 
| 360 |                                                         VMsToEEs.put(vm, EEs); | 
| 361 |                                                 } | 
| 362 |                                                 EEs.add(ee); | 
| 363 |                                         } | 
| 364 |                                 } | 
| 365 |                         } | 
| 366 |                         // select VM that is compatible with most required environments | 
| 367 |                         iterator = VMsToEEs.entrySet().iterator(); | 
| 368 |                         IVMInstall bestFit = null; | 
| 369 |                         int bestCount = 0; | 
| 370 |                         while (iterator.hasNext()) { | 
| 371 |                                 Entry entry = (Entry) iterator.next(); | 
| 372 |                                 Set EEs = (Set)entry.getValue(); | 
| 373 |                                 if (EEs.size() > bestCount) { | 
| 374 |                                         bestCount = EEs.size(); | 
| 375 |                                         bestFit = (IVMInstall)entry.getKey(); | 
| 376 |                                 } | 
| 377 |                         } | 
| 378 |                         String systemEE = null; | 
| 379 |                         if (bestFit != null) { | 
| 380 |                                 // find the EE this VM is strictly compatible with | 
| 381 |                                 IExecutionEnvironment[] environments = manager.getExecutionEnvironments(); | 
| 382 |                                 for (int i = 0; i < environments.length; i++) { | 
| 383 |                                         IExecutionEnvironment environment = environments[i]; | 
| 384 |                                         if (environment.isStrictlyCompatible(bestFit)) { | 
| 385 |                                                 systemEE = environment.getId(); | 
| 386 |                                                 break; | 
| 387 |                                         } | 
| 388 |                                 } | 
| 389 |                                 if (systemEE == null) { | 
| 390 |                                         // a best fit, but not strictly compatible with any environment (e.g. | 
| 391 |                                         // a 1.7 VM for which there is no profile yet). This is a bit of a hack | 
| 392 |                                         // until an OSGi profile exists for 1.7. | 
| 393 |                                         if (bestFit instanceof IVMInstall2) { | 
| 394 |                                     String javaVersion = ((IVMInstall2)bestFit).getJavaVersion(); | 
| 395 |                                     if (javaVersion != null) { | 
| 396 |                                             if (javaVersion.startsWith(JavaCore.VERSION_1_7)) { | 
| 397 |                                                     // set EE to 1.6 when 1.7 is detected | 
| 398 |                                                     systemEE = "JavaSE-1.6"; //$NON-NLS-1$ | 
| 399 |                                             } | 
| 400 |                                     } | 
| 401 |                                         } | 
| 402 |                                 } | 
| 403 |                                 if (systemEE != null) { | 
| 404 |                                         // only update if different from current or missing VM binding | 
| 405 |                                         if (!systemEE.equals(getExecutionEnvironment()) || fVMBinding == null) { | 
| 406 |                                                 try { | 
| 407 |                                                         File file = Util.createEEFile(bestFit, systemEE); | 
| 408 |                                                         JavaRuntime.addVMInstallChangedListener(this); | 
| 409 |                                                         fVMBinding = bestFit; | 
| 410 |                                                         ExecutionEnvironmentDescription ee = new ExecutionEnvironmentDescription(file); | 
| 411 |                                                         initialize(ee); | 
| 412 |                                                 } catch (CoreException e) { | 
| 413 |                                                         error = new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, CoreMessages.ApiBaseline_2, e); | 
| 414 |                                                 } catch (IOException e) { | 
| 415 |                                                         error = new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, CoreMessages.ApiBaseline_2, e); | 
| 416 |                                                 } | 
| 417 |                                         } | 
| 418 |                                 } else { | 
| 419 |                                         // VM is not strictly compatible with any EE | 
| 420 |                                         error = new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, CoreMessages.ApiBaseline_3); | 
| 421 |                                 } | 
| 422 |                         } else { | 
| 423 |                                 // no VMs match any required EE | 
| 424 |                                 error = new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, CoreMessages.ApiBaseline_3); | 
| 425 |                         } | 
| 426 |                         if (error == null) { | 
| 427 |                                 // build status for unbound required EE's | 
| 428 |                                 Set missing = new HashSet(ees); | 
| 429 |                                 Set covered = new HashSet((Set)VMsToEEs.get(bestFit)); | 
| 430 |                                 missing.removeAll(covered); | 
| 431 |                                 if (missing.isEmpty()) { | 
| 432 |                                         fEEStatus = new Status(IStatus.OK, ApiPlugin.PLUGIN_ID, | 
| 433 |                                                         MessageFormat.format(CoreMessages.ApiBaseline_1, new String[]{systemEE})); | 
| 434 |                                 } else { | 
| 435 |                                         iterator = missing.iterator(); | 
| 436 |                                         MultiStatus multi = new MultiStatus(ApiPlugin.PLUGIN_ID, 0, CoreMessages.ApiBaseline_4, null); | 
| 437 |                                         while (iterator.hasNext()) { | 
| 438 |                                                 String id = (String) iterator.next(); | 
| 439 |                                                 multi.add(new Status(IStatus.WARNING, ApiPlugin.PLUGIN_ID, | 
| 440 |                                                                 MessageFormat.format(CoreMessages.ApiBaseline_5, new String[]{id}))); | 
| 441 |                                         } | 
| 442 |                                         fEEStatus = multi; | 
| 443 |                                 } | 
| 444 |                         } else { | 
| 445 |                                 fEEStatus = error; | 
| 446 |                         } | 
| 447 |                 } | 
| 448 |         } | 
| 449 |   | 
| 450 |         /** | 
| 451 |          * Returns true if the {@link IApiBaseline} has infos loaded (components) false otherwise. | 
| 452 |          * This is a handle only method that will not load infos. | 
| 453 |          *  | 
| 454 |          * @return true if the {@link IApiBaseline} has infos loaded (components) false otherwise. | 
| 455 |          */ | 
| 456 |         public boolean peekInfos() { | 
| 457 |                 return fComponentsById != null; | 
| 458 |         } | 
| 459 |          | 
| 460 |         /* (non-Javadoc) | 
| 461 |          * @see IApiBaseline#getApiComponents() | 
| 462 |          */ | 
| 463 |         public IApiComponent[] getApiComponents() { | 
| 464 |                 loadBaselineInfos(); | 
| 465 |                 if(fComponentsById == null) { | 
| 466 |                         return EMPTY_COMPONENTS; | 
| 467 |                 } | 
| 468 |                 Collection values = fComponentsById.values(); | 
| 469 |                 return (IApiComponent[]) values.toArray(new IApiComponent[values.size()]); | 
| 470 |         } | 
| 471 |          | 
| 472 |         /* (non-Javadoc) | 
| 473 |          * @see IApiBaseline#resolvePackage(IApiComponent, String) | 
| 474 |          */ | 
| 475 |         public synchronized IApiComponent[] resolvePackage(IApiComponent sourceComponent, String packageName) throws CoreException { | 
| 476 |                 HashMap componentsForPackage = null; | 
| 477 |                 if(fComponentsProvidingPackageCache != null){ | 
| 478 |                         componentsForPackage = (HashMap) fComponentsProvidingPackageCache.get(packageName); | 
| 479 |                 } | 
| 480 |                 else { | 
| 481 |                         fComponentsProvidingPackageCache = new HashMap(8); | 
| 482 |                 } | 
| 483 |                 IApiComponent[] cachedComponents = null; | 
| 484 |                 if (componentsForPackage != null) { | 
| 485 |                         cachedComponents = (IApiComponent[]) componentsForPackage.get(sourceComponent); | 
| 486 |                         if (cachedComponents != null && cachedComponents.length > 0) { | 
| 487 |                                 return cachedComponents; | 
| 488 |                         } | 
| 489 |                 } else { | 
| 490 |                         componentsForPackage = new HashMap(8); | 
| 491 |                         fComponentsProvidingPackageCache.put(packageName, componentsForPackage); | 
| 492 |                 } | 
| 493 |                 // check system packages first | 
| 494 |                 if (isSystemPackage(packageName)) { | 
| 495 |                         if (fSystemLibraryComponent != null) { | 
| 496 |                                 cachedComponents = new IApiComponent[] { fSystemLibraryComponent }; | 
| 497 |                         } else { | 
| 498 |                                 return EMPTY_COMPONENTS; | 
| 499 |                         } | 
| 500 |                 } else { | 
| 501 |                         if (sourceComponent != null) { | 
| 502 |                                 List componentsList = new ArrayList(); | 
| 503 |                                 resolvePackage0(sourceComponent, packageName, componentsList); | 
| 504 |                                 if (componentsList.size() != 0) { | 
| 505 |                                         cachedComponents = new IApiComponent[componentsList.size()]; | 
| 506 |                                         componentsList.toArray(cachedComponents); | 
| 507 |                                 } | 
| 508 |                         } | 
| 509 |                 } | 
| 510 |                 if (cachedComponents == null) { | 
| 511 |                         cachedComponents = EMPTY_COMPONENTS; | 
| 512 |                 } | 
| 513 |                 if(cachedComponents.length == 0) { | 
| 514 |                         return EMPTY_COMPONENTS; | 
| 515 |                 } | 
| 516 |                 componentsForPackage.put(sourceComponent, cachedComponents); | 
| 517 |                 return cachedComponents; | 
| 518 |         } | 
| 519 |   | 
| 520 |         /** | 
| 521 |          * Resolves the listing of {@link IApiComponent}s that export the given package name. The collection  | 
| 522 |          * of {@link IApiComponent}s is written into the specified list <code>componentList</code>  | 
| 523 |          * @param component | 
| 524 |          * @param packageName | 
| 525 |          * @param componentsList | 
| 526 |          * @throws CoreException | 
| 527 |          */ | 
| 528 |         private void resolvePackage0(IApiComponent component, String packageName, List componentsList) throws CoreException { | 
| 529 |                 if (component instanceof BundleApiComponent) { | 
| 530 |                         BundleDescription bundle = ((BundleApiComponent)component).getBundleDescription(); | 
| 531 |                         if (bundle != null) { | 
| 532 |                                 StateHelper helper = getState().getStateHelper(); | 
| 533 |                                 ExportPackageDescription[] visiblePackages = helper.getVisiblePackages(bundle); | 
| 534 |                                 for (int i = 0, max = visiblePackages.length; i < max; i++) { | 
| 535 |                                         ExportPackageDescription pkg = visiblePackages[i]; | 
| 536 |                                         if (packageName.equals(pkg.getName())) { | 
| 537 |                                                 BundleDescription bundleDescription = pkg.getExporter(); | 
| 538 |                                                 IApiComponent exporter = getApiComponent(bundleDescription.getSymbolicName()); | 
| 539 |                                                 if (exporter != null) { | 
| 540 |                                                         componentsList.add(exporter); | 
| 541 |                                                 } | 
| 542 |                                         } | 
| 543 |                                 } | 
| 544 |                                 if (component.isFragment()) { | 
| 545 |                                         // a fragment can see all the packages from the host | 
| 546 |                                         HostSpecification host = bundle.getHost(); | 
| 547 |                                         BundleDescription[] hosts = host.getHosts(); | 
| 548 |                                         for (int i = 0, max = hosts.length; i < max; i++) { | 
| 549 |                                                 BundleDescription currentHost = hosts[i]; | 
| 550 |                                                 IApiComponent apiComponent = component.getBaseline().getApiComponent(currentHost.getName()); | 
| 551 |                                                 if (apiComponent != null) { | 
| 552 |                                                         resolvePackage0(apiComponent, packageName, componentsList); | 
| 553 |                                                 } | 
| 554 |                                         } | 
| 555 |                                 } | 
| 556 |                                 // check for package within the source component | 
| 557 |                                 String[] packageNames = component.getPackageNames(); | 
| 558 |                                 int index = Arrays.binarySearch(packageNames, packageName, null); | 
| 559 |                                 if (index >= 0) { | 
| 560 |                                         componentsList.add(component); | 
| 561 |                                 } | 
| 562 |                         } | 
| 563 |                 } | 
| 564 |         } | 
| 565 |          | 
| 566 |         /** | 
| 567 |          * Returns all of the visible dependent components from the current state | 
| 568 |          *  | 
| 569 |          * @param components | 
| 570 |          * @return the listing of visible dependent components to the given ones | 
| 571 |          * @throws CoreException | 
| 572 |          */ | 
| 573 |         public IApiComponent[] getVisibleDependentComponents(IApiComponent[] components) throws CoreException { | 
| 574 |                 ArrayList bundles = getBundleDescriptions(components); | 
| 575 |                 BundleDescription[] descs = getState().getStateHelper().getDependentBundles((BundleDescription[]) bundles.toArray(new BundleDescription[bundles.size()])); | 
| 576 |                 HashSet visible = new HashSet(); | 
| 577 |                 ExportPackageDescription[] packages = null; | 
| 578 |                 for (int i = 0; i < descs.length; i++) { | 
| 579 |                         packages = getState().getStateHelper().getVisiblePackages(descs[i]); | 
| 580 |                         for (int j = 0; j < packages.length; j++) { | 
| 581 |                                 if(bundles.contains(packages[j].getSupplier())) { | 
| 582 |                                         visible.add(descs[i]); | 
| 583 |                                 } | 
| 584 |                         } | 
| 585 |                 } | 
| 586 |                 return getApiComponents((BundleDescription[]) visible.toArray(new BundleDescription[visible.size()])); | 
| 587 |         } | 
| 588 |          | 
| 589 |         /** | 
| 590 |          * Returns whether the specified package is supplied by the system | 
| 591 |          * library. | 
| 592 |          *  | 
| 593 |          * @param packageName package name | 
| 594 |          * @return whether the specified package is supplied by the system | 
| 595 |          *         library  | 
| 596 |          */ | 
| 597 |         private boolean isSystemPackage(String packageName) { | 
| 598 |                 if (packageName.startsWith("java.")) { //$NON-NLS-1$ | 
| 599 |                         return true; | 
| 600 |                 } | 
| 601 |                 if (fSystemPackageNames == null) { | 
| 602 |                         ExportPackageDescription[] systemPackages = getState().getSystemPackages(); | 
| 603 |                         fSystemPackageNames = new HashSet(systemPackages.length); | 
| 604 |                         for (int i = 0; i < systemPackages.length; i++) { | 
| 605 |                                 fSystemPackageNames.add(systemPackages[i].getName()); | 
| 606 |                         } | 
| 607 |                 } | 
| 608 |                 return fSystemPackageNames.contains(packageName); | 
| 609 |         } | 
| 610 |          | 
| 611 |         /** | 
| 612 |          * @return the OSGi state for this {@link IApiProfile} | 
| 613 |          * @nooverride This method is not intended to be re-implemented or extended by clients. | 
| 614 |          * @noreference This method is not intended to be referenced by clients. | 
| 615 |          */ | 
| 616 |         public State getState() { | 
| 617 |                 if(fState == null) { | 
| 618 |                         fState = StateObjectFactory.defaultFactory.createState(true); | 
| 619 |                 } | 
| 620 |                 return fState; | 
| 621 |         } | 
| 622 |          | 
| 623 |         /* (non-Javadoc) | 
| 624 |          * @see IApiBaseline#getApiComponent(String) | 
| 625 |          */ | 
| 626 |         public IApiComponent getApiComponent(String id) { | 
| 627 |                 loadBaselineInfos(); | 
| 628 |                 if(fComponentsById == null) { | 
| 629 |                         return null; | 
| 630 |                 } | 
| 631 |                 return (IApiComponent) fComponentsById.get(id); | 
| 632 |         } | 
| 633 |   | 
| 634 |         /* (non-Javadoc) | 
| 635 |          * @see IApiBaseline#getExecutionEnvironment() | 
| 636 |          */ | 
| 637 |         public String getExecutionEnvironment() { | 
| 638 |                 return fExecutionEnvironment; | 
| 639 |         } | 
| 640 |          | 
| 641 |         /** | 
| 642 |          * Loads the infos from the *.profile file the first time the baseline is accessed | 
| 643 |          */ | 
| 644 |         private void loadBaselineInfos() { | 
| 645 |                 if(fComponentsById != null) { | 
| 646 |                         return; | 
| 647 |                 } | 
| 648 |                 try { | 
| 649 |                         ApiBaselineManager.getManager().loadBaselineInfos(this); | 
| 650 |                 } | 
| 651 |                 catch(CoreException ce) { | 
| 652 |                         ApiPlugin.log(ce); | 
| 653 |                 } | 
| 654 |         } | 
| 655 |          | 
| 656 |         /** | 
| 657 |          * Returns all errors in the state. | 
| 658 |          *  | 
| 659 |          * @return state errors | 
| 660 |          * @nooverride This method is not intended to be re-implemented or extended by clients. | 
| 661 |          * @noreference This method is not intended to be referenced by clients. | 
| 662 |          */ | 
| 663 |         public ResolverError[] getErrors() { | 
| 664 |                 List errs = null; | 
| 665 |                 BundleDescription[] bundles = getState().getBundles(); | 
| 666 |                 for (int i = 0; i < bundles.length; i++) { | 
| 667 |                         ResolverError[] errors = getState().getResolverErrors(bundles[i]); | 
| 668 |                         for (int j = 0; j < errors.length; j++) { | 
| 669 |                                 if (errs == null) { | 
| 670 |                                         errs = new ArrayList(); | 
| 671 |                                 } | 
| 672 |                                 errs.add(errors[j]); | 
| 673 |                         } | 
| 674 |                 } | 
| 675 |                 if (errs != null) { | 
| 676 |                         return (ResolverError[]) errs.toArray(new ResolverError[errs.size()]); | 
| 677 |                 } | 
| 678 |                 return null; | 
| 679 |         } | 
| 680 |         /** | 
| 681 |          * @see org.eclipse.pde.api.tools.internal.model.ApiElement#setName(java.lang.String) | 
| 682 |          */ | 
| 683 |         public void setName(String name) { | 
| 684 |                 super.setName(name); | 
| 685 |         } | 
| 686 |          | 
| 687 |         /** | 
| 688 |          * @see java.lang.Object#equals(java.lang.Object) | 
| 689 |          */ | 
| 690 |         public boolean equals(Object obj) { | 
| 691 |                 if(obj instanceof IApiBaseline) { | 
| 692 |                         IApiBaseline baseline = (IApiBaseline) obj; | 
| 693 |                         return this.getName().equals(baseline.getName()); | 
| 694 |                 } | 
| 695 |                 return super.equals(obj); | 
| 696 |         } | 
| 697 |          | 
| 698 |         /** | 
| 699 |          * @see java.lang.Object#hashCode() | 
| 700 |          */ | 
| 701 |         public int hashCode() { | 
| 702 |                 return this.getName().hashCode(); | 
| 703 |         } | 
| 704 |          | 
| 705 |         /* (non-Javadoc) | 
| 706 |          * @see IApiBaseline#dispose() | 
| 707 |          */ | 
| 708 |         public void dispose() { | 
| 709 |                 if(fState == null) { | 
| 710 |                         //already disposed or nothing to dispose | 
| 711 |                         return; | 
| 712 |                 } | 
| 713 |                 if (ApiPlugin.isRunningInFramework()) { | 
| 714 |                         JavaRuntime.removeVMInstallChangedListener(this); | 
| 715 |                 } | 
| 716 |                 clearCachedElements(); | 
| 717 |                 IApiComponent[] components = getApiComponents(); | 
| 718 |                 for (int i = 0; i < components.length; i++) { | 
| 719 |                         components[i].dispose(); | 
| 720 |                 } | 
| 721 |                 clearComponentsCache(); | 
| 722 |                 if(fComponentsById != null) { | 
| 723 |                         fComponentsById.clear(); | 
| 724 |                         fComponentsById = null; | 
| 725 |                 } | 
| 726 |                 if(fComponentsByProjectNames != null) { | 
| 727 |                         fComponentsByProjectNames.clear(); | 
| 728 |                         fComponentsByProjectNames = null; | 
| 729 |                 } | 
| 730 |                 if (fSystemPackageNames != null) { | 
| 731 |                         fSystemPackageNames.clear(); | 
| 732 |                 } | 
| 733 |                 if(fSystemLibraryComponent != null) { | 
| 734 |                         fSystemLibraryComponent.dispose(); | 
| 735 |                         fSystemLibraryComponent = null; | 
| 736 |                 } | 
| 737 |                 fState = null; | 
| 738 |         } | 
| 739 |   | 
| 740 |         /** | 
| 741 |          * @see org.eclipse.pde.api.tools.internal.provisional.model.IApiBaseline#close() | 
| 742 |          */ | 
| 743 |         public void close() throws CoreException { | 
| 744 |                 clearCachedElements(); | 
| 745 |                 IApiComponent[] components = getApiComponents(); | 
| 746 |                 for (int i = 0; i < components.length; i++) { | 
| 747 |                         components[i].close(); | 
| 748 |                 } | 
| 749 |         } | 
| 750 |   | 
| 751 |         /** | 
| 752 |          * Clears all element infos from the cache for this baseline | 
| 753 |          * @since 1.1 | 
| 754 |          */ | 
| 755 |         void clearCachedElements() { | 
| 756 |                 ApiModelCache.getCache().removeElementInfo(this); | 
| 757 |         } | 
| 758 |          | 
| 759 |         /* (non-Javadoc) | 
| 760 |          * @see IApiBaseline#getDependentComponents(IApiComponent[]) | 
| 761 |          */ | 
| 762 |         public IApiComponent[] getDependentComponents(IApiComponent[] components) throws CoreException { | 
| 763 |                 ArrayList bundles = getBundleDescriptions(components); | 
| 764 |                 BundleDescription[] bundleDescriptions = getState().getStateHelper().getDependentBundles((BundleDescription[]) bundles.toArray(new BundleDescription[bundles.size()])); | 
| 765 |                 return getApiComponents(bundleDescriptions); | 
| 766 |         } | 
| 767 |   | 
| 768 |         /** | 
| 769 |          * Returns an array of API components corresponding to the given bundle descriptions. | 
| 770 |          *  | 
| 771 |          * @param bundles bundle descriptions | 
| 772 |          * @return corresponding API components | 
| 773 |          */ | 
| 774 |         private IApiComponent[] getApiComponents(BundleDescription[] bundles) { | 
| 775 |                 ArrayList dependents = new ArrayList(bundles.length); | 
| 776 |                 for (int i = 0; i < bundles.length; i++) { | 
| 777 |                         BundleDescription bundle = bundles[i]; | 
| 778 |                         IApiComponent component = getApiComponent(bundle.getSymbolicName()); | 
| 779 |                         if (component != null) { | 
| 780 |                                 dependents.add(component); | 
| 781 |                         } | 
| 782 |                 } | 
| 783 |                 return (IApiComponent[]) dependents.toArray(new IApiComponent[dependents.size()]); | 
| 784 |         } | 
| 785 |   | 
| 786 |         /** | 
| 787 |          * Returns an array of bundle descriptions corresponding to the given API components. | 
| 788 |          *  | 
| 789 |          * @param components API components | 
| 790 |          * @return corresponding bundle descriptions | 
| 791 |          */ | 
| 792 |         private ArrayList getBundleDescriptions(IApiComponent[] components) throws CoreException { | 
| 793 |                 ArrayList bundles = new ArrayList(components.length); | 
| 794 |                 for (int i = 0; i < components.length; i++) { | 
| 795 |                         IApiComponent component = components[i]; | 
| 796 |                         if (component instanceof BundleApiComponent) { | 
| 797 |                                 bundles.add(((BundleApiComponent)component).getBundleDescription()); | 
| 798 |                         } | 
| 799 |                 } | 
| 800 |                 return bundles; | 
| 801 |         } | 
| 802 |   | 
| 803 |         /* (non-Javadoc) | 
| 804 |          * @see org.eclipse.pde.api.tools.IApiBaseline#getPrerequisiteComponents(org.eclipse.pde.api.tools.IApiComponent[]) | 
| 805 |          */ | 
| 806 |         public IApiComponent[] getPrerequisiteComponents(IApiComponent[] components) throws CoreException { | 
| 807 |                 ArrayList bundles = getBundleDescriptions(components); | 
| 808 |                 BundleDescription[] bundlesDescriptions = getState().getStateHelper().getPrerequisites((BundleDescription[]) bundles.toArray(new BundleDescription[bundles.size()])); | 
| 809 |                 return getApiComponents(bundlesDescriptions); | 
| 810 |         } | 
| 811 |   | 
| 812 |         /** | 
| 813 |          * Clear cached settings for the given package. | 
| 814 |          *  | 
| 815 |          * @param packageName | 
| 816 |          * @noreference This method is not intended to be referenced by clients. | 
| 817 |          * @nooverride This method is not intended to be re-implemented or extended by clients. | 
| 818 |          */ | 
| 819 |         public synchronized void clearPackage(String packageName) { | 
| 820 |                 if(fComponentsProvidingPackageCache != null) { | 
| 821 |                         fComponentsProvidingPackageCache.remove(packageName); | 
| 822 |                 } | 
| 823 |         } | 
| 824 |          | 
| 825 |         /* (non-Javadoc) | 
| 826 |          * @see java.lang.Object#toString() | 
| 827 |          */ | 
| 828 |         public String toString() { | 
| 829 |                 return getName(); | 
| 830 |         } | 
| 831 |   | 
| 832 |         /* (non-Javadoc) | 
| 833 |          * @see IApiBaseline#getExecutionEnvironmentStatus() | 
| 834 |          */ | 
| 835 |         public IStatus getExecutionEnvironmentStatus() { | 
| 836 |                 return fEEStatus; | 
| 837 |         } | 
| 838 |   | 
| 839 |         /* (non-Javadoc) | 
| 840 |          * @see org.eclipse.jdt.launching.IVMInstallChangedListener#defaultVMInstallChanged(org.eclipse.jdt.launching.IVMInstall, org.eclipse.jdt.launching.IVMInstall) | 
| 841 |          */ | 
| 842 |         public void defaultVMInstallChanged(IVMInstall previous, IVMInstall current) {} | 
| 843 |   | 
| 844 |         /* (non-Javadoc) | 
| 845 |          * @see org.eclipse.jdt.launching.IVMInstallChangedListener#vmAdded(org.eclipse.jdt.launching.IVMInstall) | 
| 846 |          */ | 
| 847 |         public void vmAdded(IVMInstall vm) { | 
| 848 |                 if (!(vm instanceof VMStandin)) { | 
| 849 |                         // there may be a better fit for VMs/EEs | 
| 850 |                         try { | 
| 851 |                                 rebindVM(); | 
| 852 |                         } catch (CoreException e) { | 
| 853 |                                 ApiPlugin.log(e); | 
| 854 |                         } | 
| 855 |                 } | 
| 856 |         } | 
| 857 |   | 
| 858 |         /* (non-Javadoc) | 
| 859 |          * @see org.eclipse.jdt.launching.IVMInstallChangedListener#vmChanged(org.eclipse.jdt.launching.PropertyChangeEvent) | 
| 860 |          */ | 
| 861 |         public void vmChanged(PropertyChangeEvent event) { | 
| 862 |                 if (!(event.getSource() instanceof VMStandin)) { | 
| 863 |                         String property = event.getProperty(); | 
| 864 |                         if (IVMInstallChangedListener.PROPERTY_INSTALL_LOCATION.equals(property) || | 
| 865 |                                         IVMInstallChangedListener.PROPERTY_LIBRARY_LOCATIONS.equals(property)) { | 
| 866 |                                 try { | 
| 867 |                                         rebindVM(); | 
| 868 |                                 } catch (CoreException e) { | 
| 869 |                                         ApiPlugin.log(e); | 
| 870 |                                 } | 
| 871 |                         } | 
| 872 |                 } | 
| 873 |         } | 
| 874 |   | 
| 875 |         /** | 
| 876 |          * Re-binds the VM this baseline is bound to. | 
| 877 |          */ | 
| 878 |         private void rebindVM() throws CoreException { | 
| 879 |                 fVMBinding = null; | 
| 880 |                 IApiComponent[] components = getApiComponents(); | 
| 881 |                 HashSet ees = new HashSet(); | 
| 882 |                 for (int i = 0; i < components.length; i++) { | 
| 883 |                         ees.addAll(Arrays.asList(components[i].getExecutionEnvironments())); | 
| 884 |                 } | 
| 885 |                 resolveSystemLibrary(ees); | 
| 886 |         } | 
| 887 |          | 
| 888 |         /* (non-Javadoc) | 
| 889 |          * @see org.eclipse.jdt.launching.IVMInstallChangedListener#vmRemoved(org.eclipse.jdt.launching.IVMInstall) | 
| 890 |          */ | 
| 891 |         public void vmRemoved(IVMInstall vm) { | 
| 892 |                 if (vm.equals(fVMBinding)) { | 
| 893 |                         try { | 
| 894 |                                 rebindVM(); | 
| 895 |                         } catch (CoreException e) { | 
| 896 |                                 ApiPlugin.log(e); | 
| 897 |                         } | 
| 898 |                 } | 
| 899 |         } | 
| 900 |          | 
| 901 |         /* (non-Javadoc) | 
| 902 |          * @see IApiBaseline#getLocation() | 
| 903 |          */ | 
| 904 |         public String getLocation() { | 
| 905 |                 return this.fLocation; | 
| 906 |         } | 
| 907 |   | 
| 908 |         /* (non-Javadoc) | 
| 909 |          * @see IApiBaseline#setLocation(String) | 
| 910 |          */ | 
| 911 |         public void setLocation(String location) { | 
| 912 |                 this.fLocation = location; | 
| 913 |         } | 
| 914 |         public IApiComponent getApiComponent(IProject project) { | 
| 915 |                 loadBaselineInfos(); | 
| 916 |                 if(fComponentsByProjectNames == null) { | 
| 917 |                         return null; | 
| 918 |                 } | 
| 919 |                 return (IApiComponent) fComponentsByProjectNames.get(project.getName()); | 
| 920 |         } | 
| 921 | } |