| /******************************************************************************* |
| * Copyright (c) 2004, 2011 QNX Software Systems 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: |
| * QNX Software Systems - initial API and implementation |
| * IBM Corporation |
| *******************************************************************************/ |
| package org.eclipse.cdt.make.internal.core.scannerconfig; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.cdt.core.CCorePlugin; |
| import org.eclipse.cdt.core.model.CoreModel; |
| import org.eclipse.cdt.core.model.ICProject; |
| import org.eclipse.cdt.core.model.PathEntryContainerChanged; |
| import org.eclipse.cdt.make.core.MakeCorePlugin; |
| import org.eclipse.cdt.make.core.scannerconfig.IDiscoveredPathManager; |
| import org.eclipse.cdt.make.core.scannerconfig.IScannerConfigBuilderInfo2; |
| import org.eclipse.cdt.make.core.scannerconfig.IScannerConfigBuilderInfo2Set; |
| import org.eclipse.cdt.make.core.scannerconfig.IScannerInfoCollector; |
| import org.eclipse.cdt.make.core.scannerconfig.IScannerInfoCollector2; |
| import org.eclipse.cdt.make.core.scannerconfig.InfoContext; |
| import org.eclipse.cdt.make.core.scannerconfig.ScannerConfigScope; |
| import org.eclipse.cdt.make.internal.core.MakeMessages; |
| import org.eclipse.cdt.make.internal.core.scannerconfig2.SCProfileInstance; |
| import org.eclipse.cdt.make.internal.core.scannerconfig2.ScannerConfigProfileManager; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IResourceChangeEvent; |
| import org.eclipse.core.resources.IResourceChangeListener; |
| import org.eclipse.core.resources.IWorkspace; |
| import org.eclipse.core.resources.IWorkspaceRunnable; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.ISafeRunnable; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.SafeRunner; |
| import org.eclipse.core.runtime.Status; |
| |
| public class DiscoveredPathManager implements IDiscoveredPathManager, IResourceChangeListener { |
| |
| private Map<IProject, DiscoveredInfoHolder> fDiscoveredInfoHolderMap = new HashMap<>(); |
| private List<IDiscoveredInfoListener> listeners = Collections |
| .synchronizedList(new ArrayList<IDiscoveredInfoListener>()); |
| |
| private static final int INFO_CHANGED = 1; |
| private static final int INFO_REMOVED = 2; |
| |
| private static class DiscoveredInfoHolder { |
| Map<InfoContext, IDiscoveredPathInfo> fInfoMap = new HashMap<>(); |
| // PathSettingsContainer fContainer = PathSettingsContainer.createRootContainer(); |
| |
| public IDiscoveredPathInfo getInfo(InfoContext context) { |
| return fInfoMap.get(context); |
| } |
| |
| // private Map getMap(IPath path, boolean create, boolean exactPath){ |
| // PathSettingsContainer child = fContainer.getChildContainer(path, create, exactPath); |
| // Map map = null; |
| // if(child != null){ |
| // map = (Map)child.getValue(); |
| // if(map == null && create){ |
| // map = new HashMap(); |
| // child.setValue(map); |
| // } |
| // } |
| // |
| // return map; |
| // } |
| |
| // public IDiscoveredPathInfo getInfo(IFile file, String instanceId){ |
| // IPath path = file.getProjectRelativePath(); |
| // Map map = getMap(path, false, false); |
| // for(Iterator iter = map.entrySet().iterator(); iter.hasNext();){ |
| // Map.Entry entry = (Map.Entry)iter.next(); |
| // InfoContext context = (InfoContext)entry.getKey(); |
| // if(context.matches(file)) |
| // return (IDiscoveredPathInfo)entry.getValue(); |
| // } |
| // return null; |
| // } |
| |
| public IDiscoveredPathInfo setInfo(InfoContext context, IDiscoveredPathInfo info) { |
| if (info != null) |
| return fInfoMap.put(context, info); |
| return fInfoMap.remove(context); |
| } |
| |
| } |
| |
| public DiscoveredPathManager() { |
| |
| } |
| |
| public void startup() { |
| ResourcesPlugin.getWorkspace().addResourceChangeListener(this); |
| } |
| |
| public void shutdown() { |
| ResourcesPlugin.getWorkspace().removeResourceChangeListener(this); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent) |
| */ |
| @Override |
| public void resourceChanged(IResourceChangeEvent event) { |
| if (event.getSource() instanceof IWorkspace) { |
| IResource resource = event.getResource(); |
| |
| switch (event.getType()) { |
| case IResourceChangeEvent.POST_CHANGE: |
| DiscoveredScannerInfoStore.getInstance().updateScannerConfigStore(event.getDelta()); |
| break; |
| case IResourceChangeEvent.PRE_DELETE: |
| case IResourceChangeEvent.PRE_CLOSE: |
| if (resource.getType() == IResource.PROJECT) { |
| //TODO: better handlind of resource remove/rename |
| fDiscoveredInfoHolderMap.remove(resource); |
| ScannerConfigProfileManager.getInstance().handleProjectRemoved(resource.getProject()); |
| } |
| break; |
| } |
| } |
| } |
| |
| @Override |
| public IDiscoveredPathInfo getDiscoveredInfo(IProject project) throws CoreException { |
| return getDiscoveredInfo(project, new InfoContext(project)); |
| } |
| |
| @Override |
| public IDiscoveredPathInfo getDiscoveredInfo(IProject project, InfoContext context) throws CoreException { |
| return getDiscoveredInfo(project, context, true); |
| } |
| |
| @Override |
| public IDiscoveredPathInfo getDiscoveredInfo(IProject project, InfoContext context, |
| boolean defaultToProjectSettings) throws CoreException { |
| DiscoveredInfoHolder holder = getHolder(project, true); |
| IDiscoveredPathInfo info = holder.getInfo(context); |
| |
| if (info == null) { |
| info = loadPathInfo(project, context, defaultToProjectSettings); |
| holder.setInfo(context, info); |
| } |
| |
| return info; |
| } |
| |
| private DiscoveredInfoHolder getHolder(IProject project, boolean create) { |
| DiscoveredInfoHolder holder = fDiscoveredInfoHolderMap.get(project); |
| if (holder == null && create) { |
| holder = new DiscoveredInfoHolder(); |
| fDiscoveredInfoHolderMap.put(project, holder); |
| } |
| return holder; |
| } |
| |
| private IDiscoveredPathInfo loadPathInfo(IProject project, InfoContext context, boolean defaultToProjectSettings) |
| throws CoreException { |
| IDiscoveredPathInfo pathInfo = null; |
| |
| IScannerConfigBuilderInfo2Set container = ScannerConfigProfileManager.createScannerConfigBuildInfo2Set(project); |
| IScannerConfigBuilderInfo2 buildInfo = container.getInfo(context); |
| if (buildInfo == null && defaultToProjectSettings) |
| buildInfo = container.getInfo(new InfoContext(project)); |
| |
| if (buildInfo != null) { |
| String profileId = buildInfo.getSelectedProfileId(); |
| SCProfileInstance profileInstance = ScannerConfigProfileManager.getInstance().getSCProfileInstance(project, |
| context, profileId); |
| IScannerInfoCollector collector = profileInstance.getScannerInfoCollector(); |
| |
| if (collector instanceof IScannerInfoCollector2) { |
| IScannerInfoCollector2 collector2 = (IScannerInfoCollector2) collector; |
| pathInfo = collector2.createPathInfoObject(); |
| } |
| } |
| if (pathInfo == null) { |
| pathInfo = new DiscoveredPathInfo(project); |
| } |
| return pathInfo; |
| } |
| |
| // private DiscoveredInfoHolder getHolder |
| |
| // private IDiscoveredPathInfo loadPathInfo(IProject project) throws CoreException { |
| // IDiscoveredPathInfo pathInfo = null; |
| // |
| // IScannerConfigBuilderInfo2 buildInfo = ScannerConfigProfileManager.createScannerConfigBuildInfo2(project); |
| // String profileId = buildInfo.getSelectedProfileId(); |
| // SCProfileInstance profileInstance = ScannerConfigProfileManager.getInstance(). |
| // getSCProfileInstance(project, profileId); |
| // IScannerInfoCollector collector = profileInstance.getScannerInfoCollector(); |
| // |
| // if (collector instanceof IScannerInfoCollector2) { |
| // IScannerInfoCollector2 collector2 = (IScannerInfoCollector2) collector; |
| // pathInfo = collector2.createPathInfoObject(); |
| // } |
| // else { |
| // pathInfo = new DiscoveredPathInfo(project); |
| // } |
| // return pathInfo; |
| // } |
| |
| @Override |
| public void removeDiscoveredInfo(IProject project) { |
| removeDiscoveredInfo(project, new InfoContext(project)); |
| } |
| |
| @Override |
| public void removeDiscoveredInfo(IProject project, InfoContext context) { |
| DiscoveredInfoHolder holder = getHolder(project, false); |
| if (holder != null) { |
| IDiscoveredPathInfo info = holder.setInfo(context, null); |
| if (info != null) { |
| fireUpdate(INFO_REMOVED, info); |
| } |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.make.core.scannerconfig.IDiscoveredPathManager#updateDiscoveredInfo(org.eclipse.cdt.make.core.scannerconfig.IDiscoveredPathManager.IDiscoveredPathInfo, java.util.List) |
| */ |
| @Override |
| public void updateDiscoveredInfo(IDiscoveredPathInfo info, List<IResource> changedResources) throws CoreException { |
| updateDiscoveredInfo(new InfoContext(info.getProject()), info, true, changedResources); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.make.core.scannerconfig.IDiscoveredPathManager#updateDiscoveredInfo(org.eclipse.cdt.make.core.scannerconfig.IDiscoveredPathManager.IDiscoveredPathInfo, java.util.List) |
| */ |
| @Override |
| public void updateDiscoveredInfo(InfoContext context, IDiscoveredPathInfo info, boolean updateContainer, |
| List<IResource> changedResources) throws CoreException { |
| DiscoveredInfoHolder holder = getHolder(info.getProject(), true); |
| IDiscoveredPathInfo oldInfo = holder.getInfo(context); |
| if (oldInfo != null) { |
| IDiscoveredScannerInfoSerializable serializable = info.getSerializable(); |
| if (serializable != null) { |
| holder.setInfo(context, info); |
| IProject project = info.getProject(); |
| DiscoveredScannerInfoStore.getInstance().saveDiscoveredScannerInfoToState(project, context, |
| serializable); |
| fireUpdate(INFO_CHANGED, info); |
| |
| if (updateContainer) { |
| |
| IScannerConfigBuilderInfo2 buildInfo = ScannerConfigProfileManager |
| .createScannerConfigBuildInfo2(project); |
| String profileId = buildInfo.getSelectedProfileId(); |
| ScannerConfigScope profileScope = ScannerConfigProfileManager.getInstance() |
| .getSCProfileConfiguration(profileId).getProfileScope(); |
| changeDiscoveredContainer(project, profileScope, changedResources); |
| } |
| } else { |
| throw new CoreException(new Status(IStatus.ERROR, MakeCorePlugin.getUniqueIdentifier(), -1, |
| MakeMessages.getString("DiscoveredPathManager.Info_Not_Serializable"), null)); //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| /** |
| * Allows one to update the discovered information for a particular scanner discovery profile ID. |
| * TODO: This should be made API in IDiscoveredPathManager, or in an interface derived there from. |
| * |
| * @param context |
| * @param info |
| * @param updateContainer |
| * @param changedResources |
| * @param profileId |
| * @throws CoreException |
| */ |
| public void updateDiscoveredInfo(InfoContext context, IDiscoveredPathInfo info, boolean updateContainer, |
| List<IResource> changedResources, String profileId) throws CoreException { |
| DiscoveredInfoHolder holder = getHolder(info.getProject(), true); |
| IDiscoveredPathInfo oldInfo = holder.getInfo(context); |
| if (oldInfo != null) { |
| IDiscoveredScannerInfoSerializable serializable = info.getSerializable(); |
| if (serializable != null) { |
| holder.setInfo(context, info); |
| IProject project = info.getProject(); |
| DiscoveredScannerInfoStore.getInstance().saveDiscoveredScannerInfoToState(project, context, |
| serializable); |
| fireUpdate(INFO_CHANGED, info); |
| |
| if (updateContainer) { |
| |
| IScannerConfigBuilderInfo2 buildInfo = ScannerConfigProfileManager |
| .createScannerConfigBuildInfo2(project); |
| |
| ScannerConfigScope profileScope = ScannerConfigProfileManager.getInstance() |
| .getSCProfileConfiguration(profileId).getProfileScope(); |
| changeDiscoveredContainer(project, profileScope, changedResources); |
| } |
| } else { |
| throw new CoreException(new Status(IStatus.ERROR, MakeCorePlugin.getUniqueIdentifier(), -1, |
| MakeMessages.getString("DiscoveredPathManager.Info_Not_Serializable"), null)); //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.make.core.scannerconfig.IDiscoveredPathManager#changeDiscoveredContainer(org.eclipse.core.resources.IProject, java.lang.String) |
| */ |
| @Override |
| public void changeDiscoveredContainer(final IProject project, final ScannerConfigScope profileScope, |
| final List<IResource> changedResources) { |
| // order here is of essence |
| // 1. clear DiscoveredPathManager's path info cache |
| DiscoveredInfoHolder holder = getHolder(project, false); |
| InfoContext context = new InfoContext(project); |
| IDiscoveredPathInfo oldInfo = holder.getInfo(context); |
| |
| // 2. switch the containers |
| try { |
| IWorkspaceRunnable runnable = new IWorkspaceRunnable() { |
| @Override |
| public void run(IProgressMonitor monitor) throws CoreException { |
| ICProject cProject = CoreModel.getDefault().create(project); |
| if (ScannerConfigScope.PROJECT_SCOPE.equals(profileScope)) { |
| CoreModel.setPathEntryContainer(new ICProject[] { cProject }, |
| new DiscoveredPathContainer(project), null); |
| } else if (ScannerConfigScope.FILE_SCOPE.equals(profileScope)) { |
| PerFileDiscoveredPathContainer container = new PerFileDiscoveredPathContainer(project); |
| CoreModel.setPathEntryContainer(new ICProject[] { cProject }, container, null); |
| if (changedResources != null) { |
| List<PathEntryContainerChanged> changeDelta = new ArrayList<>(changedResources.size()); |
| for (IResource resource : changedResources) { |
| IPath path = resource.getFullPath(); |
| changeDelta.add( |
| new PathEntryContainerChanged(path, PathEntryContainerChanged.INCLUDE_CHANGED |
| | PathEntryContainerChanged.MACRO_CHANGED)); // both include paths and symbols changed |
| } |
| CoreModel.pathEntryContainerUpdates(container, |
| changeDelta.toArray(new PathEntryContainerChanged[changeDelta.size()]), null); |
| } |
| } else { |
| MakeCorePlugin.log(new Status(IStatus.ERROR, MakeCorePlugin.getUniqueIdentifier(), 1, |
| MakeMessages.getString("DiscoveredContainer.ScopeErrorMessage"), null)); //$NON-NLS-1$ |
| } |
| |
| } |
| }; |
| CoreModel.run(runnable, null); |
| } catch (CoreException e) { |
| MakeCorePlugin.log(e); |
| } |
| |
| // 3. clear the container's path entry cache |
| if (oldInfo != null) { |
| fireUpdate(INFO_REMOVED, oldInfo); |
| } |
| } |
| |
| private void fireUpdate(final int type, final IDiscoveredPathInfo info) { |
| Object[] list = listeners.toArray(); |
| for (int i = 0; i < list.length; i++) { |
| final IDiscoveredInfoListener listener = (IDiscoveredInfoListener) list[i]; |
| if (listener != null) { |
| SafeRunner.run(new ISafeRunnable() { |
| |
| @Override |
| public void handleException(Throwable exception) { |
| IStatus status = new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, -1, |
| CCorePlugin.getResourceString("CDescriptorManager.exception.listenerError"), exception); //$NON-NLS-1$ |
| CCorePlugin.log(status); |
| } |
| |
| @Override |
| public void run() throws Exception { |
| switch (type) { |
| case INFO_CHANGED: |
| listener.infoChanged(info); |
| break; |
| case INFO_REMOVED: |
| listener.infoRemoved(info); |
| break; |
| } |
| } |
| }); |
| } |
| } |
| } |
| |
| @Override |
| public void addDiscoveredInfoListener(IDiscoveredInfoListener listener) { |
| listeners.add(listener); |
| } |
| |
| @Override |
| public void removeDiscoveredInfoListener(IDiscoveredInfoListener listener) { |
| listeners.remove(listener); |
| } |
| } |