/******************************************************************************* * Copyright (c) 2007, 2014 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 * Markus Schorn (Wind River Systems) * IBM Corporation * James Blackburn (Broadcom Corp.) * Alex Blewitt Bug 132511 - nature order not preserved * Christian Walther (Indel AG) - [436060] Race condition in updateProjectDescriptions() *******************************************************************************/ package org.eclipse.cdt.internal.core.settings.model; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.FactoryConfigurationError; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsProvider; import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsProvidersKeeper; import org.eclipse.cdt.core.model.CModelException; import org.eclipse.cdt.core.model.ICElement; import org.eclipse.cdt.core.model.ICElementDelta; import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.core.settings.model.CExternalSetting; import org.eclipse.cdt.core.settings.model.CProjectDescriptionEvent; import org.eclipse.cdt.core.settings.model.ICBuildSetting; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.core.settings.model.ICDescriptionDelta; import org.eclipse.cdt.core.settings.model.ICFileDescription; import org.eclipse.cdt.core.settings.model.ICFolderDescription; import org.eclipse.cdt.core.settings.model.ICLanguageSetting; import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; import org.eclipse.cdt.core.settings.model.ICProjectDescription; import org.eclipse.cdt.core.settings.model.ICProjectDescriptionListener; import org.eclipse.cdt.core.settings.model.ICProjectDescriptionManager; import org.eclipse.cdt.core.settings.model.ICProjectDescriptionWorkspacePreferences; import org.eclipse.cdt.core.settings.model.ICResourceDescription; import org.eclipse.cdt.core.settings.model.ICSettingBase; import org.eclipse.cdt.core.settings.model.ICSettingEntry; import org.eclipse.cdt.core.settings.model.ICSettingObject; import org.eclipse.cdt.core.settings.model.ICSettingsStorage; import org.eclipse.cdt.core.settings.model.ICSourceEntry; import org.eclipse.cdt.core.settings.model.ICStorageElement; import org.eclipse.cdt.core.settings.model.ICTargetPlatformSetting; import org.eclipse.cdt.core.settings.model.extension.CConfigurationData; import org.eclipse.cdt.core.settings.model.extension.CConfigurationDataProvider; import org.eclipse.cdt.core.settings.model.extension.CFileData; import org.eclipse.cdt.core.settings.model.extension.CFolderData; import org.eclipse.cdt.core.settings.model.extension.CLanguageData; import org.eclipse.cdt.core.settings.model.extension.CResourceData; import org.eclipse.cdt.core.settings.model.extension.ICProjectConverter; import org.eclipse.cdt.core.settings.model.extension.impl.CDataFactory; import org.eclipse.cdt.core.settings.model.util.CDataUtil; import org.eclipse.cdt.core.settings.model.util.KindBasedStore; import org.eclipse.cdt.core.settings.model.util.ListComparator; import org.eclipse.cdt.core.settings.model.util.PathSettingsContainer; import org.eclipse.cdt.core.settings.model.util.PatternNameMap; import org.eclipse.cdt.internal.core.CConfigBasedDescriptorManager; import org.eclipse.cdt.internal.core.model.CElementDelta; import org.eclipse.cdt.internal.core.settings.model.CExternalSettinsDeltaCalculator.ExtSettingsDelta; import org.eclipse.cdt.internal.core.settings.model.xml.InternalXmlStorageElement; import org.eclipse.cdt.internal.core.settings.model.xml.XmlStorage; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProjectDescription; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.ISavedState; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.IWorkspaceRunnable; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.ICoreRunnable; import org.eclipse.core.runtime.IExtension; import org.eclipse.core.runtime.IExtensionPoint; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.QualifiedName; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.runtime.content.IContentType; import org.eclipse.core.runtime.content.IContentTypeManager; import org.eclipse.core.runtime.jobs.IJobManager; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.runtime.jobs.Job; import org.osgi.framework.Version; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.ProcessingInstruction; import org.xml.sax.SAXException; import com.ibm.icu.text.MessageFormat; /** * The CProjectDescriptionManager is to marshall the loading and storing * of CDT Project Descriptions. * * This class delegates loading and store of the project model to the appropriate * AbstractCProjectDescriptionStorage for the Project Description. [ Discovered at Project load * time.] * * Users should not synchronize on the singleton instance of this class. It is the job of * the AbstractCProjectDescriptionStorage to ensure thread safe access to the backing store * as described in that interface. * * Previously this class created and persisted * @see ICProjectDescriptionManager */ public class CProjectDescriptionManager implements ICProjectDescriptionManager { public static final int INTERNAL_GET_IGNORE_CLOSE = 1 << 31; private static final String VERSION_ELEMENT_NAME = "fileVersion"; //$NON-NLS-1$ /** Preference Version 4.0 & 5.0 are equivalent for us. Version was inadvertently bumped * when during project description storage work. * This is the minimum preference version we support loading.*/ public static final Version MIN_DESCRIPTION_VERSION = new Version("4.0"); //$NON-NLS-1$ /** Current preference file storage version */ public static final Version DESCRIPTION_VERSION = new Version("5.0"); //$NON-NLS-1$ public final static String MODULE_ID = "org.eclipse.cdt.core.settings"; //$NON-NLS-1$ static final String CONFIGURATION = "cconfiguration"; //$NON-NLS-1$ private static final ICLanguageSettingEntry[] EMPTY_LANGUAGE_SETTINGS_ENTRIES_ARRAY = new ICLanguageSettingEntry[0]; private static final ICElementDelta[] EMPTY_CELEMENT_DELTA = new ICElementDelta[0]; private static final ICLanguageSetting[] EMPTY_LANGUAGE_SETTINGS_ARRAY = new ICLanguageSetting[0]; private static final String PREFERENCES_STORAGE = "preferences"; //$NON-NLS-1$ private static final String PREFERENCE_BUILD_SYSTEM_ELEMENT = "buildSystem"; //$NON-NLS-1$ private static final String PREFERENCES_ELEMENT = "preferences"; //$NON-NLS-1$ private static final String ID = "id"; //$NON-NLS-1$ private static final String PREFERENCE_CFG_ID_PREFIX = "preference."; //$NON-NLS-1$ private static final String PREFERENCE_CFG_NAME = SettingsModelMessages.getString("CProjectDescriptionManager.15"); //$NON-NLS-1$ private static final String ROOT_PREFERENCE_ELEMENT = "preferences"; //$NON-NLS-1$ private static final String DEFAULT_CFG_ID_PREFIX = CCorePlugin.PLUGIN_ID + ".default.config"; //$NON-NLS-1$ private static final String DEFAULT_CFG_NAME = "Configuration"; //$NON-NLS-1$ private static final QualifiedName SCANNER_INFO_PROVIDER_PROPERTY = new QualifiedName(CCorePlugin.PLUGIN_ID, "scannerInfoProvider"); //$NON-NLS-1$ static class CompositeWorkspaceRunnable implements IWorkspaceRunnable { private List<IWorkspaceRunnable> fRunnables = new ArrayList<>(); private String fName; private boolean fStopOnErr; CompositeWorkspaceRunnable(String name) { fName = name == null ? "" : name; //$NON-NLS-1$ } public void add(IWorkspaceRunnable runnable) { fRunnables.add(runnable); } @Override public void run(IProgressMonitor monitor) throws CoreException { try { SubMonitor progress = SubMonitor.convert(monitor, fName, fRunnables.size()); for (IWorkspaceRunnable r : fRunnables) { try { r.run(progress.split(1)); } catch (CoreException | RuntimeException e) { if (fStopOnErr) throw e; } } } finally { monitor.done(); } } public boolean isEmpty() { return fRunnables.isEmpty(); } } /** * Container class for ICProjectDescription change listeners */ private static class ListenerDescriptor{ final ICProjectDescriptionListener fListener; final int fEventTypes; public ListenerDescriptor(ICProjectDescriptionListener listener, int eventTypes) { fListener = listener; fEventTypes = eventTypes; } public boolean handlesEvent(int eventType) { return (eventType & fEventTypes) != 0; } } private volatile Map<String, CConfigurationDataProviderDescriptor> fProviderMap; private volatile CProjectConverterDesciptor fConverters[]; /** Set of Listeners listening for Project Description Deltas */ private Set<ListenerDescriptor> fListeners = new CopyOnWriteArraySet<ListenerDescriptor>(); private Map<String, CConfigurationDescriptionCache> fPreferenceMap = new HashMap<String, CConfigurationDescriptionCache>(); private volatile CConfigBasedDescriptorManager fDescriptorManager; private ResourceChangeHandler fRcChangeHandler; private CProjectDescriptionWorkspacePreferences fPreferences; private boolean fAllowEmptyCreatingDescription = true; // allowed by default private ICDataProxyContainer fPrefUpdater = new ICDataProxyContainer() { @Override public void updateChild(CDataProxy child, boolean write) { if (write) { try { ((CConfigurationDescription) child).doWritable(); } catch (CoreException e) { CCorePlugin.log(e); } } } @Override public ICSettingObject[] getChildSettings() { return fPreferenceMap.values().toArray(new CConfigurationDescriptionCache[fPreferenceMap.size()]); } }; /** The CProjectDescriptionManager instance */ private static volatile CProjectDescriptionManager fInstance; private CProjectDescriptionManager() {} public static CProjectDescriptionManager getInstance() { if (fInstance == null) { synchronized (CProjectDescriptionManager.class) { if (fInstance == null) { fInstance = new CProjectDescriptionManager(); } } } return fInstance; } public void projectClosedRemove(IProject project) { CProjectDescriptionStorageManager.getInstance().projectClosedRemove(project); disposeAssociatedListeners(project); } private void disposeAssociatedListeners(IProject project) { List<ScannerInfoProviderProxy> proxyListeners = new ArrayList<ScannerInfoProviderProxy>(); for (ListenerDescriptor ldescriptor : fListeners) { if (ldescriptor.fListener instanceof ScannerInfoProviderProxy) proxyListeners.add((ScannerInfoProviderProxy) ldescriptor.fListener); } // avoid calling proxy.close() inside fListeners loop as it modifies the collection for (ScannerInfoProviderProxy proxy : proxyListeners) { if (project.equals(proxy.getProject())) proxy.close(); } } /** * * @param from Project to move the description from * @param to Project where the description is moved to * @return <b>ICProjectDescription</b> - non serialized, modified, writable * project description. To serialize, call <code>setProjectDescription()</code> * */ public ICProjectDescription projectMove(IProject from, IProject to) { CProjectDescriptionStorageManager.getInstance().projectMove(from, to); int flags = CProjectDescriptionManager.INTERNAL_GET_IGNORE_CLOSE | ICProjectDescriptionManager.GET_WRITABLE; CProjectDescription des = (CProjectDescription) getProjectDescription(to, flags); // set configuration descriptions to "writable" state if (des != null) { for (ICConfigurationDescription cfgDes : des.getConfigurations()) { des.updateChild((CConfigurationDescription) cfgDes, true); } } return des; } public Job startup() { if (fRcChangeHandler == null) { fRcChangeHandler = new ResourceChangeHandler(); ResourcesPlugin.getWorkspace().addResourceChangeListener( fRcChangeHandler, IResourceChangeEvent.POST_CHANGE | IResourceChangeEvent.PRE_DELETE | IResourceChangeEvent.PRE_CLOSE /*| IResourceChangeEvent.POST_BUILD*/); if (fDescriptorManager == null) { fDescriptorManager = CConfigBasedDescriptorManager.getInstance(); fDescriptorManager.startup(); } CExternalSettingsManager.getInstance().startup(); } return createPostStartupJob(); } private Job createPostStartupJob() { IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); Job rcJob = new Job(SettingsModelMessages.getString("CProjectDescriptionManager.0")) { //$NON-NLS-1$ @Override protected IStatus run(IProgressMonitor monitor) { fInstance.initProviderInfo(); try { startSaveParticipant(); } catch (CoreException e) { CCorePlugin.log(e); return e.getStatus(); } return Status.OK_STATUS; } }; rcJob.setRule(root); rcJob.setPriority(Job.INTERACTIVE); rcJob.setSystem(true); return rcJob; } /* * This method adds a save participant and resource change listener * Throws CoreException if the methods fails to add a save participant. * The resource change listener in not added in this case either. */ private void startSaveParticipant() throws CoreException{ // Set up a listener for resource change events ISavedState lastState = ResourcesPlugin.getWorkspace().addSaveParticipant(CCorePlugin.PLUGIN_ID, fRcChangeHandler); if (lastState != null) { lastState.processResourceChangeEvents(fRcChangeHandler); } } public void shutdown() { CExternalSettingsManager.getInstance().shutdown(); if (fDescriptorManager != null) { fDescriptorManager.shutdown(); fDescriptorManager = null; } if (fRcChangeHandler != null) { ResourcesPlugin.getWorkspace().removeResourceChangeListener(fRcChangeHandler); fRcChangeHandler = null; } CProjectDescriptionStorageManager.getInstance().shutdown(); } @Override public ICProjectDescription getProjectDescription(IProject project, boolean write) { return getProjectDescription(project, true, write); } public ICProjectDescription getProjectDescription(IProject project, boolean load, boolean write) { int flags = load ? 0 : GET_IF_LOADDED; flags |= write ? GET_WRITABLE : 0; return getProjectDescription(project, flags); } @Override public ICProjectDescription getProjectDescription(IProject project, int flags) { try { return getProjectDescriptionInternal(project, flags); } catch (CoreException e) { // FIXME Currently the resource change handler ResourceChangeHandler.getProjectDescription(...) // Does this when the project is closed. Don't log an error or the tests will fail // CCorePlugin.log(e); } return null; } /** * Base method for getting a Project's Description * @param project * @param flags * @return ICProjectDescription * @throws CoreException if project description isn't available */ private ICProjectDescription getProjectDescriptionInternal(IProject project, int flags) throws CoreException { AbstractCProjectDescriptionStorage storage = getProjectDescriptionStorage(project); return storage.getProjectDescription(flags, new NullProgressMonitor()); } /** * Run the workspace modification in the current thread using the workspace scheduling rule * Equivalent to: <code>runWspModification(IWorkspaceRunnable, ResourcecPlugin.getWorkspace().getRoot(), IProgressMonitor)</code> *<br/><br/> * Note that if the workspace is locked, or the current job / thread doesn't contain the workspace * scheduling rule, then we schedule a job to run the {@link IWorkspaceRunnable} *<br/><br/> * The scheduled job is returned, or null if the operation was run immediately. * * @param runnable the IWorkspaceRunnable to run * @param monitor * @return scheduled job or null if the operation was run immediately */ public static Job runWspModification(final ICoreRunnable runnable, IProgressMonitor monitor) { return runWspModification(runnable, ResourcesPlugin.getWorkspace().getRoot(), monitor); } /** * Either runs the modification in the current thread (if the workspace is not locked) * or schedules a runnable to perform the operation * @param runnable * @param monitor * @return scheduled job or null if the operation was run immediately */ public static Job runWspModification(final ICoreRunnable runnable, final ISchedulingRule rule, IProgressMonitor monitor) { // Should the rule be scheduled, or run immediately boolean scheduleRule = ResourcesPlugin.getWorkspace().isTreeLocked(); // Check whether current job contains rule 'rule' // If not, we must schedule another job to execute the runnable if (!scheduleRule) { Job currentJob = Job.getJobManager().currentJob(); if (currentJob != null && currentJob.getRule() != null && !currentJob.getRule().contains(rule)) scheduleRule = true; } if (!scheduleRule) { // Run immediately IJobManager mngr = Job.getJobManager(); try{ mngr.beginRule(rule, monitor); runAtomic(runnable, rule, monitor); } catch (Exception e) { CCorePlugin.log(e); } finally { mngr.endRule(rule); } } else { // schedule a job for it Job job = new Job(SettingsModelMessages.getString("CProjectDescriptionManager.12")) { //$NON-NLS-1$ @Override protected IStatus run(IProgressMonitor monitor) { try { runAtomic(runnable, rule, monitor); } catch (CoreException e) { CCorePlugin.log(e); return e.getStatus(); } return Status.OK_STATUS; } }; job.setRule(rule); job.setSystem(true); job.schedule(); return job; } return null; } private static void runAtomic(final ICoreRunnable r, ISchedulingRule rule, IProgressMonitor monitor) throws CoreException{ IWorkspace wsp = ResourcesPlugin.getWorkspace(); wsp.run(new ICoreRunnable() { @Override public void run(IProgressMonitor monitor) throws CoreException { try { r.run(monitor); } catch (Exception e) { CCorePlugin.log(e); throw new CoreException(new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, e.getMessage(), e)); } } }, rule, IWorkspace.AVOID_UPDATE, monitor); } @Override public void updateProjectDescriptions(IProject[] projects, IProgressMonitor monitor) throws CoreException{ IWorkspace wsp = ResourcesPlugin.getWorkspace(); if (projects == null) projects = wsp.getRoot().getProjects(); final ICProjectDescription dessWritable[] = new ICProjectDescription[projects.length]; final ICProjectDescription dessCache[] = new ICProjectDescription[projects.length]; int num = 0; for (IProject project : projects) { ICProjectDescription des = getProjectDescription(project, false, true); if (des != null) { dessWritable[num] = des; dessCache[num] = getProjectDescription(project, false, false); num++; } } if (num != 0) { final int finalNum = num; runWspModification(new ICoreRunnable() { @Override public void run(IProgressMonitor monitor) throws CoreException { SubMonitor subMonitor = SubMonitor.convert(monitor, SettingsModelMessages.getString("CProjectDescriptionManager.13"), finalNum); //$NON-NLS-1$ for (int i = 0; i < finalNum; i++) { ICProjectDescription des = dessWritable[i]; ICProjectDescription desCache = dessCache[i]; try { // Only apply the project description if it is still current, otherwise: // - someone else must already have called setProjectDescription, so there is // nothing to do // - we might overwrite someone else's changes with our older description if (desCache == getProjectDescription(des.getProject(), false, false)) { setProjectDescription(des.getProject(), des, true, subMonitor.split(1)); } } catch (CoreException e) { CCorePlugin.log(e); } } } }, monitor); } } public ICProjectConverter getConverter(IProject project, String oldOwnerId, ICProjectDescription des) { CProjectConverterDesciptor[] converterDess = getConverterDescriptors(); ICProjectConverter converter = null; for (CProjectConverterDesciptor converterDes : converterDess) { if (converterDes.canConvertProject(project, oldOwnerId, des)) { try { converter = converterDes.getConverter(); } catch (CoreException e) { } if (converter != null) break; } } return converter; } private CProjectConverterDesciptor[] getConverterDescriptors() { if (fConverters == null) { initConverterInfoSynch(); } return fConverters; } private synchronized void initConverterInfoSynch() { if (fConverters != null) return; IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(CProjectConverterDesciptor.PROJECT_CONVERTER_EXTPOINT_ID); IExtension exts[] = extensionPoint.getExtensions(); CProjectConverterDesciptor[] dess = new CProjectConverterDesciptor[exts.length]; for (int i = 0; i < exts.length; i++) { dess[i] = new CProjectConverterDesciptor(exts[i]); } fConverters = dess; } @Override public ICProjectDescription createProjectDescription(IProject project, boolean loadIfExists) throws CoreException{ return createProjectDescription(project, loadIfExists, false); } @Override public ICProjectDescription createProjectDescription(IProject project, boolean loadIfExists, boolean creating) throws CoreException{ int flags = ICProjectDescriptionManager.GET_WRITABLE | ICProjectDescriptionManager.GET_CREATE_DESCRIPTION; flags |= loadIfExists ? 0 : ICProjectDescriptionManager.GET_EMPTY_PROJECT_DESCRIPTION; flags |= creating ? ICProjectDescriptionManager.PROJECT_CREATING : 0; return getProjectDescriptionInternal(project, flags); } public ScannerInfoProviderProxy getScannerInfoProviderProxy(IProject project) { ICProjectDescription des = getProjectDescription(project, false); if (des == null) { return new ScannerInfoProviderProxy(project); } ScannerInfoProviderProxy provider = (ScannerInfoProviderProxy) des.getSessionProperty(SCANNER_INFO_PROVIDER_PROPERTY); if (provider == null) { provider = new ScannerInfoProviderProxy(project); des.setSessionProperty(SCANNER_INFO_PROVIDER_PROPERTY, provider); } else { provider.updateProject(project); } return provider; } @Override public ICProjectDescription getProjectDescription(IProject project) { return getProjectDescription(project, true); } /* * returns true if the project description was modified false - otherwise */ public boolean checkHandleActiveCfgChange(CProjectDescription newDes, ICProjectDescription oldDes, IProjectDescription eDes, IProgressMonitor monitor) { if (newDes == null) return false; ICConfigurationDescription newCfg = newDes.getActiveConfiguration(); if (newCfg == null) return false; ICConfigurationDescription oldCfg = oldDes != null ? oldDes.getActiveConfiguration() : null; checkActiveCfgChange(newDes, oldDes); checkSettingCfgChange(newDes, oldDes); boolean modified = false; try { if (checkBuildSystemChange(eDes, newCfg, oldCfg, monitor)) modified = true; } catch (CoreException e) { CCorePlugin.log(e); } try { if (checkProjectRefChange(eDes, newDes, newCfg, oldCfg, monitor)) modified = true; } catch (CoreException e) { CCorePlugin.log(e); } return modified; } // String loadActiveCfgId(ICProjectDescription des) { // try { // return des.getProject().getPersistentProperty(ACTIVE_CFG_PROPERTY); // } catch (CoreException e) { // CCorePlugin.log(e); // } // return null; // } private Collection<IProject> projSetFromProjNameSet(Collection<String> projNames) { if (projNames.size() == 0) return new HashSet<IProject>(0); Set<IProject> set = new LinkedHashSet<IProject>(); IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); for (String sproj : projNames) set.add(root.getProject(sproj)); return set; } /** * Fix up platform references having changed CDT configuration references */ @SuppressWarnings("unchecked") private boolean checkProjectRefChange(IProjectDescription des, ICProjectDescription newCDesc, ICConfigurationDescription newCfg, ICConfigurationDescription oldCfg, IProgressMonitor monitor) throws CoreException{ if (newCfg == null) return false; Map<String, String> oldMap = oldCfg != null ? oldCfg.getReferenceInfo() : Collections.EMPTY_MAP; Map<String, String> newMap = newCfg.getReferenceInfo(); // If there's been no change nothing to do if (newMap.equals(oldMap)) return false; // We're still looking at the same configuration - any refs removed? HashSet<String> removedRefs = new HashSet<String>(); if (oldCfg != null && oldCfg.getId().equals(newCfg.getId())) { removedRefs.addAll(oldMap.keySet()); removedRefs.removeAll(newMap.keySet()); } // Get the full set of references from all configuration LinkedHashSet<String> allRefs = new LinkedHashSet<String>(); for (ICConfigurationDescription cfg : newCDesc.getConfigurations()) allRefs.addAll(cfg.getReferenceInfo().keySet()); // Don't remove a reference if it's referenced by any configuration in the project description removedRefs.removeAll(allRefs); Collection<IProject> oldProjects = new LinkedHashSet<IProject>(Arrays.asList(des.getReferencedProjects())); Collection<IProject> newProjects = projSetFromProjNameSet(allRefs); // If there are no changes, just return if (removedRefs.isEmpty() && oldProjects.containsAll(newProjects)) return false; // Ensure the Eclipse configuration references all projects we reference oldProjects.addAll(newProjects); // Removing any projects we no longer referece oldProjects.removeAll(projSetFromProjNameSet(removedRefs)); des.setReferencedProjects(oldProjects.toArray(new IProject[oldProjects.size()])); return true; } // private void checkBuildSystemChange(IProject project, String newBsId, String oldBsId, IProgressMonitor monitor) throws CoreException{ // checkBuildSystemChange(project, null, newBsId, oldBsId, monitor); // } private boolean checkActiveCfgChange(CProjectDescription des, ICProjectDescription oldDes // ICConfigurationDescription newCfg, // ICConfigurationDescription oldCfg ) { ICConfigurationDescription oldCfg = oldDes != null ? oldDes.getActiveConfiguration() : null; // String newId = newCfg.getId(); String oldId = oldCfg != null ? oldCfg.getId() : null; return des.checkPersistActiveCfg(oldId, false); } private boolean checkSettingCfgChange(CProjectDescription des, ICProjectDescription oldDes // ICConfigurationDescription newCfg, // ICConfigurationDescription oldCfg ) { ICConfigurationDescription oldCfg = oldDes != null ? oldDes.getDefaultSettingConfiguration() : null; // String newId = newCfg.getId(); String oldId = oldCfg != null ? oldCfg.getId() : null; return des.checkPersistSettingCfg(oldId, false); } private boolean checkBuildSystemChange(IProjectDescription des, ICConfigurationDescription newCfg, ICConfigurationDescription oldCfg, IProgressMonitor monitor) throws CoreException{ String newBsId = newCfg != null ? newCfg.getBuildSystemId() : null; String oldBsId = oldCfg != null ? oldCfg.getBuildSystemId() : null; CConfigurationDataProviderDescriptor newDr = newBsId != null ? getCfgProviderDescriptor(newBsId) : null; CConfigurationDataProviderDescriptor oldDr = oldBsId != null ? getCfgProviderDescriptor(oldBsId) : null; List<String> newNatures, oldNatures, conflictingNatures; newNatures = oldNatures = conflictingNatures = Collections.emptyList(); if (oldDr != null) oldNatures = Arrays.asList(oldDr.getNatureIds()); if (newDr != null) { newNatures = Arrays.asList(newDr.getNatureIds()); conflictingNatures = Arrays.asList(newDr.getConflictingNatureIds()); } // List of existing natureIds final String[] natureIds = des.getNatureIds(); // Get the set of items to remove ({oldNatures} - {newNatures}) + conflictingNatures Set<String> toRemove = new HashSet<String>(oldNatures); toRemove.removeAll(newNatures); // Don't remove items we're re-adding toRemove.addAll(conflictingNatures); // Add conflicting natures for removal // Modify an ordered set of the existing natures with the changes final LinkedHashSet<String> cur = new LinkedHashSet<String>(Arrays.asList(natureIds)); cur.addAll(newNatures); cur.removeAll(toRemove); final String[] newNatureIds = cur.toArray(new String[cur.size()]); if (!Arrays.equals(newNatureIds, natureIds)) { des.setNatureIds(newNatureIds); return true; } return false; } @Override public void setProjectDescription(IProject project, ICProjectDescription des) throws CoreException { setProjectDescription(project, des, false, null); } @Override public void setProjectDescription(IProject project, ICProjectDescription des, boolean force, IProgressMonitor monitor) throws CoreException { int flags = force ? SET_FORCE : 0; setProjectDescription(project, des, flags, monitor); } static boolean checkFlags(int flags, int check) { return (flags & check) == check; } /** ThreadLocal flag to let CDescriptor know whether already in a setProjectDescription */ ThreadLocal<Boolean> settingProjectDescription = new ThreadLocal<Boolean>() {@Override protected Boolean initialValue() {return false;}}; @Override public void setProjectDescription(IProject project, ICProjectDescription des, int flags, IProgressMonitor monitor) throws CoreException { boolean originalState = isCurrentThreadSetProjectDescription(); try { if (originalState) { // Technically this is not critical problem since the recursive setProjectDescription() gets scheduled in background thread. // But it is quite likely to be an error on part of the caller unaware that their listener can be called from inside setProjectDescription(). // To avoid the log entry the callers should check CProjectDescriptionManager.isCurrentThreadSetProjectDescription() // and schedule the update in background thread themselves. CCorePlugin.logStackTrace(IStatus.INFO, "Recursive setProjectDescription from event listener, project=" + project); //$NON-NLS-1$ } settingProjectDescription.set(true); if (des != null) { if (!project.isAccessible()) throw ExceptionFactory.createCoreException(MessageFormat.format(CCorePlugin.getResourceString("ProjectDescription.ProjectNotAccessible"), new Object[] {project.getName()})); //$NON-NLS-1$ if (!des.isValid() && (!fAllowEmptyCreatingDescription || !des.isCdtProjectCreating())) throw ExceptionFactory.createCoreException(SettingsModelMessages.getString("CProjectDescriptionManager.17") + project.getName()); //$NON-NLS-1$ if (!checkFlags(flags, SET_FORCE) && !des.isModified()) return; if (((CProjectDescription) des).isLoading()) { throw ExceptionFactory.createCoreException("description is being loadded"); //$NON-NLS-1$ } if (((CProjectDescription) des).isApplying()) { throw ExceptionFactory.createCoreException("description is being applied"); //$NON-NLS-1$ } } CProjectDescriptionStorageManager.getInstance().setProjectDescription(project, des, flags, monitor); } finally { settingProjectDescription.set(originalState); } } /** * Indicates that a setProjectDescription is currently in progress to prevent recursive setProjectDescription * @return boolean */ public boolean isCurrentThreadSetProjectDescription() { return settingProjectDescription.get(); } /** * Base for getting a project desc's storage. project must be accessible. * @param project * @return ProjectDescription storage * @throws CoreException if Project isn't accessible */ private AbstractCProjectDescriptionStorage getProjectDescriptionStorage(IProject project) throws CoreException { if (project == null || !project.isAccessible()) throw ExceptionFactory.createCoreException(MessageFormat.format(CCorePlugin.getResourceString("ProjectDescription.ProjectNotAccessible"), new Object[] {project != null ? project.getName() : "<null>"})); //$NON-NLS-1$ //$NON-NLS-2$ AbstractCProjectDescriptionStorage storage = CProjectDescriptionStorageManager.getInstance().getProjectDescriptionStorage(project); if (storage == null) throw ExceptionFactory.createCoreException(SettingsModelMessages.getString("CProjectDescriptionManager.FailedToGetStorage") + project.getName()); //$NON-NLS-1$ return storage; } /** * Return an ICSettingsStorage based on the provided ICStorageElement * in the given IProject * @param project * @return ICSettingsStorages */ public ICSettingsStorage getStorageForElement(IProject project, ICStorageElement element) throws CoreException { if (project != null) return getProjectDescriptionStorage(project).getStorageForElement(element); // project is null means it's a preference element, uses XmlStorages return new XmlStorage((InternalXmlStorageElement) element); } private void serializePreference(String key, InternalXmlStorageElement element) throws CoreException{ Document doc = element.fElement.getOwnerDocument(); // Transform the document to something we can save in a file ByteArrayOutputStream stream = new ByteArrayOutputStream(); FileOutputStream fileStream = null; try { Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$ transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); //$NON-NLS-1$ transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$ DOMSource source = new DOMSource(doc); StreamResult result = new StreamResult(stream); transformer.transform(source, result); // Save the document File file = getPreferenceFile(key); String utfString = stream.toString("UTF-8"); //$NON-NLS-1$ if (file.exists()) { // if (projectFile.isReadOnly()) { // // // Inform Eclipse that we are intending to modify this file // // This will provide the user the opportunity, via UI prompts, to fetch the file from source code control // // reset a read-only file protection to write etc. // // If there is no shell, i.e. shell is null, then there will be no user UI interaction // // //TODO // //IStatus status = projectFile.getWorkspace().validateEdit(new IFile[]{projectFile}, shell); // // // If the file is still read-only, then we should not attempt the write, since it will // // just fail - just throw an exception, to be caught below, and inform the user // // For other non-successful status, we take our chances, attempt the write, and pass // // along any exception thrown // // //if (!status.isOK()) { // // if (status.getCode() == IResourceStatus.READ_ONLY_LOCAL) { // // stream.close(); // // throw new CoreException(status); // //} // //} // } // projectFile.setContents(new ByteArrayInputStream(utfString.getBytes("UTF-8")), IResource.FORCE, new NullProgressMonitor()); //$NON-NLS-1$ } else { file.createNewFile(); } fileStream = new FileOutputStream(file); byte[] bytes; try { bytes = utfString.getBytes("UTF-8"); //$NON-NLS-1$ } catch (UnsupportedEncodingException e) { bytes = utfString.getBytes(); } fileStream.write(bytes); fileStream.close(); // Close the streams stream.close(); } catch (TransformerConfigurationException e) { throw ExceptionFactory.createCoreException(e); } catch (TransformerException e) { throw ExceptionFactory.createCoreException(e); } catch (IOException e) { throw ExceptionFactory.createCoreException(e); } } ICLanguageSetting findLanguagSettingForFile(String fileName, IProject project, ICLanguageSetting settings[]) { // if (cType != null) { // setting = findLanguageSettingForContentTypeId(cType.getId(), settings, true); // if (setting == null) // setting = findLanguageSettingForContentTypeId(cType.getId(), settings, false); // } ICLanguageSetting setting = null; int index = fileName.lastIndexOf('.'); if (index > 0) { String ext = fileName.substring(index + 1).trim(); if (ext.length() > 0) { setting = findLanguageSettingForExtension(ext, settings); } } return setting; } public ICLanguageSetting findLanguageSettingForContentTypeId(String id, ICLanguageSetting settings[]/*, boolean src*/) { for (ICLanguageSetting setting : settings) { String ids[] = setting.getSourceContentTypeIds(); if (ListComparator.indexOf(id, ids) != -1) return setting; } return null; } public ICLanguageSetting[] findCompatibleSettingsForContentTypeId(String id, ICLanguageSetting[] settings/*, boolean src*/) { IContentTypeManager manager = Platform.getContentTypeManager(); IContentType cType = manager.getContentType(id); if (cType != null) { String [] exts = cType.getFileSpecs(IContentType.FILE_EXTENSION_SPEC); if (exts != null && exts.length != 0) { List<ICLanguageSetting> list = new ArrayList<ICLanguageSetting>(); ICLanguageSetting setting; for (String ext : exts) { setting = findLanguageSettingForExtension(ext, settings/*, src*/); if (setting != null) list.add(setting); } return list.toArray(new ICLanguageSetting[list.size()]); } } return EMPTY_LANGUAGE_SETTINGS_ARRAY; } public ICLanguageSetting findLanguageSettingForExtension(String ext, ICLanguageSetting settings[]/*, boolean src*/) { for (ICLanguageSetting setting : settings) { String exts[] = setting.getSourceExtensions(); /* if (src) { if (setting.getSourceContentType() == null) { exts = setting.getSourceExtensions(); } } else { if (setting.getHeaderContentType() == null) { exts = setting.getHeaderExtensions(); } } */ if (exts != null && exts.length != 0) { for (String ex: exts) { if (ext.equals(ex)) return setting; } } } return null; } /** * Returns a map of configurations elements as discovered from the supplied project * description * @param des * @return Map String -> ICStorageElement: configuration name -> configuration ICStorageElement * @throws CoreException */ Map<String, ICStorageElement> createCfgStorages(ICProjectDescription des) throws CoreException{ LinkedHashMap<String, ICStorageElement> map = new LinkedHashMap<String, ICStorageElement>(); ICStorageElement rootElement = des.getStorage(MODULE_ID, false); if (rootElement != null) { ICStorageElement children[] = rootElement.getChildren(); for (ICStorageElement elem : children) { if (CONFIGURATION.equals(elem.getName())) { String id = elem.getAttribute(CConfigurationSpecSettings.ID); if (id != null) map.put(id, elem); } } } return map; } /** * Create the configuration storage cfgId in the project description * @param storage the root settingsStorage of the project * @param cfgId the configuration Id desire * @return the cfgId as discovered in the project description or a new ICStorageElement with that Id * @throws CoreException on failure to create storage */ ICStorageElement createStorage(ICSettingsStorage storage, String cfgId) throws CoreException { ICStorageElement rootElement = storage.getStorage(MODULE_ID, true); // throws CoreException ICStorageElement children[] = rootElement.getChildren(); ICStorageElement element = null; for (ICStorageElement elem : children) { if (CONFIGURATION.equals(elem.getName()) && cfgId.equals(elem.getAttribute(CConfigurationSpecSettings.ID))) { element = elem; break; } } if (element == null) { element = rootElement.createChild(CONFIGURATION); element.setAttribute(CConfigurationSpecSettings.ID, cfgId); } return element; } /** * Creates a new configuration storage based on an existing 'base' storage. * If a configuration with the new ID already exists in the passed in project storage * a CoreException is thrown. * @param storage the setting storage of the current project description * @param cfgId configID of the new configuration - must be unique in * @param base the base (spec settings) storage element from which settings should be copied. * @return ICStorageElement representing the new configuration * @throws CoreException on failure */ ICStorageElement createStorage(ICSettingsStorage storage, String cfgId, ICStorageElement base) throws CoreException{ ICStorageElement rootElement = storage.getStorage(MODULE_ID, true); ICStorageElement children[] = rootElement.getChildren(); for (ICStorageElement child : children) { if (CONFIGURATION.equals(child.getName()) && cfgId.equals(child.getAttribute(CConfigurationSpecSettings.ID))) throw ExceptionFactory.createCoreException(MessageFormat .format(SettingsModelMessages.getString("CProjectDescriptionManager.cfgIDAlreadyExists"), //$NON-NLS-1$ new Object[] {cfgId})); } ICStorageElement config = rootElement.importChild(base); config.setAttribute(CConfigurationSpecSettings.ID, cfgId); return config; } /** * Remove the storage with the supplied configuration Id from the project * @param storage The root settingsStorage of the project * @param cfgId the configuration ID which would be * @throws CoreException */ void removeStorage(ICSettingsStorage storage, String cfgId) throws CoreException{ ICStorageElement rootElement = storage.getStorage(MODULE_ID, false); if (rootElement != null) { ICStorageElement children[] = rootElement.getChildren(); for (ICStorageElement elem: children) { if (CONFIGURATION.equals(elem.getName()) && cfgId.equals(elem.getAttribute(CConfigurationSpecSettings.ID))) { rootElement.removeChild(elem); break; } } } } void notifyCached(ICConfigurationDescription des, CConfigurationData data, IProgressMonitor monitor) { if (monitor == null) monitor = new NullProgressMonitor(); try { CConfigurationDataProvider provider = getProvider(des); provider.dataCached(des, data, monitor); } catch (CoreException e) { CCorePlugin.log(e); } } void removeData(ICConfigurationDescription des, CConfigurationData data, IProgressMonitor monitor) throws CoreException{ if (monitor == null) monitor = new NullProgressMonitor(); CConfigurationDataProvider provider = getProvider(des); provider.removeConfiguration(des, data, monitor); } CConfigurationData createData(ICConfigurationDescription des, ICConfigurationDescription baseDescription, CConfigurationData base, boolean clone, IProgressMonitor monitor) throws CoreException{ if (monitor == null) monitor = new NullProgressMonitor(); CConfigurationDataProvider provider = getProvider(des); return provider.createConfiguration(des, baseDescription, base, clone, monitor); } CConfigurationDataProvider getProvider(ICConfigurationDescription des) throws CoreException{ CConfigurationDataProviderDescriptor providerDes = getCfgProviderDescriptor(des); if (providerDes == null) throw ExceptionFactory.createCoreException(SettingsModelMessages.getString("CProjectDescriptionManager.1")); //$NON-NLS-1$ return providerDes.getProvider(); } private CConfigurationDataProviderDescriptor getCfgProviderDescriptor(ICConfigurationDescription des) { return getCfgProviderDescriptor(des.getBuildSystemId()); } private CConfigurationDataProviderDescriptor getCfgProviderDescriptor(String id) { initProviderInfo(); return fProviderMap.get(id); } private void initProviderInfo() { if (fProviderMap != null) return; synchronized (this) { if (fProviderMap != null) return; IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(CConfigurationDataProviderDescriptor.DATA_PROVIDER_EXTPOINT_ID); IExtension exts[] = extensionPoint.getExtensions(); fProviderMap = new HashMap<String, CConfigurationDataProviderDescriptor>(exts.length); for (IExtension ext : exts) { CConfigurationDataProviderDescriptor des = new CConfigurationDataProviderDescriptor(ext); fProviderMap.put(des.getId(), des); } } } /* CConfigurationSpecSettings createConfigurationSpecSettings(ICConfigurationDescription cfg) throws CoreException{ CConfigurationSpecSettings settings = null; if (cfg instanceof CConfigurationDescriptionCache) { settings = new CConfigurationSpecSettings(cfg, createStorage(cfg.getProjectDescription(), cfg.getId())); } else { ICProjectDescription des = getProjecDescription(cfg.getProjectDescription().getProject(), false); CConfigurationDescriptionCache cache = (CConfigurationDescriptionCache) des.getConfigurationById(cfg.getId()); if (cache != null) { settings = new CConfigurationSpecSettings(cfg, cache.getSpecSettings()); } else { settings = new CConfigurationSpecSettings(cfg, createStorage(cfg.getProjectDescription(), cfg.getId())); } } return settings; } */ public ICStorageElement createPreferenceStorage(String key, boolean createEmptyIfNotFound, boolean readOnly) throws CoreException{ try { DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); Document doc = null; Element element = null; InputStream stream = null; try{ stream = getPreferenceProperty(key); if (stream != null) { doc = builder.parse(stream); // Get the first element in the project file Node rootElement = doc.getFirstChild(); if (rootElement.getNodeType() != Node.PROCESSING_INSTRUCTION_NODE) { throw ExceptionFactory.createCoreException(SettingsModelMessages.getString("CProjectDescriptionManager.2")); //$NON-NLS-1$ } else { String fileVersion = rootElement.getNodeValue(); Version version = new Version(fileVersion); // Make sure that the version is compatible with the manager // Version must between min version and current version inclusive if (MIN_DESCRIPTION_VERSION.compareTo(version) > 0 || DESCRIPTION_VERSION.compareTo(version) < 0) { throw ExceptionFactory.createCoreException(SettingsModelMessages.getString("CProjectDescriptionManager.3")); //$NON-NLS-1$ } } // Now get the project root element (there should be only one) NodeList nodes = doc.getElementsByTagName(ROOT_PREFERENCE_ELEMENT); if (nodes.getLength() == 0) throw ExceptionFactory.createCoreException(SettingsModelMessages.getString("CProjectDescriptionManager.4")); //$NON-NLS-1$ Node node = nodes.item(0); if (node.getNodeType() != Node.ELEMENT_NODE) throw ExceptionFactory.createCoreException(SettingsModelMessages.getString("CProjectDescriptionManager.5")); //$NON-NLS-1$ element = (Element) node; } else if (!createEmptyIfNotFound) { throw ExceptionFactory.createCoreException(SettingsModelMessages.getString("CProjectDescriptionManager.6")); //$NON-NLS-1$ } } catch (FactoryConfigurationError e) { if (!createEmptyIfNotFound) throw ExceptionFactory.createCoreException(e.getLocalizedMessage()); } catch (SAXException e) { if (!createEmptyIfNotFound) throw ExceptionFactory.createCoreException(e); } catch (IOException e) { if (!createEmptyIfNotFound) throw ExceptionFactory.createCoreException(e); } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { } } } if (element == null) { doc = builder.newDocument(); ProcessingInstruction instruction = doc.createProcessingInstruction(VERSION_ELEMENT_NAME, DESCRIPTION_VERSION.toString()); doc.appendChild(instruction); element = doc.createElement(ROOT_PREFERENCE_ELEMENT); doc.appendChild(element); } return new InternalXmlStorageElement(element, null, false, readOnly); } catch (ParserConfigurationException e) { throw ExceptionFactory.createCoreException(e); } } private InputStream getPreferenceProperty(String key) { InputStream stream = null; File file = getPreferenceFile(key); if (file.exists()) { try { stream = new FileInputStream(file); } catch (FileNotFoundException e) { CCorePlugin.log(e); } } return stream; } private File getPreferenceFile(String key) { IPath path = CCorePlugin.getDefault().getStateLocation(); path = path.append(key); return path.toFile(); } public static File toLocalFile(URI uri, IProgressMonitor monitor) throws CoreException { IFileStore fileStore = EFS.getStore(uri); File localFile = fileStore.toLocalFile(EFS.NONE, monitor); if (localFile ==null) // non local file system localFile= fileStore.toLocalFile(EFS.CACHE, monitor); return localFile; } ICDescriptionDelta createDelta(ICProjectDescription newDescription, ICProjectDescription oldDescription) { CProjectDescriptionDelta delta = new CProjectDescriptionDelta(newDescription, oldDescription); if (delta.getDeltaKind() == ICDescriptionDelta.CHANGED) { ICConfigurationDescription[] cfgs = newDescription.getConfigurations(); for (ICConfigurationDescription cfg : cfgs) { ICConfigurationDescription oldCfg = oldDescription.getConfigurationById(cfg.getId()); CProjectDescriptionDelta cfgDelta = createDelta(cfg, oldCfg); if (cfgDelta != null) { delta.addChild(cfgDelta); } } cfgs = oldDescription.getConfigurations(); for (ICConfigurationDescription cfg : cfgs) { ICConfigurationDescription newCfg = newDescription.getConfigurationById(cfg.getId()); if (newCfg == null) delta.addChild(createDelta(null, cfg)); } if (checkCfgChange(newDescription, oldDescription, true)) delta.addChangeFlags(ICDescriptionDelta.ACTIVE_CFG); if (checkCfgChange(newDescription, oldDescription, false)) delta.addChangeFlags(ICDescriptionDelta.INDEX_CFG); if (oldDescription.isCdtProjectCreating() && !newDescription.isCdtProjectCreating()) delta.addChangeFlags(ICDescriptionDelta.PROJECT_CREAION_COMPLETED); } return delta.isEmpty() ? null : delta; } private boolean checkCfgChange(ICProjectDescription newDes, ICProjectDescription oldDes, boolean active) { ICConfigurationDescription newCfg, oldCfg; if (active) { newCfg = newDes.getActiveConfiguration(); oldCfg = oldDes.getActiveConfiguration(); } else { newCfg = newDes.getDefaultSettingConfiguration(); oldCfg = oldDes.getDefaultSettingConfiguration(); } if (newCfg == null) { return oldCfg != null; } else if (oldCfg == null) { return true; } return !newCfg.getId().equals(oldCfg.getId()); } /* void postProcessNewDescriptionCache(CProjectDescription des, ICProjectDescriptionDelta delta) { if (delta == null && delta.getDeltaKind() != ICProjectDescriptionDelta.CHANGED) return; ICConfigurationDescription indexCfg = des.getIndexConfiguration(); ICConfigurationDescription activeCfg = des.getActiveConfiguration(); ICProjectDescriptionDelta activeCfgDelta = findDelta(activeCfg.getId(), delta); if (indexCfg != activeCfg) { switch(activeCfgDelta.getDeltaKind()) { case ICProjectDescriptionDelta.CHANGED: des.setIndexConfiguration(activeCfg); } } } */ private ICDescriptionDelta findDelta(String id, ICDescriptionDelta delta) { ICDescriptionDelta children[] = delta.getChildren(); ICSettingObject obj; for (ICDescriptionDelta child : children) { obj = child.getSetting(); if (obj.getId().equals(id)) return child; } return null; } public int calculateDescriptorFlags(ICConfigurationDescription newCfg, ICConfigurationDescription oldCfg) { try { int flags = 0; CConfigurationSpecSettings newSettings = ((IInternalCCfgInfo) newCfg).getSpecSettings(); CConfigurationSpecSettings oldSettings = ((IInternalCCfgInfo) oldCfg).getSpecSettings(); String newId = newSettings.getCOwnerId(); String oldId = oldSettings.getCOwnerId(); if (!CDataUtil.objectsEqual(newId, oldId)) flags |= ICDescriptionDelta.OWNER; Map<String, CConfigExtensionReference[]> newMap = newSettings.getExtensionMapCopy(); Map<String, CConfigExtensionReference[]> oldMap = oldSettings.getExtensionMapCopy(); Iterator<Map.Entry<String, CConfigExtensionReference[]>> iter = newMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry<String, CConfigExtensionReference[]> entry = iter.next(); iter.remove(); CConfigExtensionReference[] oldRefs = oldMap.remove(entry.getKey()); if (oldRefs == null) { flags |= ICDescriptionDelta.EXT_REF; break; } CConfigExtensionReference[] newRefs = entry.getValue(); if (newRefs.length != oldRefs.length) { flags |= ICDescriptionDelta.EXT_REF; break; } Set<CConfigExtensionReference> newSet = new HashSet<CConfigExtensionReference>(Arrays.asList(newRefs)); Set<CConfigExtensionReference> oldSet = new HashSet<CConfigExtensionReference>(Arrays.asList(oldRefs)); if (newSet.size() != oldSet.size()) { flags |= ICDescriptionDelta.EXT_REF; break; } newSet.removeAll(oldSet); if (newSet.size() != 0) { flags |= ICDescriptionDelta.EXT_REF; break; } } return flags; } catch (CoreException e) { CCorePlugin.log(e); } return 0; } public CProjectDescriptionDelta createDelta(ICConfigurationDescription newCfg, ICConfigurationDescription oldCfg) { CProjectDescriptionDelta delta = new CProjectDescriptionDelta(newCfg, oldCfg); IInternalCCfgInfo newInfo = (IInternalCCfgInfo) newCfg; IInternalCCfgInfo oldInfo = (IInternalCCfgInfo) oldCfg; if (delta.getDeltaKind() == ICDescriptionDelta.CHANGED) { ICFolderDescription[] foDess = newCfg.getFolderDescriptions(); for (ICFolderDescription foDes : foDess) { ICResourceDescription oldDes = oldCfg.getResourceDescription(foDes.getPath(), true); if (oldDes != null && oldDes.getType() == ICSettingBase.SETTING_FOLDER) { CProjectDescriptionDelta foDelta = createDelta(foDes, (ICFolderDescription) oldDes); if (foDelta != null) delta.addChild(foDelta); } else { delta.addChild(createDelta(foDes, null)); } } foDess = oldCfg.getFolderDescriptions(); for (ICFolderDescription foDes : foDess) { ICResourceDescription newDes = newCfg.getResourceDescription(foDes.getPath(), true); if (newDes == null || newDes.getType() != ICSettingBase.SETTING_FOLDER) { delta.addChild(createDelta(null, foDes)); } } ICFileDescription[] fiDess = newCfg.getFileDescriptions(); for (ICFileDescription fiDes : fiDess) { ICResourceDescription oldDes = oldCfg.getResourceDescription(fiDes.getPath(), true); if (oldDes != null && oldDes.getType() == ICSettingBase.SETTING_FILE) { CProjectDescriptionDelta fiDelta = createDelta(fiDes, (ICFileDescription) oldDes); if (fiDelta != null) delta.addChild(fiDelta); } else { delta.addChild(createDelta(fiDes, null)); } } fiDess = oldCfg.getFileDescriptions(); for (ICFileDescription fiDes : fiDess) { ICResourceDescription newDes = newCfg.getResourceDescription(fiDes.getPath(), true); if (newDes == null || newDes.getType() != ICSettingBase.SETTING_FILE) { delta.addChild(createDelta(null, fiDes)); } } CProjectDescriptionDelta bsDelta = createDelta(newCfg.getBuildSetting(), oldCfg.getBuildSetting()); if (bsDelta != null) delta.addChild(bsDelta); CProjectDescriptionDelta tpsDelta = createDelta(newCfg.getTargetPlatformSetting(), oldCfg.getTargetPlatformSetting()); if (tpsDelta != null) delta.addChild(tpsDelta); if (!newCfg.getName().equals(oldCfg.getName())) { delta.addChangeFlags(ICDescriptionDelta.NAME); } if (!CDataUtil.objectsEqual(newCfg.getDescription(), oldCfg.getDescription())) { delta.addChangeFlags(ICDescriptionDelta.DESCRIPTION); } ICSourceEntry newEntries[] = newCfg.getSourceEntries(); ICSourceEntry oldEntries[] = oldCfg.getSourceEntries(); if (newEntries.length > oldEntries.length) { delta.addChangeFlags(ICDescriptionDelta.SOURCE_ADDED); } else { for (ICSourceEntry newEntry : newEntries) { boolean found = false; for (ICSourceEntry oldEntry : oldEntries) { if (newEntry.equals(oldEntry)) { found = true; break; } } if (!found) { delta.addChangeFlags(ICDescriptionDelta.SOURCE_ADDED); break; } } } if (oldEntries.length > newEntries.length) { delta.addChangeFlags(ICDescriptionDelta.SOURCE_REMOVED); } else { for (ICSourceEntry oldEntry : oldEntries) { boolean found = false; for (ICSourceEntry newEntry : newEntries) { if (oldEntry.equals(newEntry)) { found = true; break; } } if (!found) { delta.addChangeFlags(ICDescriptionDelta.SOURCE_REMOVED); break; } } } try { CConfigurationSpecSettings newSettings = newInfo.getSpecSettings(); CConfigurationSpecSettings oldSettings = oldInfo.getSpecSettings(); if (!newSettings.extRefSettingsEqual(oldSettings)) delta.addChangeFlags(ICDescriptionDelta.EXT_REF); } catch (CoreException e) { CCorePlugin.log(e); } List<ILanguageSettingsProvider> newLSProviders = null; if (newCfg instanceof ILanguageSettingsProvidersKeeper) newLSProviders = ((ILanguageSettingsProvidersKeeper) newCfg).getLanguageSettingProviders(); List<ILanguageSettingsProvider> oldLSProviders = null; if (oldCfg instanceof ILanguageSettingsProvidersKeeper) oldLSProviders = ((ILanguageSettingsProvidersKeeper) oldCfg).getLanguageSettingProviders(); if (newLSProviders != oldLSProviders && (newLSProviders == null || !newLSProviders.equals(oldLSProviders))) delta.addChangeFlags(ICDescriptionDelta.LANGUAGE_SETTINGS_PROVIDERS); calculateCfgExtSettingsDelta(delta); int drFlags = calculateDescriptorFlags(newCfg, oldCfg); if (drFlags != 0) delta.addChangeFlags(drFlags); } return delta.isEmpty() ? null : delta; } private void calculateCfgExtSettingsDelta(CProjectDescriptionDelta delta) { ICConfigurationDescription newDes = (ICConfigurationDescription) delta.getNewSetting(); ICConfigurationDescription oldDes = (ICConfigurationDescription) delta.getOldSetting(); ExtSettingsDelta[] deltas = getSettingChange(newDes, oldDes); int flags = 0; int addedRemoved = ICDescriptionDelta.EXTERNAL_SETTINGS_ADDED | ICDescriptionDelta.EXTERNAL_SETTINGS_REMOVED; if (deltas != null ) { for (ExtSettingsDelta dt : deltas) { ICSettingEntry[][] d = dt.getEntriesDelta(); if (d[0] != null) flags |= ICDescriptionDelta.EXTERNAL_SETTINGS_ADDED; if (d[1] != null) flags |= ICDescriptionDelta.EXTERNAL_SETTINGS_REMOVED; if ((flags & (addedRemoved)) == addedRemoved) break; } // delta.setExtSettingsDeltas(deltas); if (flags != 0) delta.addChangeFlags(flags); } int cfgRefFlags = calcRefChangeFlags(newDes, oldDes); if (cfgRefFlags != 0) delta.addChangeFlags(cfgRefFlags); } private int calcRefChangeFlags(ICConfigurationDescription newDes, ICConfigurationDescription oldDes) { Map<String, String> newMap = newDes != null ? newDes.getReferenceInfo() : null; Map<String, String> oldMap = oldDes != null ? oldDes.getReferenceInfo() : null; int flags = 0; if (newMap == null || newMap.size() == 0) { if (oldMap != null && oldMap.size() != 0) { flags = ICDescriptionDelta.CFG_REF_REMOVED; } } else { if (oldMap == null || oldMap.size() == 0) { flags = ICDescriptionDelta.CFG_REF_ADDED; } else { boolean stop = false; for (Map.Entry<String, String> newEntry : newMap.entrySet()) { String newProj = newEntry.getKey(); String newCfg = newEntry.getValue(); String oldCfg = oldMap.remove(newProj); if (!newCfg.equals(oldCfg)) { flags |= ICDescriptionDelta.CFG_REF_ADDED; if (oldCfg != null) { flags |= ICDescriptionDelta.CFG_REF_REMOVED; stop = true; } if (stop) break; } } if (!oldMap.isEmpty()) flags |= ICDescriptionDelta.CFG_REF_REMOVED; } } return flags; } private ExtSettingsDelta[] getSettingChange(ICConfigurationDescription newDes, ICConfigurationDescription oldDes) { CExternalSetting[] newSettings = newDes != null ? (CExternalSetting[]) newDes.getExternalSettings() : null; CExternalSetting[] oldSettings = oldDes != null ? (CExternalSetting[]) oldDes.getExternalSettings() : null; return CExternalSettinsDeltaCalculator.getInstance().getSettingChange(newSettings, oldSettings); } private CProjectDescriptionDelta createDelta(ICFolderDescription newFo, ICFolderDescription oldFo) { CProjectDescriptionDelta delta = new CProjectDescriptionDelta(newFo, oldFo); if (delta.getDeltaKind() == ICDescriptionDelta.CHANGED) { ICLanguageSetting newLss[] = newFo.getLanguageSettings(); ICLanguageSetting oldLss[] = oldFo.getLanguageSettings(); List<ICLanguageSetting> newList = new ArrayList<ICLanguageSetting>(Arrays.asList(newLss)); List<ICLanguageSetting> oldList = new ArrayList<ICLanguageSetting>(Arrays.asList(oldLss)); List<ICLanguageSetting[]> matched = sortSettings(newList, oldList); for (ICLanguageSetting[] match : matched) { CProjectDescriptionDelta lsDelta = createDelta(match[0], match[1]); if (lsDelta != null) delta.addChild(lsDelta); } for (ICLanguageSetting added : newList) { delta.addChild(createDelta(added, null)); } for (ICLanguageSetting removed : oldList) { delta.addChild(createDelta(null, removed)); } // HashMap oldMap = new HashMap(); // for (int i = 0; i < oldLss.length; i++) { // oldMap.put(oldLss[i].getId(), oldLss[i]); // } // for (int i = 0; i < newLss.length; i++) { // ICLanguageSetting oldLs = (ICLanguageSetting) oldMap.remove(newLss[i].getId()); // CProjectDescriptionDelta lsDelta = createDelta(newLss[i], oldLs); // if (lsDelta != null) // delta.addChild(lsDelta); // } // if (!oldMap.isEmpty()) { // for (Iterator iter = oldMap.values().iterator(); iter.hasNext();) { // delta.addChild(createDelta(null, (ICLanguageSetting) iter.next())); // } // } // if (!newFo.getPath().equals(oldFo.getPath())) // delta.addChangeFlags(ICProjectDescriptionDelta.PATH); if (newFo.isExcluded() != oldFo.isExcluded()) delta.addChangeFlags(ICDescriptionDelta.EXCLUDE); } return delta.isEmpty() ? null : delta; } private List<ICLanguageSetting[]> sortSettings(List<ICLanguageSetting> settings1, List<ICLanguageSetting> settings2) { ICLanguageSetting setting1; ICLanguageSetting setting2; List<ICLanguageSetting[]> result = new ArrayList<ICLanguageSetting[]>(); for (Iterator<ICLanguageSetting> iter1 = settings1.iterator(); iter1.hasNext();) { setting1 = iter1.next(); for (Iterator<ICLanguageSetting> iter2 = settings2.iterator(); iter2.hasNext();) { setting2 = iter2.next(); if (setting2.getId().equals(setting1.getId())) { iter1.remove(); iter2.remove(); ICLanguageSetting [] match = new ICLanguageSetting[2]; match[0] = setting1; match[1] = setting2; result.add(match); break; } } } for (Iterator<ICLanguageSetting> iter1 = settings1.iterator(); iter1.hasNext();) { setting1 = iter1.next(); String lId = setting1.getLanguageId(); if (lId != null) { for (Iterator<ICLanguageSetting> iter2 = settings2.iterator(); iter2.hasNext();) { setting2 = iter2.next(); if (lId.equals(setting2.getLanguageId())) { iter1.remove(); iter2.remove(); ICLanguageSetting [] match = new ICLanguageSetting[2]; match[0] = setting1; match[1] = setting2; result.add(match); break; } } } } for (Iterator<ICLanguageSetting> iter1 = settings1.iterator(); iter1.hasNext();) { setting1 = iter1.next(); String cTypeIds1[] = setting1.getSourceContentTypeIds(); if (cTypeIds1.length != 0) { for (Iterator<ICLanguageSetting> iter2 = settings2.iterator(); iter2.hasNext();) { setting2 = iter2.next(); if (Arrays.equals(cTypeIds1, setting2.getSourceContentTypeIds())) { iter1.remove(); iter2.remove(); ICLanguageSetting [] match = new ICLanguageSetting[2]; match[0] = setting1; match[1] = setting2; result.add(match); break; } } } } for (Iterator<ICLanguageSetting> iter1 = settings1.iterator(); iter1.hasNext();) { setting1 = iter1.next(); if (setting1.getSourceContentTypeIds().length == 0) { String srcExts[] = setting1.getSourceExtensions(); if (srcExts.length != 0) { for (Iterator<ICLanguageSetting> iter2 = settings2.iterator(); iter2.hasNext();) { setting2 = iter2.next(); if (setting2.getSourceContentTypeIds().length == 0) { if (Arrays.equals(srcExts, setting2.getSourceExtensions())) { iter1.remove(); iter2.remove(); ICLanguageSetting [] match = new ICLanguageSetting[2]; match[0] = setting1; match[1] = setting2; result.add(match); break; } } } } } } return result; } private CProjectDescriptionDelta createDelta(ICFileDescription newFi, ICFileDescription oldFi) { CProjectDescriptionDelta delta = new CProjectDescriptionDelta(newFi, oldFi); if (delta.getDeltaKind() == ICDescriptionDelta.CHANGED) { CProjectDescriptionDelta lsDelta = createDelta(newFi.getLanguageSetting(), oldFi.getLanguageSetting()); if (lsDelta != null) delta.addChild(lsDelta); // if (!newFi.getPath().equals(oldFi.getPath())) // delta.addChangeFlags(ICProjectDescriptionDelta.PATH); if (newFi.isExcluded() != oldFi.isExcluded()) delta.addChangeFlags(ICDescriptionDelta.EXCLUDE); } return delta.isEmpty() ? null : delta; } private CProjectDescriptionDelta createDelta(ICLanguageSetting newLs, ICLanguageSetting oldLs) { CProjectDescriptionDelta delta = new CProjectDescriptionDelta(newLs, oldLs); if (delta.getDeltaKind() == ICDescriptionDelta.CHANGED) { if (!CDataUtil.objectsEqual(newLs.getLanguageId(), oldLs.getLanguageId())) delta.addChangeFlags(ICDescriptionDelta.LANGUAGE_ID); int kinds[] = KindBasedStore.getLanguageEntryKinds(); int addedKinds = 0; int removedKinds = 0; int reorderedKinds = 0; for (int kind : kinds) { ICLanguageSettingEntry newEntries[] = newLs.getSettingEntries(kind); ICLanguageSettingEntry oldEntries[] = oldLs.getSettingEntries(kind); boolean[] change = calculateSettingsChanges(newEntries, oldEntries); if (change[0]) addedKinds |= kind; if (change[1]) removedKinds |= kind; if (change[2]) reorderedKinds |= kind; } delta.setAddedLanguageEntriesKinds(addedKinds); delta.setRemovedLanguageEntriesKinds(removedKinds); delta.setReorderedLanguageEntriesKinds(reorderedKinds); String[] newCtIds = newLs.getSourceContentTypeIds(); String[] oldCtIds = oldLs.getSourceContentTypeIds(); if (!Arrays.equals(newCtIds, oldCtIds)) delta.addChangeFlags(ICDescriptionDelta.SOURCE_CONTENT_TYPE); String[] newExts = newLs.getSourceExtensions(); String[] oldExts = oldLs.getSourceExtensions(); if (!Arrays.equals(newExts, oldExts)) delta.addChangeFlags(ICDescriptionDelta.SOURCE_EXTENSIONS); // newCt = newLs.getHeaderContentType(); // oldCt = oldLs.getHeaderContentType(); // if (!compare(newCt, oldCt)) // delta.addChangeFlags(ICDescriptionDelta.HEADER_CONTENT_TYPE); // newExts = newLs.getHeaderExtensions(); // oldExts = oldLs.getHeaderExtensions(); // if (!Arrays.equals(newExts, oldExts)) // delta.addChangeFlags(ICDescriptionDelta.HEADER_ENTENSIONS); } return delta.isEmpty() ? null : delta; } private boolean[] calculateSettingsChanges(ICLanguageSettingEntry newEntries[], ICLanguageSettingEntry oldEntries[]) { boolean result[] = new boolean[3]; // if nothing was known before do not generate any deltas. if (oldEntries == null) { return result; } // Sanity checks if (newEntries == null) { newEntries = EMPTY_LANGUAGE_SETTINGS_ENTRIES_ARRAY; } Set<ICLanguageSettingEntry> newEntrySet = new HashSet<ICLanguageSettingEntry>(Arrays.asList(newEntries)); Set<ICLanguageSettingEntry> oldEntrySet = new HashSet<ICLanguageSettingEntry>(Arrays.asList(oldEntries)); // Check the removed entries. for (ICLanguageSettingEntry oldEntry : oldEntries) { boolean found = false; if (newEntrySet.contains(oldEntry)) { found = true; break; } if (!found) { result[1] = true; break; } } // Check the new entries. for (ICLanguageSettingEntry newEntry : newEntries) { boolean found = false; if (oldEntrySet.contains(newEntry)) { found = true; break; } if (!found) { result[0] = true; break; } } // Check for reorder if (!result[0] && !result[1] && oldEntries.length == newEntries.length) { for (int i = 0; i < newEntries.length; i++) { if (!newEntries[i].equals(oldEntries[i])) { result[2] = true; break; } } } // They may have remove some duplications, catch here .. consider it as reordering. if (!result[0] && !result[1] && oldEntries.length != newEntries.length) { result[2] = true; } return result; } /* public boolean entriesEqual(ICLanguageSettingEntry entries1[], ICLanguageSettingEntry entries2[]) { if (entries1.length != entries2.length) return false; for (int i = 0; i < entries1.length; i++) { if (!entries1[i].equals(entries2[i])) return false; } return true; } */ private CProjectDescriptionDelta createDelta(ICBuildSetting newBuildSetting, ICBuildSetting oldBuildSetting) { CProjectDescriptionDelta delta = new CProjectDescriptionDelta(newBuildSetting, oldBuildSetting); if (!Arrays.equals(newBuildSetting.getErrorParserIDs(), oldBuildSetting.getErrorParserIDs())) delta.addChangeFlags(ICDescriptionDelta.ERROR_PARSER_IDS); return delta.isEmpty() ? null : delta; } private CProjectDescriptionDelta createDelta(ICTargetPlatformSetting newTPS, ICTargetPlatformSetting oldTPS) { CProjectDescriptionDelta delta = new CProjectDescriptionDelta(newTPS, oldTPS); if (!Arrays.equals(newTPS.getBinaryParserIds(), oldTPS.getBinaryParserIds())) delta.addChangeFlags(ICDescriptionDelta.BINARY_PARSER_IDS); return delta.isEmpty() ? null : delta; } ICElementDelta[] generateCElementDeltas(ICProject cProject, ICDescriptionDelta projDesDelta) { if (projDesDelta == null) return EMPTY_CELEMENT_DELTA; int kind = projDesDelta.getDeltaKind(); switch(kind) { case ICDescriptionDelta.CHANGED: ICProjectDescription newDes = (ICProjectDescription) projDesDelta.getNewSetting(); ICProjectDescription oldDes = (ICProjectDescription) projDesDelta.getOldSetting(); // int flags = projDesDelta.getChangeFlags(); // ICConfigurationDescription activeCfg = newDes.getActiveConfiguration(); ICConfigurationDescription indexCfg = newDes.getDefaultSettingConfiguration(); // if (indexCfg != activeCfg) { // ICDescriptionDelta delta = findDelta(activeCfg.getId(), projDesDelta); // if (delta != null && delta.getDeltaKind() == ICDescriptionDelta.CHANGED) { // indexCfg = activeCfg; // newDes.setIndexConfiguration(activeCfg); // } // } ICConfigurationDescription oldIndexCfg = oldDes.getDefaultSettingConfiguration(); ICDescriptionDelta indexDelta; if (oldIndexCfg != null && oldIndexCfg.getId().equals(indexCfg.getId())) { indexDelta = findDelta(indexCfg.getId(), projDesDelta); } else { indexDelta = createDelta(indexCfg, oldIndexCfg); } if (indexDelta != null) { List<CElementDelta> list = new ArrayList<CElementDelta>(); generateCElementDeltasFromCfgDelta(cProject, indexDelta, list); return list.toArray(new ICElementDelta[list.size()]); } return EMPTY_CELEMENT_DELTA; case ICDescriptionDelta.ADDED: case ICDescriptionDelta.REMOVED: break; } return EMPTY_CELEMENT_DELTA; } private List<CElementDelta> generateCElementDeltasFromCfgDelta(ICProject cProject, ICDescriptionDelta cfgDelta, List<CElementDelta> list) { int kind = cfgDelta.getDeltaKind(); switch(kind) { case ICDescriptionDelta.CHANGED: int descriptionFlags = cfgDelta.getChangeFlags(); int celementFlags = 0; if ((descriptionFlags & ICDescriptionDelta.SOURCE_ADDED) != 0) celementFlags |= ICElementDelta.F_ADDED_PATHENTRY_SOURCE; if ((descriptionFlags & ICDescriptionDelta.SOURCE_REMOVED) != 0) celementFlags |= ICElementDelta.F_REMOVED_PATHENTRY_SOURCE; if (celementFlags != 0) { CElementDelta cElDelta = new CElementDelta(cProject.getCModel()); cElDelta.changed(cProject, celementFlags); list.add(cElDelta); } ICDescriptionDelta children[] = cfgDelta.getChildren(); int type; for (ICDescriptionDelta child : children) { type = child.getSettingType(); if (type == ICSettingBase.SETTING_FILE || type == ICSettingBase.SETTING_FOLDER) { generateCElementDeltasFromResourceDelta(cProject, child, list); } } break; case ICDescriptionDelta.ADDED: case ICDescriptionDelta.REMOVED: break; } return list; } /** * The method maps {@link ICDescriptionDelta} to {@link CElementDelta} which are added to the {@code list}. * The delta will indicate modification of CElement for a given resource plus language settings * if they changed (relative to parent resource description if the resource has no its own). */ private void generateCElementDeltasFromResourceDelta(ICProject cProject, ICDescriptionDelta delta, List<CElementDelta> list) { int kind = delta.getDeltaKind(); ICDescriptionDelta parentDelta = delta.getParent(); ICResourceDescription oldRcDes; ICResourceDescription newRcDes; IPath path; switch(kind) { case ICDescriptionDelta.REMOVED: oldRcDes = (ICResourceDescription) delta.getOldSetting(); path = oldRcDes.getPath(); newRcDes = ((ICConfigurationDescription) parentDelta.getNewSetting()).getResourceDescription(path, false); break; case ICDescriptionDelta.ADDED: newRcDes = (ICResourceDescription) delta.getNewSetting(); path = newRcDes.getPath(); oldRcDes = ((ICConfigurationDescription) parentDelta.getOldSetting()).getResourceDescription(path, false); break; case ICDescriptionDelta.CHANGED: newRcDes = (ICResourceDescription) delta.getNewSetting(); path = newRcDes.getPath(); oldRcDes = (ICResourceDescription) delta.getOldSetting(); break; default: // Not possible CCorePlugin.log(new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, SettingsModelMessages.getString("CProjectDescriptionManager.illegalDeltaKind")+kind)); //$NON-NLS-1$ return; } path = path.makeRelative(); ICElement elem = null; try { elem = cProject.findElement(path); } catch (CModelException e) { return; } IResource rc = elem.getResource(); if (rc != null) { CElementDelta ceRcDelta = new CElementDelta(elem.getCModel()); ceRcDelta.changed(elem, ICElementDelta.F_MODIFIERS); list.add(ceRcDelta); if (rc.getType() == IResource.FILE) { String fileName = path.lastSegment(); ICLanguageSetting newLS = getLanguageSetting(newRcDes, fileName); ICLanguageSetting oldLS = getLanguageSetting(oldRcDes, fileName); ICDescriptionDelta ld = createDelta(newLS, oldLS); generateCElementDeltasFromLanguageDelta(elem, ld, list); } else { if (newRcDes.getType() != ICSettingBase.SETTING_FOLDER) { CCorePlugin.log(new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, SettingsModelMessages.getString("CProjectDescriptionManager.wrongTypeOfResourceDescription")+newRcDes)); //$NON-NLS-1$ return; } ICFolderDescription newFoDes = (ICFolderDescription) newRcDes; if (oldRcDes.getType() != ICSettingBase.SETTING_FOLDER) { CCorePlugin.log(new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, SettingsModelMessages.getString("CProjectDescriptionManager.wrongTypeOfResourceDescription")+oldRcDes)); //$NON-NLS-1$ return; } ICFolderDescription oldFoDes = (ICFolderDescription) oldRcDes; ICDescriptionDelta folderDelta = createDelta(newFoDes, oldFoDes); if (folderDelta != null) { for (ICDescriptionDelta child : folderDelta.getChildren()) { if (child.getSettingType() == ICSettingBase.SETTING_LANGUAGE) { generateCElementDeltasFromLanguageDelta(elem, child, list); } } } } } } private ICLanguageSetting getLanguageSetting(ICResourceDescription rcDes, String fileName) { if (rcDes.getType() == ICSettingBase.SETTING_FILE) { return ((ICFileDescription) rcDes).getLanguageSetting(); } return ((ICFolderDescription) rcDes).getLanguageSettingForFile(fileName); } private List<CElementDelta> generateCElementDeltasFromLanguageDelta(ICElement elem, ICDescriptionDelta delta, List<CElementDelta> list) { if (delta == null) return list; int flags = 0; flags |= calculateEntriesFlags(delta.getAddedEntriesKinds(), true); flags |= calculateEntriesFlags(delta.getRemovedEntriesKinds(), false); flags |= calculateEntriesFlags(delta.getReorderedEntriesKinds(), true); if (flags != 0) { CElementDelta cElDelta = new CElementDelta(elem.getCModel()); cElDelta.changed(elem, flags); list.add(cElDelta); } return list; } private int calculateEntriesFlags(int languageDeltaKinds, boolean added) { int flags = 0; int kindsArray[] = kindsToArray(languageDeltaKinds); for (int element : kindsArray) { switch(element) { case ICSettingEntry.INCLUDE_PATH: flags |= ICElementDelta.F_CHANGED_PATHENTRY_INCLUDE; break; case ICSettingEntry.INCLUDE_FILE: flags |= ICElementDelta.F_CHANGED_PATHENTRY_INCLUDE; break; case ICSettingEntry.MACRO: flags |= ICElementDelta.F_CHANGED_PATHENTRY_MACRO; break; case ICSettingEntry.MACRO_FILE: flags |= ICElementDelta.F_CHANGED_PATHENTRY_MACRO; break; case ICSettingEntry.LIBRARY_PATH: flags |= added ? ICElementDelta.F_ADDED_PATHENTRY_LIBRARY : ICElementDelta.F_REMOVED_PATHENTRY_LIBRARY; break; case ICSettingEntry.LIBRARY_FILE: flags |= added ? ICElementDelta.F_ADDED_PATHENTRY_LIBRARY : ICElementDelta.F_REMOVED_PATHENTRY_LIBRARY; break; } } return flags; } int[] kindsToArray(int kinds) { int allKinds[] = KindBasedStore.getLanguageEntryKinds(); int kindsArray[] = new int[allKinds.length]; int num = 0; for (int kind : allKinds) { if ((kind & kinds) != 0) { kindsArray[num++] = kind; } } if (num < allKinds.length) { int tmp[] = new int[num]; if (num > 0) System.arraycopy(kindsArray, 0, tmp, 0, num); kindsArray = tmp; } return kindsArray; } /* * Methods for manipulating the set of project description listeners */ @Override public void addCProjectDescriptionListener(ICProjectDescriptionListener listener, int eventTypes) { fListeners.add(new ListenerDescriptor(listener, eventTypes)); } @Override public void removeCProjectDescriptionListener(ICProjectDescriptionListener listener) { for (ListenerDescriptor listenerDescriptor : fListeners) { if (listenerDescriptor.fListener.equals(listener)) { fListeners.remove(listenerDescriptor); break; } } } public void notifyListeners(CProjectDescriptionEvent event) { int eventType = event.getEventType(); for (ListenerDescriptor listener : fListeners) { if (listener.handlesEvent(eventType)) listener.fListener.handleEvent(event); } } void checkRemovedConfigurations(ICDescriptionDelta delta) { if (delta == null) return; ICDescriptionDelta cfgDeltas[] = delta.getChildren(); for (ICDescriptionDelta cfgDelta : cfgDeltas) { if (cfgDelta.getDeltaKind() == ICDescriptionDelta.REMOVED) { CConfigurationDescriptionCache des = (CConfigurationDescriptionCache) cfgDelta.getOldSetting(); CConfigurationData data = des.getConfigurationData(); try { removeData(des, data, null); } catch (CoreException e) { } } } } @Override public ICConfigurationDescription getPreferenceConfiguration(String buildSystemId) throws CoreException { return getPreferenceConfiguration(buildSystemId, true); } private void runContextOperations(SettingsContext context, IProgressMonitor monitor) { IWorkspaceRunnable toRun = context.createOperationRunnable(); if (toRun != null) { runWspModification(toRun, monitor); } } @Override public ICConfigurationDescription getPreferenceConfiguration(String buildSystemId, boolean write) throws CoreException { ICConfigurationDescription des = getLoaddedPreference(buildSystemId); if (des == null) { try { des = loadPreference(buildSystemId); } catch (CoreException e) { // CCorePlugin.log(e); } if (des == null) { try { des = createNewPreference(buildSystemId); } catch (CoreException e) { CCorePlugin.log(e); } } setLoaddedPreference(buildSystemId, (CConfigurationDescriptionCache) des); } if (des != null && write) { try { des = createWritablePreference((CConfigurationDescriptionCache) des); } catch (CoreException e) { CCorePlugin.log(e); } } return des; } @Override public void setPreferenceConfiguration(String buildSystemId, ICConfigurationDescription des) throws CoreException{ if (!needSavePreference(buildSystemId, des)) return; CConfigurationDescriptionCache cache = createPreferenceCache(des); savePreferenceConfiguration(buildSystemId, cache); setLoaddedPreference(buildSystemId, cache); } private void savePreferenceConfiguration(String buildStystemId, CConfigurationDescriptionCache cache) throws CoreException{ ICStorageElement elem = cache.getSpecSettings().getRootStorageElement(); saveBuildSystemConfigPreferenceStorage(buildStystemId, elem); } private void saveBuildSystemConfigPreferenceStorage(String buildSystemId, ICStorageElement elem) throws CoreException{ ICStorageElement cur = getBuildSystemConfigPreferenceStorage(buildSystemId); ICStorageElement parent = cur.getParent(); parent.removeChild(cur); parent.importChild(elem); savePreferenceStorage(PREFERENCES_STORAGE, MODULE_ID, parent); } private boolean needSavePreference(String buildSystemId, ICConfigurationDescription des) { if (des.isModified() || !des.isPreferenceConfiguration() || !des.getBuildSystemId().equals(buildSystemId)) return true; return false; } private ICConfigurationDescription createWritablePreference(CConfigurationDescriptionCache cache) throws CoreException{ return new CConfigurationDescription(cache, fPrefUpdater); } private CConfigurationDescriptionCache createPreferenceCache(ICConfigurationDescription des) throws CoreException{ IInternalCCfgInfo cfgDes = (IInternalCCfgInfo) des; CConfigurationData baseData = cfgDes.getConfigurationData(false); CConfigurationDescriptionCache baseCache = null; if (baseData instanceof CConfigurationDescriptionCache) { baseCache = (CConfigurationDescriptionCache) baseData; baseData = baseCache.getConfigurationData(); } CConfigurationSpecSettings settings = cfgDes.getSpecSettings(); ICStorageElement rootEl = getBuildSystemConfigPreferenceStorage(des.getBuildSystemId(), true, false); ICStorageElement rootParent = rootEl.getParent(); rootParent.removeChild(rootEl); ICStorageElement baseRootEl = settings.getRootStorageElement(); rootEl = rootParent.importChild(baseRootEl); CConfigurationDescriptionCache cache = new CConfigurationDescriptionCache(des, baseData, baseCache, cfgDes.getSpecSettings(), null, rootEl); SettingsContext context = new SettingsContext(null); cache.applyData(context); cache.doneInitialization(); runContextOperations(context, null); return cache; } private ICConfigurationDescription createNewPreference(String buildSystemId) throws CoreException { ICStorageElement cfgEl = getBuildSystemConfigPreferenceStorage(buildSystemId, true, false); String id = PREFERENCE_CFG_ID_PREFIX + buildSystemId; CConfigurationDescription des = new CConfigurationDescription(id, PREFERENCE_CFG_NAME, buildSystemId, cfgEl, fPrefUpdater); return createPreferenceCache(des); } // private XmlStorage createBuildSystemCfgPrefStore() throws CoreException{ // ICStorageElement elem = getPreferenceStorage(PREFERENCES_STORAGE, MODULE_ID, true, false); // // XmlStorage store = new XmlStorage((InternalXmlStorageElement) elem); // // return store; // } // // ICSettingsStorage getBuildSystemCfgPrefStore() throws CoreException{ // if (fPrefCfgStorage == null) { // fPrefCfgStorage = createBuildSystemCfgPrefStore(); // } // // return copyStorage(fPrefCfgStorage, false); // } ICStorageElement getBuildSystemConfigPreferenceStorage(String buildSystemId) throws CoreException{ return getBuildSystemConfigPreferenceStorage(buildSystemId, true, false); } private ICStorageElement getBuildSystemConfigPreferenceStorage(String buildSystemId, boolean createIfNotDound, boolean readOnly) throws CoreException{ ICStorageElement elem = getPreferenceStorage(PREFERENCES_STORAGE, MODULE_ID, createIfNotDound, readOnly); ICStorageElement cfgEl = null; if (elem != null) { ICStorageElement children[] = elem.getChildren(); for (ICStorageElement child : children) { if (PREFERENCE_BUILD_SYSTEM_ELEMENT.equals(child.getName())) { if (buildSystemId.equals(child.getAttribute(ID))) { cfgEl = child; break; } } } if (cfgEl == null) { cfgEl = elem.createChild(PREFERENCE_BUILD_SYSTEM_ELEMENT); cfgEl.setAttribute(ID, buildSystemId); } } return cfgEl; } private ICConfigurationDescription loadPreference(String buildSystemId) throws CoreException{ ICStorageElement elem = getPreferenceStorage(PREFERENCES_STORAGE, MODULE_ID, false, false); ICStorageElement children[] = elem.getChildren(); ICStorageElement cfgEl = null; for (ICStorageElement child : children) { if (PREFERENCE_BUILD_SYSTEM_ELEMENT.equals(child.getName())) { if (buildSystemId.equals(child.getAttribute(ID))) { cfgEl = child; break; } } } CConfigurationDescriptionCache cache = new CConfigurationDescriptionCache(cfgEl, null); cache.loadData(); cache.doneInitialization(); return cache; } public ICStorageElement getPreferenceStorage(String prefKey, String storageId, boolean createIfNotDound, boolean readOnly) throws CoreException{ XmlStorage store = getPreferenceStore(prefKey, createIfNotDound, readOnly); return store.getStorage(storageId, createIfNotDound); } private XmlStorage getPreferenceStore(String prefKey, boolean createIfNotDound, boolean readOnly) throws CoreException{ ICStorageElement elem = createPreferenceStorage(prefKey, createIfNotDound, readOnly); XmlStorage store = new XmlStorage((InternalXmlStorageElement) elem); return store; } public void savePreferenceStorage(String prefKey, String storageId, ICStorageElement elem) throws CoreException{ XmlStorage store = getPreferenceStore(prefKey, true, false); store.importStorage(storageId, elem); InternalXmlStorageElement rootEl = new InternalXmlStorageElement(store.fElement, store.isReadOnly()); serializePreference(prefKey, rootEl); } private CConfigurationDescriptionCache getLoaddedPreference(String buildSystemId) { return fPreferenceMap.get(buildSystemId); } private void setLoaddedPreference(String buildSystemId, CConfigurationDescriptionCache des) { fPreferenceMap.put(buildSystemId, des); } public CConfigBasedDescriptorManager getDescriptorManager() { return fDescriptorManager; } public CConfigurationData createDefaultConfigData(IProject project, CDataFactory factory) throws CoreException{ return createDefaultConfigData(project, CDataUtil.genId(DEFAULT_CFG_ID_PREFIX), DEFAULT_CFG_NAME, factory); } public CConfigurationData createDefaultConfigData(IProject project, String id, String name, CDataFactory factory) throws CoreException{ if (factory == null) factory = new CDataFactory(); CConfigurationData data = CDataUtil.createEmptyData(id, name, factory, true); // CDataUtil. //// data.initEmptyData(); // // CDataUtil.adjustConfig(data, factory); factory.setModified(data, false); return data; } public boolean isNewStyleIndexCfg(IProject project) { ICProjectDescription des = getProjectDescription(project, false); if (des != null) return isNewStyleIndexCfg(des); return false; } public boolean isNewStyleIndexCfg(ICProjectDescription des) { ICConfigurationDescription cfgDes = des.getDefaultSettingConfiguration(); if (cfgDes != null) return isNewStyleCfg(cfgDes); return false; } @Override public boolean isNewStyleProject(IProject project) { return isNewStyleProject(getProjectDescription(project, false)); } @Override public boolean isNewStyleProject(ICProjectDescription des) { if (des == null) return false; return isNewStyleIndexCfg(des); } public boolean isNewStyleCfg(ICConfigurationDescription cfgDes) { if (cfgDes == null) return false; CConfigurationData data = ((IInternalCCfgInfo) cfgDes).getConfigurationData(false); if (data instanceof CConfigurationDescriptionCache) { data = ((CConfigurationDescriptionCache) data).getConfigurationData(); } return data != null && !PathEntryConfigurationDataProvider.isPathEntryData(data); } // public String[] getContentTypeFileSpecs (IProject project, IContentType type) { // String[] globalSpecs = type.getFileSpecs(IContentType.FILE_EXTENSION_SPEC); // IContentTypeSettings settings = null; // if (project != null) { // IScopeContext projectScope = new ProjectScope(project); // try { // settings = type.getSettings(projectScope); // } catch (Exception e) {} // if (settings != null) { // String[] specs = settings.getFileSpecs(IContentType.FILE_EXTENSION_SPEC); // if (specs.length > 0) { // int total = globalSpecs.length + specs.length; // String[] projSpecs = new String[total]; // int i=0; // for (int j=0; j<specs.length; j++) { // projSpecs[i] = specs[j]; // i++; // } // for (int j=0; j<globalSpecs.length; j++) { // projSpecs[i] = globalSpecs[j]; // i++; // } // return projSpecs; // } // } // } // return globalSpecs; // } // public String[] getExtensionsFromContentTypes(IProject project, String[] typeIds) { // return CDataUtil.getExtensionsFromContentTypes(project, typeIds); // } static ICLanguageSetting getLanguageSettingForFile(ICConfigurationDescription cfgDes, IPath path, boolean ignoreExcludeStatus) { int segCount = path.segmentCount(); if (segCount == 0) return null; ICResourceDescription rcDes = cfgDes.getResourceDescription(path, false); if (rcDes == null || (!ignoreExcludeStatus && rcDes.isExcluded())) return null; if (rcDes.getType() == ICSettingBase.SETTING_FOLDER) { return ((ICFolderDescription) rcDes).getLanguageSettingForFile(path.lastSegment()); } return ((ICFileDescription) rcDes).getLanguageSetting(); } static private HashMap<HashSet<String>, CLanguageData> createExtSetToLDataMap(IProject project, CLanguageData[] lDatas) { HashMap<HashSet<String>, CLanguageData> map = new HashMap<>(); for (CLanguageData lData : lDatas) { String[] exts = CDataUtil.getSourceExtensions(project, lData); HashSet<String> set = new HashSet<String>(Arrays.asList(exts)); map.put(set, lData); } return map; } static boolean removeNonCustomSettings(IProject project, CConfigurationData data) { PathSettingsContainer cont = CDataUtil.createRcDataHolder(data); PathSettingsContainer[] children = cont.getChildren(false); PathSettingsContainer parent; CResourceData childRcData; boolean modified = false; for (PathSettingsContainer child : children) { childRcData = (CResourceData) child.getValue(); if (childRcData.getType() == ICSettingBase.SETTING_FOLDER) { CResourceData parentRcData = null; for (parent = child.getParentContainer(); (parentRcData = (CResourceData) parent.getValue()).getType() != ICSettingBase.SETTING_FOLDER; parent = parent.getParentContainer()) { // no body, this loop is to find the parent } if (!settingsCustomized(project, (CFolderData) parentRcData, (CFolderData) childRcData, parent.isRoot())) { try { data.removeResourceData(childRcData); child.remove(); modified = true; } catch (CoreException e) { CCorePlugin.log(e); } } } else { parent = child.getParentContainer(); if (!settingsCustomized(project, (CResourceData) parent.getValue(), (CFileData) childRcData)) { try { data.removeResourceData(childRcData); child.remove(); modified = true; } catch (CoreException e) { CCorePlugin.log(e); } } } } return modified; } static private boolean settingsCustomized(IProject project, CFolderData parent, CFolderData child, boolean isParentRoot) { if (baseSettingsCustomized(parent, child)) return true; CLanguageData[] childLDatas = child.getLanguageDatas(); CLanguageData[] parentLDatas = parent.getLanguageDatas(); // Note that parent-root can define more languages than regular folder where tools are filtered if (!isParentRoot && childLDatas.length != parentLDatas.length) return true; HashMap<HashSet<String>, CLanguageData> parentMap = createExtSetToLDataMap(project, parentLDatas); HashMap<HashSet<String>, CLanguageData> childMap = createExtSetToLDataMap(project, childLDatas); for (Map.Entry<HashSet<String>, CLanguageData> childEntry : childMap.entrySet()) { CLanguageData parentLData = parentMap.get(childEntry.getKey()); if (parentLData == null) return true; CLanguageData childLData = childEntry.getValue(); if (!langDatasEqual(parentLData, childLData)) return true; } return false; } static private boolean settingsCustomized(IProject project, CResourceData parent, CFileData child) { if (baseSettingsCustomized(parent, child)) return true; CLanguageData lData = child.getLanguageData(); if (parent.getType() == ICSettingBase.SETTING_FOLDER) { CFolderData foParent = (CFolderData) parent; IPath childPath = child.getPath(); String fileName = childPath.lastSegment(); if (PatternNameMap.isPatternName(fileName)) return true; CLanguageData parentLangData = CDataUtil.findLanguagDataForFile(fileName, project, foParent); return !langDatasEqual(lData, parentLangData); } CFileData fiParent = (CFileData) parent; CLanguageData parentLangData = fiParent.getLanguageData(); return !langDatasEqual(lData, parentLangData); } static boolean langDatasEqual(CLanguageData lData1, CLanguageData lData2) { if (lData1 == null) return lData2 == null; if (lData2 == null) return false; int kinds[] = KindBasedStore.getLanguageEntryKinds(); for (int kind : kinds) { ICLanguageSettingEntry entries1[] = lData1.getEntries(kind); ICLanguageSettingEntry entries2[] = lData2.getEntries(kind); if (!Arrays.equals(entries1, entries2)) return false; } return true; } private static boolean baseSettingsCustomized(CResourceData parent, CResourceData child) { // if (parent.isExcluded() != child.isExcluded()) // return true; if (child.hasCustomSettings()) return true; return false; } @Override public ICProjectDescriptionWorkspacePreferences getProjectDescriptionWorkspacePreferences( boolean write) { if (fPreferences == null) { try { fPreferences = loadPreferences(); } catch (CoreException e) { } if (fPreferences == null) fPreferences = new CProjectDescriptionWorkspacePreferences((ICStorageElement) null, null, true); } CProjectDescriptionWorkspacePreferences prefs = fPreferences; if (write) prefs = new CProjectDescriptionWorkspacePreferences(prefs, false); return prefs; } @Override public boolean setProjectDescriptionWorkspacePreferences(ICProjectDescriptionWorkspacePreferences prefs, boolean updateProjects, IProgressMonitor monitor) { boolean changed = false; ICProjectDescriptionWorkspacePreferences oldPrefs = getProjectDescriptionWorkspacePreferences(false); try { if (oldPrefs != prefs && prefs.getConfigurationRelations() != oldPrefs.getConfigurationRelations()) { changed = true; } if (changed) { CProjectDescriptionWorkspacePreferences basePrefs; if (prefs instanceof CProjectDescriptionWorkspacePreferences) { basePrefs = (CProjectDescriptionWorkspacePreferences) prefs; } else { throw new IllegalArgumentException(); } fPreferences = new CProjectDescriptionWorkspacePreferences(basePrefs, null, true); storePreferences(fPreferences); if (updateProjects) updateProjectDescriptions(null, monitor); } } catch (CoreException e) { CCorePlugin.log(e); } return changed; } private void storePreferences(CProjectDescriptionWorkspacePreferences prefs) throws CoreException { ICStorageElement elem = getCProjectDescriptionPreferencesElement(true, false); prefs.serialize(elem); saveCProjectDescriptionPreferencesElement(elem); } private void saveCProjectDescriptionPreferencesElement(ICStorageElement elem) throws CoreException{ ICStorageElement cur = getCProjectDescriptionPreferencesElement(true, false); ICStorageElement parent = cur.getParent(); parent.removeChild(cur); parent.importChild(elem); savePreferenceStorage(PREFERENCES_STORAGE, MODULE_ID, parent); } private CProjectDescriptionWorkspacePreferences loadPreferences() throws CoreException{ ICStorageElement elem = getCProjectDescriptionPreferencesElement(false, true); return new CProjectDescriptionWorkspacePreferences(elem, null, true); } private ICStorageElement getCProjectDescriptionPreferencesElement(boolean createIfNotFound, boolean readOnly) throws CoreException{ ICStorageElement elem = getPreferenceStorage(PREFERENCES_STORAGE, MODULE_ID, createIfNotFound, readOnly); ICStorageElement[] children = elem.getChildren(); for (ICStorageElement child : children) { if (PREFERENCES_ELEMENT.equals(child.getName())) return child; } if (createIfNotFound) return elem.createChild(PREFERENCES_ELEMENT); throw ExceptionFactory.createCoreException(SettingsModelMessages.getString("CProjectDescriptionManager.14")); //$NON-NLS-1$ } @Override public void updateExternalSettingsProviders(String[] ids, IProgressMonitor monitor) { ExtensionContainerFactory.updateReferencedProviderIds(ids, monitor); } boolean isEmptyCreatingDescriptionAllowed() { return fAllowEmptyCreatingDescription; } void setEmptyCreatingDescriptionAllowed(boolean allow) { fAllowEmptyCreatingDescription = allow; } }