blob: 6ba01ccefbb5eeb7c6a910f4f23029a51d495834 [file] [log] [blame]
/*
* Copyright (c) 2014, 2015 Eike Stepper (Loehne, Germany) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Eike Stepper - initial API and implementation
*/
package org.eclipse.oomph.projectconfig.presentation.sync;
import org.eclipse.oomph.preferences.PreferenceNode;
import org.eclipse.oomph.preferences.Property;
import org.eclipse.oomph.preferences.util.PreferencesUtil;
import org.eclipse.oomph.projectconfig.Project;
import org.eclipse.oomph.projectconfig.WorkspaceConfiguration;
import org.eclipse.oomph.projectconfig.impl.ProjectConfigPlugin;
import org.eclipse.oomph.projectconfig.presentation.ProjectConfigEditorPlugin;
import org.eclipse.oomph.projectconfig.presentation.sync.ProjectConfigSynchronizerPreferences.PropertyModificationHandling;
import org.eclipse.oomph.projectconfig.util.ProjectConfigUtil;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
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.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.preference.PreferenceDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IStartup;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.osgi.service.prefs.BackingStoreException;
import org.osgi.service.prefs.Preferences;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
public final class ProjectConfigSynchronizer implements IStartup
{
private static final IWorkspace WORKSPACE = ResourcesPlugin.getWorkspace();
private static final IWorkspaceRoot WORKSPACE_ROOT = WORKSPACE.getRoot();
private WorkspaceConfiguration workspaceConfiguration;
private ProjectConfigSynchronizerDialog projectConfigSynchronizerDialog;
protected IResourceChangeListener resourceChangeListener = new IResourceChangeListener()
{
public void resourceChanged(IResourceChangeEvent event)
{
IResourceDelta delta = event.getDelta();
if (delta != null)
{
try
{
class ResourceDeltaVisitor implements IResourceDeltaVisitor
{
private Collection<IPath> changedPaths = new HashSet<IPath>();
public Collection<IPath> getChangedPaths()
{
return changedPaths;
}
public boolean visit(final IResourceDelta delta)
{
int type = delta.getResource().getType();
if (type == IResource.FOLDER)
{
// Visit the folder's children only if its the .settings folder.
IPath fullPath = delta.getFullPath();
if (!".settings".equals(fullPath.lastSegment())) //$NON-NLS-1$
{
return false;
}
}
else if (type == IResource.FILE)
{
// Include only the *.prefs resources in the .settings folder.
IPath fullPath = delta.getFullPath();
if (fullPath.segmentCount() > 2 && "prefs".equals(fullPath.getFileExtension()) //$NON-NLS-1$
&& !ProjectConfigUtil.PROJECT_CONF_NODE_NAME.equals(fullPath.removeFileExtension().lastSegment()))
{
changedPaths.add(fullPath);
}
return false;
}
// Recursively visit only the workspace root, the projects, and the .settings folders in projects.
return true;
}
}
ResourceDeltaVisitor visitor = new ResourceDeltaVisitor();
delta.accept(visitor);
final Collection<IPath> changedPaths = visitor.getChangedPaths();
if (!changedPaths.isEmpty())
{
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable()
{
public void run()
{
final WorkspaceConfiguration newWorkspaceConfiguration = ProjectConfigUtil.getWorkspaceConfiguration();
try
{
newWorkspaceConfiguration.updatePreferenceProfileReferences();
Resource newWorkspaceConfigurationResource = newWorkspaceConfiguration.eResource();
Map<Property, Property> propertyMap = new LinkedHashMap<Property, Property>();
for (IPath preferenceNodePath : changedPaths)
{
String projectName = preferenceNodePath.segment(0);
Project oldProject = workspaceConfiguration.getProject(projectName);
Project newProject = newWorkspaceConfiguration.getProject(projectName);
if (newProject != null)
{
String preferenceNodeName = preferenceNodePath.removeFileExtension().lastSegment();
PreferenceNode oldPreferenceNode = oldProject == null ? null : oldProject.getPreferenceNode().getNode(preferenceNodeName);
PreferenceNode newPreferenceNode = newProject.getPreferenceNode().getNode(preferenceNodeName);
Map<Property, Property> modifiedProperties = collectModifiedProperties(workspaceConfiguration, oldPreferenceNode, newPreferenceNode);
if (!modifiedProperties.isEmpty())
{
for (Map.Entry<Property, Property> entry : modifiedProperties.entrySet())
{
Property property = entry.getKey();
String value = property.getValue();
Property preferenceProfileProperty = newProject.getProperty(property.getRelativePath());
if (preferenceProfileProperty != null)
{
String preferenceProfilePropertyValue = preferenceProfileProperty.getValue();
if (value == null ? preferenceProfilePropertyValue != null : !value.equals(preferenceProfilePropertyValue))
{
propertyMap.put(property, preferenceProfileProperty);
}
}
else
{
propertyMap.put(property, entry.getValue());
}
}
}
}
}
if (!propertyMap.isEmpty())
{
boolean dialogCreator = false;
IWorkbenchWindow activeWorkbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
Shell shell = activeWorkbenchWindow.getShell();
if (projectConfigSynchronizerDialog == null
&& (configurationValidationPrompt || propertyModificationHandling == PropertyModificationHandling.PROMPT))
{
projectConfigSynchronizerDialog = new ProjectConfigSynchronizerDialog(shell);
projectConfigSynchronizerDialog.setWorkspaceConfiguration(newWorkspaceConfiguration);
dialogCreator = true;
}
Map<Property, Property> managedProperties = new HashMap<Property, Property>();
for (Map.Entry<Property, Property> propertyEntry : propertyMap.entrySet())
{
Property property = propertyEntry.getKey();
Property otherProperty = propertyEntry.getValue();
if (otherProperty != null && otherProperty.eResource() == newWorkspaceConfigurationResource)
{
if (propertyModificationHandling == PropertyModificationHandling.PROMPT)
{
projectConfigSynchronizerDialog.managedProperty(property, otherProperty);
}
managedProperties.put(property, otherProperty);
}
else
{
if (configurationValidationPrompt)
{
projectConfigSynchronizerDialog.unmanagedProperty(property, otherProperty);
}
}
}
if (projectConfigSynchronizerDialog != null)
{
if (dialogCreator)
{
ProjectConfigSynchronizerDialog dialog = projectConfigSynchronizerDialog;
projectConfigSynchronizerDialog.open();
if (dialog.hasUnmanagedProperties() && ProjectConfigSynchronizerPreferences.isEdit())
{
PreferenceDialog preferenceDialog = org.eclipse.ui.dialogs.PreferencesUtil.createPreferenceDialogOn(shell,
"org.eclipse.oomph.projectconfig.presentation.ProjectConfigPreferencePage", null, null); //$NON-NLS-1$
preferenceDialog.open();
}
}
else
{
Shell dialogShell = projectConfigSynchronizerDialog.getShell();
Display display = dialogShell.getDisplay();
while (!dialogShell.isDisposed())
{
if (!display.readAndDispatch())
{
display.sleep();
}
}
}
}
if (!managedProperties.isEmpty() && (propertyModificationHandling == PropertyModificationHandling.PROPAGATE
|| propertyModificationHandling == PropertyModificationHandling.PROMPT && ProjectConfigSynchronizerPreferences.isPropagate()))
{
for (Map.Entry<Property, Property> entry : managedProperties.entrySet())
{
Property managedProperty = entry.getKey();
Property managingProperty = entry.getValue();
try
{
Preferences preferences = PreferencesUtil.getPreferences(managingProperty.getParent(), false);
preferences.put(managingProperty.getName(), managedProperty.getValue());
}
catch (BackingStoreException ex)
{
ProjectConfigEditorPlugin.INSTANCE.log(ex);
}
}
try
{
WORKSPACE_ROOT.getWorkspace().run(new IWorkspaceRunnable()
{
public void run(IProgressMonitor monitor) throws CoreException
{
try
{
PreferencesUtil.getPreferences(newWorkspaceConfiguration.getInstancePreferenceNode().getParent().getNode("project"), false) //$NON-NLS-1$
.flush();
}
catch (BackingStoreException ex)
{
ProjectConfigPlugin.INSTANCE.log(ex);
}
}
}, new NullProgressMonitor());
}
catch (CoreException ex)
{
ProjectConfigPlugin.INSTANCE.log(ex);
}
}
}
// We expect this processing of the managed property to cause another delta which will apply the
// preference profiles anyway, so
// don't do it now when we might overwrite the properties being propagated.
if (projectConfigSynchronizerDialog == null || !projectConfigSynchronizerDialog.hasManagedProperties())
{
newWorkspaceConfiguration.applyPreferenceProfiles();
}
}
finally
{
workspaceConfiguration = newWorkspaceConfiguration;
projectConfigSynchronizerDialog = null;
}
}
private Map<Property, Property> collectModifiedProperties(WorkspaceConfiguration workspaceConfiguration, PreferenceNode oldPreferenceNode,
PreferenceNode newPreferenceNode)
{
LinkedHashMap<Property, Property> result = new LinkedHashMap<Property, Property>();
collectModifiedProperties(result, workspaceConfiguration, oldPreferenceNode, newPreferenceNode);
return result;
}
private void collectModifiedProperties(Map<Property, Property> result, WorkspaceConfiguration workspaceConfiguration,
PreferenceNode oldPreferenceNode, PreferenceNode newPreferenceNode)
{
if (newPreferenceNode != null)
{
for (PreferenceNode newChild : newPreferenceNode.getChildren())
{
PreferenceNode oldChild = oldPreferenceNode == null ? null : oldPreferenceNode.getNode(newChild.getName());
if (oldChild == null)
{
for (Property newProperty : newChild.getProperties())
{
result.put(newProperty, null);
}
}
else
{
collectModifiedProperties(result, workspaceConfiguration, oldChild, newChild);
}
}
for (Property newProperty : newPreferenceNode.getProperties())
{
Property oldProperty = oldPreferenceNode == null ? null : oldPreferenceNode.getProperty(newProperty.getName());
if (oldProperty == null)
{
result.put(newProperty, null);
}
else
{
String newValue = newProperty.getValue();
String oldValue = oldProperty.getValue();
if (newValue == null ? oldValue != null : !newValue.equals(oldValue))
{
result.put(newProperty, oldProperty);
}
}
}
}
}
});
}
}
catch (CoreException exception)
{
ProjectConfigEditorPlugin.INSTANCE.log(exception);
}
}
}
};
private boolean configurationValidationPrompt = ProjectConfigSynchronizerPreferences.isConfigurationValidationPrompt();
private PropertyModificationHandling propertyModificationHandling = ProjectConfigSynchronizerPreferences.getPropertyModificationHandling();
public ProjectConfigSynchronizer()
{
ProjectConfigEditorPlugin.Implementation.setProjectConfigSynchronizer(this);
}
public void earlyStartup()
{
update();
}
public void stop()
{
WORKSPACE.removeResourceChangeListener(resourceChangeListener);
}
public void update()
{
if (ProjectConfigSynchronizerPreferences.isConfigurationManagementAutomatic())
{
new ProjectConfigUtil.CompletenessChecker()
{
@Override
protected void complete(WorkspaceConfiguration workspaceConfiguration)
{
ProjectConfigSynchronizer.this.workspaceConfiguration = workspaceConfiguration;
workspaceConfiguration.updatePreferenceProfileReferences();
}
};
WORKSPACE.addResourceChangeListener(resourceChangeListener);
}
else
{
WORKSPACE.removeResourceChangeListener(resourceChangeListener);
}
configurationValidationPrompt = ProjectConfigSynchronizerPreferences.isConfigurationValidationPrompt();
propertyModificationHandling = ProjectConfigSynchronizerPreferences.getPropertyModificationHandling();
}
}