blob: b91d6c1faca32792b434f4f7137be3ee5424f063 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2015 IBM 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:
* IBM Corporation - initial API and implementation
* James Blackburn (Broadcom Corp.) - ongoing development
* Lars Vogel <Lars.Vogel@vogella.com> - Bug 473427
*******************************************************************************/
package org.eclipse.core.resources.mapping;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.internal.resources.mapping.ChangeDescription;
import org.eclipse.core.internal.resources.mapping.ResourceChangeDescriptionFactory;
import org.eclipse.core.internal.utils.Messages;
import org.eclipse.core.internal.utils.Policy;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.osgi.util.NLS;
/**
* The resource change validator is used to validate that changes made to
* resources will not adversely affect the models stored in those resources.
* <p>
* The validator is used by first creating a resource delta describing the
* proposed changes. A delta can be generated using a {@link IResourceChangeDescriptionFactory}.
* The change is then validated by calling the {@link #validateChange(IResourceDelta, IProgressMonitor)}
* method. This example validates a change to a single file:
* <code>
* IFile file = ..;//some file that is going to be changed
* ResourceChangeValidator validator = ResourceChangeValidator.getValidator();
* IResourceChangeDescriptionFactory factory = validator.createDeltaFactory();
* factory.change(file);
* IResourceDelta delta = factory.getDelta();
* IStatus result = validator.validateChange(delta, null);
* </code>
* If the result status does not have severity {@link IStatus#OK}, then
* the changes may cause problems for models that are built on those
* resources. In this case the user should be presented with the status message
* to determine if they want to proceed with the modification.
* </p>
*
* @since 3.2
*/
public final class ResourceChangeValidator {
private static ResourceChangeValidator instance;
/**
* Return the singleton change validator.
* @return the singleton change validator
*/
public static ResourceChangeValidator getValidator() {
if (instance == null)
instance = new ResourceChangeValidator();
return instance;
}
/**
* Singleton accessor method should be used instead.
* @see #getValidator()
*/
private ResourceChangeValidator() {
super();
}
private IStatus combineResults(IStatus[] result) {
List<IStatus> notOK = new ArrayList<>();
for (IStatus status : result) {
if (!status.isOK()) {
notOK.add(status);
}
}
if (notOK.isEmpty()) {
return Status.OK_STATUS;
}
if (notOK.size() == 1) {
return notOK.get(0);
}
return new MultiStatus(ResourcesPlugin.PI_RESOURCES, 0, notOK.toArray(new IStatus[notOK.size()]), Messages.mapping_multiProblems, null);
}
/**
* Return an empty change description factory that can be used to build a
* proposed resource delta.
* @return an empty change description factory that can be used to build a
* proposed resource delta
*/
public IResourceChangeDescriptionFactory createDeltaFactory() {
return new ResourceChangeDescriptionFactory();
}
private ModelProvider[] getProviders(IResource[] resources) {
IModelProviderDescriptor[] descriptors = ModelProvider.getModelProviderDescriptors();
List<ModelProvider> result = new ArrayList<>();
for (IModelProviderDescriptor descriptor : descriptors) {
try {
IResource[] matchingResources = descriptor.getMatchingResources(resources);
if (matchingResources.length > 0) {
result.add(descriptor.getModelProvider());
}
} catch (CoreException e) {
Policy.log(e.getStatus().getSeverity(), NLS.bind("Could not instantiate provider {0}", descriptor.getId()), e); //$NON-NLS-1$
}
}
return result.toArray(new ModelProvider[result.size()]);
}
/*
* Get the roots of any changes.
*/
private IResource[] getRootResources(IResourceDelta root) {
final ChangeDescription changeDescription = new ChangeDescription();
try {
root.accept(delta -> changeDescription.recordChange(delta));
} catch (CoreException e) {
// Shouldn't happen since the ProposedResourceDelta accept doesn't throw an
// exception and our visitor doesn't either
Policy.log(IStatus.ERROR, "Internal error", e); //$NON-NLS-1$
}
return changeDescription.getRootResources();
}
/**
* Validate the proposed changes contained in the given delta
* by consulting all model providers to determine if the changes
* have any adverse side effects.
* <p>
* This method returns either a {@link ModelStatus}, or a {@link MultiStatus}
* whose children are {@link ModelStatus}. In either case, the severity
* of the status indicates the severity of the possible side-effects of
* the operation. Any severity other than <code>OK</code> should be
* shown to the user. The message should be a human readable message that
* will allow the user to make a decision on whether to continue with the
* operation. The model provider id should indicate which model is flagging the
* the possible side effects.
* </p>
*
* @param delta a delta tree containing the proposed changes
* @return a status indicating any potential side effects
* on models stored in the affected resources.
*/
public IStatus validateChange(IResourceDelta delta, IProgressMonitor monitor) {
monitor = Policy.monitorFor(monitor);
try {
IResource[] resources = getRootResources(delta);
ModelProvider[] providers = getProviders(resources);
if (providers.length == 0)
return Status.OK_STATUS;
monitor.beginTask(Messages.mapping_validate, providers.length);
IStatus[] result = new IStatus[providers.length];
for (int i = 0; i < providers.length; i++)
result[i] = providers[i].validateChange(delta, Policy.subMonitorFor(monitor, 1));
return combineResults(result);
} finally {
monitor.done();
}
}
}