| /******************************************************************************* |
| * Copyright (c) 2007, 2021 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.pde.api.tools.internal; |
| |
| import java.io.File; |
| import java.io.FileFilter; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.nio.charset.StandardCharsets; |
| import java.nio.file.Files; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Set; |
| import java.util.concurrent.ConcurrentHashMap; |
| |
| import javax.xml.parsers.DocumentBuilder; |
| import javax.xml.parsers.DocumentBuilderFactory; |
| import javax.xml.parsers.FactoryConfigurationError; |
| import javax.xml.parsers.ParserConfigurationException; |
| |
| import org.eclipse.core.resources.ISaveContext; |
| import org.eclipse.core.resources.ISaveParticipant; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.core.runtime.preferences.IEclipsePreferences; |
| import org.eclipse.core.runtime.preferences.IPreferencesService; |
| import org.eclipse.core.runtime.preferences.IScopeContext; |
| import org.eclipse.core.runtime.preferences.InstanceScope; |
| import org.eclipse.osgi.service.resolver.BundleDescription; |
| import org.eclipse.pde.api.tools.internal.builder.ApiAnalysisBuilder.ApiAnalysisJob; |
| import org.eclipse.pde.api.tools.internal.model.ApiBaseline; |
| import org.eclipse.pde.api.tools.internal.model.ApiModelCache; |
| import org.eclipse.pde.api.tools.internal.model.ApiModelFactory; |
| import org.eclipse.pde.api.tools.internal.model.StubApiComponent; |
| import org.eclipse.pde.api.tools.internal.model.WorkspaceBaseline; |
| import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin; |
| import org.eclipse.pde.api.tools.internal.provisional.IApiBaselineManager; |
| import org.eclipse.pde.api.tools.internal.provisional.model.IApiBaseline; |
| import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent; |
| import org.eclipse.pde.api.tools.internal.util.Util; |
| import org.eclipse.pde.core.plugin.IPluginModelBase; |
| import org.eclipse.pde.core.plugin.ModelEntry; |
| import org.eclipse.pde.core.plugin.PluginRegistry; |
| import org.eclipse.pde.internal.core.DependencyManager; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.NodeList; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.helpers.DefaultHandler; |
| |
| /** |
| * This manager is used to maintain (persist, restore, access, update) API |
| * baselines. This manager is lazy, in that caches are built and maintained when |
| * requests are made for information, nothing is pre-loaded when the manager is |
| * initialized. |
| * |
| * @since 1.0.0 |
| * @noinstantiate This class is not intended to be instantiated by clients. |
| */ |
| public final class ApiBaselineManager implements IApiBaselineManager, ISaveParticipant { |
| |
| /** |
| * Constant for the default API baseline. Value is: |
| * <code>default_api_profile</code> |
| */ |
| private static final String DEFAULT_BASELINE = "default_api_profile"; //$NON-NLS-1$ |
| |
| /** |
| * Constant representing the id of the workspace {@link IApiBaseline}. Value |
| * is: <code>workspace</code> |
| */ |
| public static final String WORKSPACE_API_BASELINE_ID = "workspace"; //$NON-NLS-1$ |
| |
| /** |
| * Constant representing the file extension for a baseline file. Value is: |
| * <code>.profile</code> |
| */ |
| private static final String BASELINE_FILE_EXTENSION = ".profile"; //$NON-NLS-1$ |
| |
| /** |
| * The main cache for the manager. The form of the cache is: |
| * |
| * <pre> |
| * Map<String(baselineid), {@link IApiBaseline}> |
| * </pre> |
| */ |
| private volatile ConcurrentHashMap<String, IApiBaseline> baselinecache; |
| |
| /** |
| * Cache of baseline names to the location with their infos in it |
| */ |
| private volatile Map<String, String> handlecache; |
| |
| private volatile Set<String> hasinfos; |
| |
| /** |
| * The current default {@link IApiBaseline} |
| */ |
| private String defaultbaseline = null; |
| |
| /** |
| * The current workspace baseline |
| */ |
| private volatile IApiBaseline workspacebaseline; |
| |
| /** |
| * The default save location for persisting the cache from this manager. |
| */ |
| private IPath savelocation = null; |
| |
| /** |
| * If the cache of baselines needs to be saved or not. |
| */ |
| private volatile boolean fNeedsSaving; |
| |
| /** |
| * The singleton instance |
| */ |
| private static ApiBaselineManager fInstance = null; |
| |
| /** |
| * Constructor |
| */ |
| private ApiBaselineManager(boolean framework) { |
| if (framework) { |
| ApiPlugin.getDefault().addSaveParticipant(this); |
| savelocation = ApiPlugin.getDefault().getStateLocation().append(".api_profiles").addTrailingSeparator(); //$NON-NLS-1$ |
| } |
| hasinfos = Collections.emptySet(); |
| } |
| |
| /** |
| * Returns the singleton instance of the manager |
| * |
| * @return the singleton instance of the manager |
| */ |
| public static synchronized ApiBaselineManager getManager() { |
| if (fInstance == null) { |
| fInstance = new ApiBaselineManager(ApiPlugin.isRunningInFramework()); |
| } |
| return fInstance; |
| } |
| |
| @Override |
| public IApiBaseline getApiBaseline(String name) { |
| if (name == null) { |
| return null; |
| } |
| initializeStateCache(); |
| return baselinecache.get(name); |
| } |
| |
| @Override |
| public IApiBaseline[] getApiBaselines() { |
| initializeStateCache(); |
| return baselinecache.values().toArray(new IApiBaseline[0]); |
| } |
| |
| @Override |
| public void addApiBaseline(IApiBaseline newbaseline) { |
| if (newbaseline != null) { |
| initializeStateCache(); |
| baselinecache.put(newbaseline.getName(), newbaseline); |
| if (((ApiBaseline) newbaseline).peekInfos()) { |
| hasinfos.add(newbaseline.getName()); |
| } |
| fNeedsSaving = true; |
| } |
| } |
| |
| @Override |
| public boolean removeApiBaseline(String name) { |
| if (name == null) { |
| return false; |
| } |
| initializeStateCache(); |
| IApiBaseline baseline = baselinecache.remove(name); |
| if (baseline == null) { |
| return false; |
| } |
| synchronized (this) { |
| baseline.dispose(); |
| boolean success = true; |
| if (savelocation == null) { |
| return success; |
| } |
| // remove from filesystem |
| File file = savelocation.append(name + BASELINE_FILE_EXTENSION).toFile(); |
| if (file.exists()) { |
| try { |
| success &= Files.deleteIfExists(file.toPath()); |
| } catch (IOException e) { |
| ApiPlugin.log(e); |
| } |
| } |
| fNeedsSaving = true; |
| |
| // flush the model cache |
| ApiModelCache.getCache().removeElementInfo(baseline); |
| return success; |
| } |
| } |
| |
| /** |
| * Loads the infos for the given baseline from persisted storage (the |
| * *.profile file) |
| * |
| * @param baseline the given baseline |
| * @throws CoreException if an exception occurs while loading baseline infos |
| */ |
| public void loadBaselineInfos(ApiBaseline baseline) throws CoreException { |
| initializeStateCache(); |
| if (isBaselineLoaded(baseline)) { |
| return; |
| } |
| String filename = handlecache.get(baseline.getName()); |
| if (filename != null) { |
| File file = new File(filename); |
| if (file.exists()) { |
| try (FileInputStream inputStream = new FileInputStream(file)) { |
| baseline.restoreFrom(inputStream); |
| } catch (IOException e) { |
| ApiPlugin.log(e); |
| } |
| hasinfos.add(baseline.getName()); |
| } |
| } |
| } |
| |
| public boolean isBaselineLoaded(IApiBaseline baseline) { |
| return hasinfos.contains(baseline.getName()); |
| } |
| |
| /** |
| * Initializes the baseline cache lazily. Only performs work if the current |
| * cache has not been created yet |
| * |
| * @throws FactoryConfigurationError |
| * @throws ParserConfigurationException |
| */ |
| private void initializeStateCache() { |
| if (baselinecache != null) { |
| return; |
| } |
| if (!ApiPlugin.isRunningInFramework()) { |
| synchronized (this) { |
| if (baselinecache == null) { |
| handlecache = new ConcurrentHashMap<>(8); |
| hasinfos = ConcurrentHashMap.newKeySet(8); |
| baselinecache = new ConcurrentHashMap<>(8); |
| } |
| } |
| return; |
| } |
| |
| long time = System.currentTimeMillis(); |
| synchronized (this) { |
| if (baselinecache == null) { |
| handlecache = new ConcurrentHashMap<>(8); |
| hasinfos = ConcurrentHashMap.newKeySet(8); |
| ConcurrentHashMap<String, IApiBaseline> bcache = new ConcurrentHashMap<>(8); |
| File[] baselines = savelocation.toFile().listFiles((FileFilter) pathname -> pathname.getName().endsWith(BASELINE_FILE_EXTENSION)); |
| if (baselines != null) { |
| IApiBaseline newbaseline = null; |
| for (File baseline : baselines) { |
| if (baseline.exists()) { |
| newbaseline = new ApiBaseline(new Path(baseline.getName()).removeFileExtension().toString()); |
| handlecache.put(newbaseline.getName(), baseline.getAbsolutePath()); |
| bcache.put(newbaseline.getName(), newbaseline); |
| } |
| } |
| } |
| String def = getDefaultProfilePref(); |
| if (def != null && bcache.get(def) != null) { |
| defaultbaseline = def; |
| } else { |
| defaultbaseline = null; |
| } |
| baselinecache = bcache; |
| if (ApiPlugin.DEBUG_BASELINE_MANAGER) { |
| System.out.println("Time to initialize state cache: " + (System.currentTimeMillis() - time) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |
| } |
| } |
| |
| /** |
| * @return the default API baseline saved in the preferences, or |
| * <code>null</code> if there isn't one |
| */ |
| private String getDefaultProfilePref() { |
| IPreferencesService service = Platform.getPreferencesService(); |
| return service.getString(ApiPlugin.PLUGIN_ID, DEFAULT_BASELINE, null, new IScopeContext[] { InstanceScope.INSTANCE }); |
| } |
| |
| /** |
| * Persists all of the cached elements to individual xml files named with |
| * the id of the API baseline |
| * |
| * @throws IOException |
| */ |
| private void persistStateCache() throws CoreException, IOException { |
| if (savelocation == null) { |
| return; |
| } |
| IEclipsePreferences node = InstanceScope.INSTANCE.getNode(ApiPlugin.PLUGIN_ID); |
| if (defaultbaseline != null) { |
| node.put(DEFAULT_BASELINE, defaultbaseline); |
| } else { |
| node.remove(DEFAULT_BASELINE); |
| } |
| if (baselinecache != null && !hasinfos.isEmpty()) { |
| File dir = new File(savelocation.toOSString()); |
| Files.createDirectories(dir.toPath()); |
| IApiBaseline baseline = null; |
| for (Entry<String, IApiBaseline> entry : baselinecache.entrySet()) { |
| String id = entry.getKey(); |
| baseline = entry.getValue(); |
| if (!isBaselineLoaded(baseline)) { |
| continue; |
| } |
| File file = savelocation.append(id + BASELINE_FILE_EXTENSION).toFile(); |
| if (!file.exists()) { |
| try { |
| Files.createFile(file.toPath()); |
| } catch (IOException ioe) { |
| ApiPlugin.log(new IOException("Unable to save API baseline with id: '" + id + "'", ioe)); //$NON-NLS-1$ //$NON-NLS-2$ |
| continue; |
| } |
| } |
| try (FileOutputStream fout = new FileOutputStream(file)) { |
| writeBaselineDescription(baseline, fout); |
| // need to save the api baseline state in order to be able |
| // to reload it later |
| handlecache.put(baseline.getName(), file.getAbsolutePath()); |
| fout.flush(); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Writes out the current state of the {@link IApiBaseline} as XML to the |
| * given output stream |
| * |
| * @param stream |
| * @throws CoreException |
| */ |
| private void writeBaselineDescription(IApiBaseline baseline, OutputStream stream) throws CoreException { |
| String xml = getProfileXML(baseline); |
| try { |
| stream.write(xml.getBytes(StandardCharsets.UTF_8)); |
| } catch (IOException e) { |
| abort("Error writing pofile descrition", e); //$NON-NLS-1$ |
| } |
| } |
| |
| /** |
| * Returns an XML description of the given baseline. |
| * |
| * @param baseline the given API baseline |
| * @return XML string representation of the given baseline |
| * @throws CoreException if an exception occurs while retrieving the xml |
| * string representation |
| */ |
| private String getProfileXML(IApiBaseline baseline) throws CoreException { |
| Document document = Util.newDocument(); |
| Element root = document.createElement(IApiXmlConstants.ELEMENT_APIPROFILE); |
| document.appendChild(root); |
| root.setAttribute(IApiXmlConstants.ATTR_NAME, baseline.getName()); |
| root.setAttribute(IApiXmlConstants.ATTR_VERSION, IApiXmlConstants.API_PROFILE_CURRENT_VERSION); |
| String location = baseline.getLocation(); |
| if (location != null) { |
| root.setAttribute(IApiXmlConstants.ATTR_LOCATION, location); |
| } |
| Element celement = null; |
| IApiComponent[] components = baseline.getApiComponents(); |
| for (IApiComponent component : components) { |
| Set<IApiComponent> allComponentSet = new HashSet<>(); |
| // if the baseline has multiple versions, persist all versions |
| Set<IApiComponent> multipleComponents = baseline.getAllApiComponents(component.getSymbolicName()); |
| if (multipleComponents.isEmpty()) { |
| // no multiple version - add the current component |
| allComponentSet.add(component); |
| } else { |
| allComponentSet.addAll(multipleComponents); |
| } |
| for (Iterator<IApiComponent> iterator = allComponentSet.iterator(); iterator.hasNext();) { |
| IApiComponent iApiComponent = iterator.next(); |
| if (!iApiComponent.isSystemComponent()) { |
| celement = document.createElement(IApiXmlConstants.ELEMENT_APICOMPONENT); |
| celement.setAttribute(IApiXmlConstants.ATTR_ID, iApiComponent.getSymbolicName()); |
| celement.setAttribute(IApiXmlConstants.ATTR_VERSION, iApiComponent.getVersion()); |
| celement.setAttribute(IApiXmlConstants.ATTR_LOCATION, new Path(iApiComponent.getLocation()).toPortableString()); |
| root.appendChild(celement); |
| } |
| } |
| // clear the temporary hashset |
| allComponentSet.clear(); |
| } |
| return Util.serializeDocument(document); |
| } |
| |
| /** |
| * Throws a core exception with the given message and underlying exception, |
| * if any. |
| * |
| * @param message error message |
| * @param e underlying exception or <code>null</code> |
| * @throws CoreException |
| */ |
| private static void abort(String message, Throwable e) throws CoreException { |
| throw new CoreException(Status.error(message, e)); |
| } |
| |
| /** |
| * Restore a baseline from the given input stream (persisted baseline). |
| * |
| * @param baseline the given baseline to restore |
| * @param stream the given input stream |
| * @throws CoreException if unable to restore the baseline |
| * @return restored baseline components or null if restore didn't work |
| */ |
| public IApiComponent[] readBaselineComponents(ApiBaseline baseline, InputStream stream) throws CoreException { |
| long start = System.currentTimeMillis(); |
| DocumentBuilder parser = null; |
| IApiComponent[] restored = null; |
| try { |
| parser = DocumentBuilderFactory.newInstance().newDocumentBuilder(); |
| parser.setErrorHandler(new DefaultHandler()); |
| } catch (ParserConfigurationException | FactoryConfigurationError e) { |
| abort("Error restoring API baseline", e); //$NON-NLS-1$ |
| } |
| try { |
| Document document = parser.parse(stream); |
| Element root = document.getDocumentElement(); |
| if (root.getNodeName().equals(IApiXmlConstants.ELEMENT_APIPROFILE)) { |
| String baselineLocation = root.getAttribute(IApiXmlConstants.ATTR_LOCATION); |
| if (baselineLocation != null && !baselineLocation.equals(Util.EMPTY_STRING)) { |
| baseline.setLocation(Path.fromPortableString(baselineLocation).toOSString()); |
| } |
| // un-pooled components |
| NodeList children = root.getElementsByTagName(IApiXmlConstants.ELEMENT_APICOMPONENT); |
| List<IApiComponent> components = new ArrayList<>(); |
| for (int j = 0; j < children.getLength(); j++) { |
| Element componentNode = (Element) children.item(j); |
| // this also contains components in pools, so don't process |
| // them |
| if (componentNode.getParentNode().equals(root)) { |
| String location = componentNode.getAttribute(IApiXmlConstants.ATTR_LOCATION); |
| IApiComponent component = ApiModelFactory.newApiComponent(baseline, Path.fromPortableString(location).toOSString()); |
| if (component != null) { |
| components.add(component); |
| } |
| } |
| } |
| // pooled components - only for xml file with version <= 1 |
| // since version 2, pools have been removed |
| children = root.getElementsByTagName(IApiXmlConstants.ELEMENT_POOL); |
| IApiComponent component = null; |
| for (int j = 0; j < children.getLength(); j++) { |
| String location = ((Element) children.item(j)).getAttribute(IApiXmlConstants.ATTR_LOCATION); |
| IPath poolPath = Path.fromPortableString(location); |
| NodeList componentNodes = root.getElementsByTagName(IApiXmlConstants.ELEMENT_APICOMPONENT); |
| for (int i = 0; i < componentNodes.getLength(); i++) { |
| Element compElement = (Element) componentNodes.item(i); |
| String id = compElement.getAttribute(IApiXmlConstants.ATTR_ID); |
| String ver = compElement.getAttribute(IApiXmlConstants.ATTR_VERSION); |
| StringBuilder name = new StringBuilder(); |
| name.append(id); |
| name.append('_'); |
| name.append(ver); |
| File file = poolPath.append(name.toString()).toFile(); |
| if (!file.exists()) { |
| name.append(".jar"); //$NON-NLS-1$ |
| file = poolPath.append(name.toString()).toFile(); |
| } |
| component = ApiModelFactory.newApiComponent(baseline, file.getAbsolutePath()); |
| if (component != null) { |
| components.add(component); |
| } |
| } |
| } |
| restored = components.toArray(new IApiComponent[components.size()]); |
| } |
| } catch (IOException | SAXException e) { |
| abort("Error restoring API baseline", e); //$NON-NLS-1$ |
| } |
| if (ApiPlugin.DEBUG_BASELINE_MANAGER) { |
| System.out.println("Time to restore a persisted baseline : " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| return restored; |
| } |
| |
| @Override |
| public void saving(ISaveContext context) throws CoreException { |
| if (!fNeedsSaving) { |
| return; |
| } |
| try { |
| persistStateCache(); |
| cleanStateCache(); |
| fNeedsSaving = false; |
| } catch (IOException e) { |
| ApiPlugin.log(e); |
| } |
| } |
| |
| /** |
| * Cleans out all but the default baseline from the in-memory cache of |
| * baselines |
| */ |
| private void cleanStateCache() { |
| if (baselinecache != null) { |
| IApiBaseline baseline = null; |
| for (Entry<String, IApiBaseline> entry : baselinecache.entrySet()) { |
| baseline = entry.getValue(); |
| if (!baseline.getName().equals(defaultbaseline)) { |
| baseline.dispose(); |
| hasinfos.remove(baseline.getName()); |
| // iter.remove(); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Returns if the given name is an existing baseline name |
| * |
| * @param name |
| * @return true if the given name is an existing baseline name, false |
| * otherwise |
| */ |
| public boolean isExistingProfileName(String name) { |
| if (baselinecache == null || name == null) { |
| return false; |
| } |
| return baselinecache.containsKey(name); |
| } |
| |
| /** |
| * Cleans up the manager |
| */ |
| public void stop() { |
| try { |
| Job.getJobManager().cancel(ApiAnalysisJob.class); |
| if (baselinecache != null) { |
| // we should first dispose all existing baselines |
| for (IApiBaseline iApiBaseline : baselinecache.values()) { |
| iApiBaseline.dispose(); |
| } |
| baselinecache.clear(); |
| } |
| synchronized (this) { |
| if (workspacebaseline != null) { |
| workspacebaseline.dispose(); |
| } |
| } |
| if (handlecache != null) { |
| handlecache.clear(); |
| } |
| if (!hasinfos.isEmpty()) { |
| hasinfos.clear(); |
| } |
| StubApiComponent.disposeAllCaches(); |
| } finally { |
| if (ApiPlugin.isRunningInFramework()) { |
| ApiPlugin.getDefault().removeSaveParticipant(this); |
| } |
| } |
| } |
| |
| @Override |
| public void doneSaving(ISaveContext context) { |
| // |
| } |
| |
| @Override |
| public void prepareToSave(ISaveContext context) throws CoreException { |
| // |
| } |
| |
| @Override |
| public void rollback(ISaveContext context) { |
| // |
| } |
| |
| @Override |
| public IApiBaseline getDefaultApiBaseline() { |
| initializeStateCache(); |
| String defbaseline = defaultbaseline; |
| if (defbaseline == null) { |
| return null; |
| } |
| return baselinecache.get(defbaseline); |
| } |
| |
| @Override |
| public void setDefaultApiBaseline(String name) { |
| fNeedsSaving = true; |
| defaultbaseline = name; |
| } |
| |
| @Override |
| public IApiBaseline getWorkspaceBaseline() { |
| if (!ApiPlugin.isRunningInFramework()) { |
| return null; |
| } |
| if (this.workspacebaseline == null) { |
| try { |
| synchronized (this) { |
| if (this.workspacebaseline == null) { |
| this.workspacebaseline = createWorkspaceBaseline(); |
| } |
| } |
| } catch (CoreException e) { |
| ApiPlugin.log(e); |
| } |
| } |
| return this.workspacebaseline; |
| } |
| |
| /** |
| * Disposes the workspace baseline such that a new one will be created on |
| * the next request. |
| */ |
| void disposeWorkspaceBaseline() { |
| if (workspacebaseline == null) { |
| return; |
| } |
| Job.getJobManager().cancel(ApiAnalysisJob.class); |
| IApiBaseline oldBaseline = null; |
| synchronized (this) { |
| if (workspacebaseline != null) { |
| if (ApiPlugin.DEBUG_BASELINE_MANAGER) { |
| System.out.println("disposing workspace baseline"); //$NON-NLS-1$ |
| } |
| oldBaseline = workspacebaseline; |
| StubApiComponent.disposeAllCaches(); |
| workspacebaseline = null; |
| } |
| } |
| if (oldBaseline != null) { |
| oldBaseline.dispose(); |
| } |
| } |
| |
| /** |
| * Creates a workspace {@link IApiBaseline} |
| * |
| * @return a new workspace {@link IApiBaseline} or <code>null</code> |
| */ |
| private IApiBaseline createWorkspaceBaseline() throws CoreException { |
| long time = System.currentTimeMillis(); |
| IApiBaseline baseline = null; |
| try { |
| baseline = new WorkspaceBaseline(); |
| // populate it with only projects that are API aware |
| List<IPluginModelBase> models = Arrays.asList(PluginRegistry.getWorkspaceModels()); |
| Set<BundleDescription> bundles = DependencyManager.getSelfAndDependencies(models); |
| List<IApiComponent> componentsList = new ArrayList<>(bundles.size()); |
| for (BundleDescription bundle : bundles) { |
| String id = bundle.getSymbolicName(); |
| ModelEntry modelEntry = PluginRegistry.findEntry(id); |
| IPluginModelBase[] workspaceModels = modelEntry.getWorkspaceModels(); |
| IApiComponent apiComponent = null; |
| if (workspaceModels.length == 0) { |
| // external model - reexported case |
| IPluginModelBase externalModel = PluginRegistry.findModel(id); |
| if (externalModel != null) { |
| try { |
| apiComponent = ApiModelFactory.newApiComponent(baseline, externalModel); |
| if (apiComponent != null) { |
| componentsList.add(apiComponent); |
| } |
| } catch (CoreException e) { |
| ApiPlugin.log(e); |
| } |
| } |
| continue; |
| } |
| for (IPluginModelBase iPluginModelBase : workspaceModels) { |
| try { |
| apiComponent = ApiModelFactory.newApiComponent(baseline, iPluginModelBase); |
| if (apiComponent != null) { |
| componentsList.add(apiComponent); |
| } |
| } catch (CoreException e) { |
| ApiPlugin.log(e); |
| } |
| } |
| } |
| baseline.addApiComponents(componentsList.toArray(new IApiComponent[componentsList.size()])); |
| } finally { |
| if (ApiPlugin.DEBUG_BASELINE_MANAGER) { |
| System.out.println("Time to create a workspace baseline : " + (System.currentTimeMillis() - time) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |
| return baseline; |
| } |
| |
| @Override |
| public IApiComponent getWorkspaceComponent(String symbolicName) { |
| IApiBaseline baseline = getWorkspaceBaseline(); |
| if (baseline != null) { |
| return baseline.getApiComponent(symbolicName); |
| } |
| return null; |
| } |
| } |