/*******************************************************************************
* Copyright (c) 2007, 2012 Intel Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Intel Corporation - Initial API and implementation
* James Blackburn (Broadcom Corp.)
* Baltasar Belyavsky (Texas Instruments) - bug 340219: Project metadata files are saved unnecessarily
*******************************************************************************/
package org.eclipse.cdt.internal.core.settings.model;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.ICElementDelta;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.settings.model.CProjectDescriptionEvent;
import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
import org.eclipse.cdt.core.settings.model.ICDescriptionDelta;
import org.eclipse.cdt.core.settings.model.ICProjectDescription;
import org.eclipse.cdt.core.settings.model.ICProjectDescriptionManager;
import org.eclipse.cdt.core.settings.model.ICSettingsStorage;
import org.eclipse.cdt.core.settings.model.ICStorageElement;
import org.eclipse.cdt.internal.core.model.CModelOperation;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
/**
* The operation which actually causes the CProjectDescription to be serialized
*
* This organizes the firing the {@link CProjectDescriptionEvent}s to all listeners
*/
public class SetCProjectDescriptionOperation extends CModelOperation {
/** The ProjectDescription Storage being used for this project description */
private final AbstractCProjectDescriptionStorage fPrjDescStorage;
private final CProjectDescription fSetDescription;
private final int fFlags;
/**
* Operation used for persisting the new CProjectDescription
* @param prjDescStorage
* @param cProject
* @param description
* @param flags
*/
public SetCProjectDescriptionOperation(AbstractCProjectDescriptionStorage prjDescStorage, ICProject cProject,
CProjectDescription description, int flags) {
super(cProject);
this.fPrjDescStorage = prjDescStorage;
fFlags = flags;
fSetDescription = description;
}
@Override
protected void executeOperation() throws CModelException {
CProjectDescriptionManager mngr = CProjectDescriptionManager.getInstance();
ICProject cProject = (ICProject) getElementToProcess();
final IProject project = cProject.getProject();
ICProjectDescription fOldDescriptionCache = mngr.getProjectDescription(project, false);
AbstractCProjectDescriptionStorage.fireAboutToApplyEvent(fSetDescription, fOldDescriptionCache);
CProjectDescription fNewDescriptionCache = null;
SettingsContext context = new SettingsContext(project);
boolean needsSerialization = false;
if (fSetDescription != null) {
ICStorageElement newEl = null;
ICSettingsStorage newStorage = null;
try {
ICStorageElement base = fSetDescription.getRootStorageElement();
needsSerialization = fSetDescription.needsDescriptionPersistence();
// el = base;
// FIXME JBB there is deep magic going on here. The project descriptions are being
// changed in non-obvious ways
newEl = fPrjDescStorage.copyElement(base, false);
newStorage = fPrjDescStorage.getStorageForElement(newEl);
} catch (CoreException e) {
CCorePlugin.log(e);
}
boolean creating = fOldDescriptionCache != null ? fOldDescriptionCache.isCdtProjectCreating() : true;
if (creating)
creating = fSetDescription.isCdtProjectCreating();
if (!fSetDescription.isValid() && (!mngr.isEmptyCreatingDescriptionAllowed() || !creating))
throw new CModelException(ExceptionFactory.createCoreException(
SettingsModelMessages.getString("CProjectDescriptionManager.17") + project.getName())); //$NON-NLS-1$
fNewDescriptionCache = new CProjectDescription(fSetDescription, true, newStorage, newEl, creating);
boolean envStates[] = getEnvStates(fNewDescriptionCache);
try {
fPrjDescStorage.setThreadLocalProjectDesc(fNewDescriptionCache);
fNewDescriptionCache.applyDatas(context);
} finally {
fPrjDescStorage.setThreadLocalProjectDesc(null);
setEnvStates(fNewDescriptionCache, envStates);
}
}
ICDescriptionDelta delta = mngr.createDelta(fNewDescriptionCache, fOldDescriptionCache);
mngr.checkRemovedConfigurations(delta);
// Generate the c element deltas
ICElementDelta cElementDeltas[] = mngr.generateCElementDeltas(cProject, delta);
for (ICElementDelta d : cElementDeltas)
addDelta(d);
if (fSetDescription != null)
fSetDescription.switchToCachedAppliedData(fNewDescriptionCache);
try {
final IProjectDescription eDes = context.getEclipseProjectDescription();
if (mngr.checkHandleActiveCfgChange(fNewDescriptionCache, fOldDescriptionCache, eDes,
new NullProgressMonitor())) {
context.setEclipseProjectDescription(eDes);
}
} catch (CoreException e2) {
CCorePlugin.log(e2);
}
// fNewDescriptionCache is still writable and may be written to at this point
AbstractCProjectDescriptionStorage.fireDataAppliedEvent(fNewDescriptionCache, fOldDescriptionCache,
fSetDescription, delta);
cProject.close(); // Why?
// M-A.L: Here is an explanation I came up with from an investigation I did a long time ago (https://bugs.eclipse.org/bugs/show_bug.cgi?id=349564#c10) :
// "Some information in the CModel cache depends on the CProjectDescription. For example,
// CContainerInfo caches the non-C resources (like excluded files). By closing the CProject,
// that cache is deleted and rebuilt from CProjectDescription when needed. As a test,
// I removed this line and excluded some files then I could see the cache not
// being rebuilt and the icons for exclusion became out of sync."
// If you are tempted to remove this line please consider this.
// ExternalSettingsManager.getInstance().updateDepentents(delta);
if (fNewDescriptionCache != null) {
fNewDescriptionCache.doneApplying();
}
// Set 'fSetProjectDescription' as the new read-only project description on the block
fPrjDescStorage.setCurrentDescription(fNewDescriptionCache, true);
CProjectDescriptionEvent event = AbstractCProjectDescriptionStorage.createAppliedEvent(fNewDescriptionCache,
fOldDescriptionCache, fSetDescription, delta);
mngr.notifyListeners(event);
try {
IWorkspaceRunnable toRun = null;
if (fNewDescriptionCache != null
&& !CProjectDescriptionManager.checkFlags(fFlags, ICProjectDescriptionManager.SET_NO_SERIALIZE)) {
if (needsSerialization || isPersistentCoreSettingChanged(event)) {
toRun = fPrjDescStorage.createDesSerializationRunnable();
if (toRun != null)
context.addWorkspaceRunnable(toRun);
}
}
toRun = context.createOperationRunnable();
if (toRun != null)
CProjectDescriptionManager.runWspModification(toRun, getSchedulingRule(), new NullProgressMonitor());
} catch (CoreException e) {
throw new CModelException(e);
}
}
// Can't use project scoped rule see CProjectDescriptionBasicTests...
// /*
// * Instead of using the workspace scheduling rule, use a more refined project scoped rule.
// * This must contain the rule in CConfigBasedDescriptor.setApply(...)
// */
// @Override
// public ISchedulingRule getSchedulingRule() {
//// return null;
// return fPrjDescStorage.getProject();
// }
private static boolean isPersistentCoreSettingChanged(CProjectDescriptionEvent event) {
ICDescriptionDelta delta = event.getProjectDelta();
if (delta == null)
return false;
if (delta.getDeltaKind() != ICDescriptionDelta.CHANGED)
return true;
if (delta.getChildren().length != 0)
return true;
int flags = delta.getChangeFlags();
// check for any flag except ACTIVE_CFG and SETTING_CFG
if ((flags & ~(ICDescriptionDelta.ACTIVE_CFG | ICDescriptionDelta.SETTING_CFG)) != 0)
return true;
return false;
}
@Override
public boolean isReadOnly() {
return false;
}
/**
* Returns a boolean array corresponding to whether the environemnt is
* dirty on the configurations in the array returned by projDesc.getConfigurations()
* @param pd
* @return boolean[] indicating which configurations have a dirty environment
*/
private boolean[] getEnvStates(CProjectDescription pd) {
ICConfigurationDescription[] cfs = pd.getConfigurations();
boolean[] result = new boolean[cfs.length];
for (int i = 0; i < cfs.length; i++) {
if (cfs[i] instanceof IInternalCCfgInfo) {
try {
CConfigurationSpecSettings ss = ((IInternalCCfgInfo) cfs[i]).getSpecSettings();
if (ss != null && ss.getEnvironment() != null)
result[i] = ss.getEnvironment().isDirty();
} catch (CoreException e) {
CCorePlugin.log(e);
}
}
}
return result;
}
/**
* Set
* @param pd
* @param data
*/
private void setEnvStates(CProjectDescription pd, boolean[] data) {
ICConfigurationDescription[] cfs = pd.getConfigurations();
if (cfs == null || data == null)
return;
for (int i = 0; i < cfs.length; i++) {
if (data.length <= i) {
CCorePlugin.log("Error: setEnvStates: different number of configurations as there are envDatas...", //$NON-NLS-1$
new Exception());
break;
}
if (!data[i])
continue; // write only TRUE values
if (cfs[i] instanceof IInternalCCfgInfo) {
try {
CConfigurationSpecSettings ss = ((IInternalCCfgInfo) cfs[i]).getSpecSettings();
if (ss != null && ss.getEnvironment() != null)
ss.getEnvironment().setDirty(true);
} catch (CoreException e) {
CCorePlugin.log(e);
}
}
}
}
}