/******************************************************************************* * Copyright (c) 2007, 2012 Intel Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Intel Corporation - Initial API and implementation * IBM Corporation *******************************************************************************/ package org.eclipse.cdt.build.internal.core.scannerconfig2; import java.lang.ref.SoftReference; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import org.eclipse.cdt.build.core.scannerconfig.CfgInfoContext; import org.eclipse.cdt.build.core.scannerconfig.ICfgScannerConfigBuilderInfo2Set; import org.eclipse.cdt.build.internal.core.scannerconfig.CfgScannerConfigUtil; import org.eclipse.cdt.core.language.settings.providers.ScannerDiscoveryLegacySupport; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.core.settings.model.ICProjectDescription; import org.eclipse.cdt.make.core.MakeCorePlugin; import org.eclipse.cdt.make.core.scannerconfig.IScannerConfigBuilderInfo2; import org.eclipse.cdt.make.core.scannerconfig.IScannerConfigBuilderInfo2Set; import org.eclipse.cdt.make.core.scannerconfig.InfoContext; import org.eclipse.cdt.make.internal.core.scannerconfig2.ScannerConfigProfileManager; import org.eclipse.cdt.managedbuilder.core.IConfiguration; import org.eclipse.cdt.managedbuilder.core.IFileInfo; import org.eclipse.cdt.managedbuilder.core.IFolderInfo; import org.eclipse.cdt.managedbuilder.core.IInputType; import org.eclipse.cdt.managedbuilder.core.IResourceInfo; import org.eclipse.cdt.managedbuilder.core.ITool; import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager; import org.eclipse.cdt.managedbuilder.core.ManagedBuilderCorePlugin; import org.eclipse.cdt.managedbuilder.internal.core.Configuration; import org.eclipse.cdt.managedbuilder.internal.dataprovider.BuildConfigurationData; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.Preferences; import org.eclipse.core.runtime.QualifiedName; public class CfgScannerConfigInfoFactory2 { private static final QualifiedName CONTAINER_INFO_PROPERTY = new QualifiedName(ManagedBuilderCorePlugin.getUniqueIdentifier(), "ScannerConfigBuilderInfo2Container"); //$NON-NLS-1$ private static class ContainerInfo{ int fCode; IScannerConfigBuilderInfo2Set fContainer; ContainerInfo(ICProjectDescription des, IScannerConfigBuilderInfo2Set container){ this.fCode = des.hashCode(); this.fContainer = container; } public boolean matches(ICProjectDescription des){ return des.hashCode() == fCode; } } private static class CfgInfo implements ICfgScannerConfigBuilderInfo2Set { private Configuration cfg; private SoftReference<IScannerConfigBuilderInfo2Set> fContainer; // private HashMap map; CfgInfo(Configuration cfg){ this.cfg = cfg; // init(); } @Override public CfgInfoContext[] getContexts() { Map<CfgInfoContext, IScannerConfigBuilderInfo2> map = createMap(); return map.keySet().toArray(new CfgInfoContext[map.size()]); } @Override public IScannerConfigBuilderInfo2 getInfo(CfgInfoContext context) { return createMap().get(context); // IScannerConfigBuilderInfo2 info = null; // if(!isPerRcTypeDiscovery()){ // info = cfg.getScannerConfigInfo(); // if(info == null){ // info = ScannerConfigInfoFactory2.create(cfg, ManagedBuilderCorePlugin.getDefault().getPluginPreferences()); // } // } else { // Tool tool = (Tool)context.getTool(); // if(tool != null) // info = tool.getScannerConfigInfo(context.getInputType()); //// else //// info = getDefaultInfo(); // } // return info; } @Override public boolean isPerRcTypeDiscovery() { return cfg.isPerRcTypeDiscovery(); } private IScannerConfigBuilderInfo2Set getContainer() throws CoreException{ IScannerConfigBuilderInfo2Set container = fContainer != null ? fContainer.get() : null; if(container == null){ if(!cfg.isPreference()){ ICConfigurationDescription cfgDes = ManagedBuildManager.getDescriptionForConfiguration(cfg); if(cfgDes != null){ ICProjectDescription projDes = cfgDes.getProjectDescription(); if(projDes != null){ ContainerInfo cInfo = (ContainerInfo)projDes.getSessionProperty(CONTAINER_INFO_PROPERTY); if(cInfo != null && cInfo.matches(projDes)){ container = cInfo.fContainer; } else { container = ScannerConfigProfileManager.createScannerConfigBuildInfo2Set(cfg.getOwner().getProject()); cInfo = new ContainerInfo(projDes, container); projDes.setSessionProperty(CONTAINER_INFO_PROPERTY, cInfo); } } } if(container == null){ container = ScannerConfigProfileManager.createScannerConfigBuildInfo2Set(cfg.getOwner().getProject()); } } else { Preferences prefs = MakeCorePlugin.getDefault().getPluginPreferences(); container = ScannerConfigProfileManager.createScannerConfigBuildInfo2Set(prefs, false); } } if(fContainer == null) { fContainer = new SoftReference<IScannerConfigBuilderInfo2Set>(container); } return container; } private Map<CfgInfoContext, IScannerConfigBuilderInfo2> createMap(){ HashMap<CfgInfoContext, IScannerConfigBuilderInfo2> map = new HashMap<CfgInfoContext, IScannerConfigBuilderInfo2>(); try{ IScannerConfigBuilderInfo2Set container = getContainer(); boolean isPerRcType = cfg.isPerRcTypeDiscovery(); Map<InfoContext, IScannerConfigBuilderInfo2> baseMap = container.getInfoMap(); if(!isPerRcType){ // Discovery profile scope = configuration wide CfgInfoContext c = new CfgInfoContext(cfg); InfoContext baseContext = c.toInfoContext(); IScannerConfigBuilderInfo2 info = container.getInfo(baseContext); if(info == null){ String id = cfg.getDiscoveryProfileId(); if(id == null) id = CfgScannerConfigUtil.getFirstProfileId(cfg.getFilteredTools()); IScannerConfigBuilderInfo2 prefInfo = null; if(!cfg.isPreference()){ IConfiguration prefCfg = ManagedBuildManager.getPreferenceConfiguration(false); ICfgScannerConfigBuilderInfo2Set prefContainer = create(prefCfg); prefInfo = prefContainer.getInfo(new CfgInfoContext(prefCfg)); } if(prefInfo == null){ if(id != null) info = container.createInfo(baseContext, id); else info = container.createInfo(baseContext); } else { if(id != null) info = container.createInfo(baseContext, prefInfo, id); else info = container.createInfo(baseContext, prefInfo, prefInfo.getSelectedProfileId()); } } map.put(new CfgInfoContext(cfg), info); } else { // Discovery profile scope = per language Map<CfgInfoContext, IScannerConfigBuilderInfo2> configMap = getConfigInfoMap(baseMap); IResourceInfo[] rcInfos = cfg.getResourceInfos(); for (IResourceInfo rcInfo : rcInfos) { ITool tools[]; if(rcInfo instanceof IFolderInfo) { tools = ((IFolderInfo)rcInfo).getFilteredTools(); } else { tools = ((IFileInfo)rcInfo).getToolsToInvoke(); } for (ITool tool : tools) { IInputType types[] = tool.getInputTypes(); if(types.length != 0){ for (IInputType inputType : types) { CfgInfoContext context = new CfgInfoContext(rcInfo, tool, inputType); context = CfgScannerConfigUtil.adjustPerRcTypeContext(context); if(context != null && context.getResourceInfo() != null){ IScannerConfigBuilderInfo2 info = configMap.get(context); if(info == null && !inputType.isExtensionElement() && inputType.getSuperClass() != null){ CfgInfoContext superContext = new CfgInfoContext(rcInfo, tool, inputType.getSuperClass()); superContext = CfgScannerConfigUtil.adjustPerRcTypeContext(superContext); if(superContext != null && superContext.getResourceInfo() != null){ info = configMap.get(superContext); } // Scanner discovery options aren't settable on a file-per-file basis. Thus // files with custom properties don't have a persisted entry in the config // info map; we create an ephemeral entry instead. We need to assign that file // the scanner profile that's used for non-custom files of the same // inputType/tool (and configuration, of course). Unfortunately, identifying // a match is inefficient, but in practice, projects don't have tons of // customized files. See Bug 354194 String id = null; for (Entry<CfgInfoContext, IScannerConfigBuilderInfo2> entry : configMap.entrySet()) { CfgInfoContext cfgInfoCxt = entry.getKey(); if (match(cfgInfoCxt.getInputType(), context.getInputType()) && match(cfgInfoCxt.getTool(), context.getTool().getSuperClass()) && cfgInfoCxt.getConfiguration().equals(context.getConfiguration())) { id = entry.getValue().getSelectedProfileId(); } } if (id == null) { // Language Settings Providers are meant to replace legacy scanner discovery // so do not try to find default profile ICConfigurationDescription cfgDescription = ManagedBuildManager.getDescriptionForConfiguration(cfg); if (ScannerDiscoveryLegacySupport.isLegacyScannerDiscoveryOn(cfgDescription)) { id = CfgScannerConfigUtil.getDefaultProfileId(context, true); } } InfoContext baseContext = context.toInfoContext(); if(info == null){ if(id != null){ info = container.createInfo(baseContext, id); } else { info = container.createInfo(baseContext); } } else { if(id != null){ info = container.createInfo(baseContext, info, id); } else { info = container.createInfo(baseContext, info); } } // Make sure to remove the ephemeral info map entry from the // container, otherwise it will not be ephemeral but rather a // permanent and stagnant part of the project description. It was // added to the container only so we could obtain an // IScannerConfigBuilderInfo2. Now that we have the info object, // revert the container. See Bug 354194. Note that the permanent // entry for the project's root folder resource info gets created // by us shortly after project creation; thus we have to make an // exception for that rcinfo. if (!(rcInfo instanceof IFolderInfo && rcInfo.getPath().isEmpty())) { container.removeInfo(context.toInfoContext()); } } if(info != null){ map.put(context, info); } } } } else { if(cfg.isPreference()) continue; CfgInfoContext context = new CfgInfoContext(rcInfo, tool, null); context = CfgScannerConfigUtil.adjustPerRcTypeContext(context); if(context != null && context.getResourceInfo() != null){ IScannerConfigBuilderInfo2 info = configMap.get(context); if(info == null){ String id = CfgScannerConfigUtil.getDefaultProfileId(context, true); InfoContext baseContext = context.toInfoContext(); if(id != null){ info = container.createInfo(baseContext, id); } else { info = container.createInfo(baseContext); } } if(info != null){ map.put(context, info); } } } } } if(!configMap.isEmpty()){ for (Entry<CfgInfoContext, IScannerConfigBuilderInfo2> entry : configMap.entrySet()) { if(map.containsKey(entry.getKey())) continue; CfgInfoContext c = entry.getKey(); if(c.getResourceInfo() != null || c.getTool() != null || c.getInputType() != null){ InfoContext baseC = c.toInfoContext(); if(!baseC.isDefaultContext()) container.removeInfo(baseC); } } } } } catch (CoreException e){ ManagedBuilderCorePlugin.log(e); } return map; } private Map<CfgInfoContext, IScannerConfigBuilderInfo2> getConfigInfoMap(Map<InfoContext, IScannerConfigBuilderInfo2> baseMap){ Map<CfgInfoContext, IScannerConfigBuilderInfo2> map = new HashMap<CfgInfoContext, IScannerConfigBuilderInfo2>(); for (Entry<InfoContext, IScannerConfigBuilderInfo2> entry : baseMap.entrySet()) { InfoContext baseContext = entry.getKey(); CfgInfoContext c = CfgInfoContext.fromInfoContext(cfg, baseContext); if(c != null){ IScannerConfigBuilderInfo2 info = entry.getValue(); map.put(c, info); } } return map; } @Override public Map<CfgInfoContext,IScannerConfigBuilderInfo2> getInfoMap() { return createMap(); } @Override public void setPerRcTypeDiscovery(boolean on) { cfg.setPerRcTypeDiscovery(on); } @Override public IScannerConfigBuilderInfo2 applyInfo(CfgInfoContext context, IScannerConfigBuilderInfo2 base) { try { IScannerConfigBuilderInfo2 newInfo; IScannerConfigBuilderInfo2Set container = getContainer(); InfoContext baseContext = context.toInfoContext(); if(base != null){ newInfo = container.createInfo(baseContext, base); } else { if(!baseContext.isDefaultContext()) container.removeInfo(baseContext); newInfo = getInfo(context); } return newInfo; } catch (CoreException e) { ManagedBuilderCorePlugin.log(e); } return null; } @Override public IConfiguration getConfiguration() { return cfg; } @Override public boolean isProfileSupported(CfgInfoContext context, String profileId) { if(!isPerRcTypeDiscovery()) return true; return CfgScannerConfigProfileManager.isPerFileProfile(profileId); } } public static ICfgScannerConfigBuilderInfo2Set create(IConfiguration cfg){ Configuration c = (Configuration)cfg; ICfgScannerConfigBuilderInfo2Set container = c.getCfgScannerConfigInfo(); if(container == null){ container = new CfgInfo(c); c.setCfgScannerConfigInfo(container); } return container; } public static void save(BuildConfigurationData data, ICProjectDescription des, ICProjectDescription baseDescription, boolean force) throws CoreException{ ContainerInfo info = (ContainerInfo)des.getSessionProperty(CONTAINER_INFO_PROPERTY); if(info != null){ if(info.matches(baseDescription)){ IScannerConfigBuilderInfo2Set baseContainer = info.fContainer; baseContainer.save(); } des.setSessionProperty(CONTAINER_INFO_PROPERTY, null); } else if (force){ Configuration cfg = (Configuration)data.getConfiguration(); CfgInfo cfgInfo = new CfgInfo(cfg); cfg.setCfgScannerConfigInfo(cfgInfo); cfgInfo.getInfoMap(); cfgInfo.getContainer().save(); des.setSessionProperty(CONTAINER_INFO_PROPERTY, null); } } public static void savePreference(IConfiguration cfg) throws CoreException{ ICfgScannerConfigBuilderInfo2Set container = ((Configuration)cfg).getCfgScannerConfigInfo(); if(container != null){ IScannerConfigBuilderInfo2Set baseContainer = ((CfgInfo)container).getContainer(); if(baseContainer != null){ baseContainer.save(); } } } private static boolean match(ITool t1, ITool t2){ if (t1 == null || t2 == null) return false; if (t1.getId().equals(t2.getId())) { return true; } return match(t1.getSuperClass(), t2.getSuperClass()); } private static boolean match(IInputType i1, IInputType i2){ if (i1 == null || i2 == null) return false; if (i1.getId().equals(i2.getId())) { return true; } return match(i1.getSuperClass(), i2.getSuperClass()); } }