/******************************************************************************* * Copyright (c) 2006 IBM 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: * IBM Corporation - initial API and implementation *******************************************************************************/ 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 notOK= new ArrayList(); for (int i= 0; i < result.length; i++) { IStatus status= result[i]; if (!status.isOK()) { notOK.add(status); } } if (notOK.isEmpty()) { return Status.OK_STATUS; } if (notOK.size() == 1) { return (IStatus)notOK.get(0); } return new MultiStatus(ResourcesPlugin.PI_RESOURCES, 0, (IStatus[])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 result= new ArrayList(); for (int i= 0; i < descriptors.length; i++) { IModelProviderDescriptor descriptor= descriptors[i]; 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 (ModelProvider[])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(new IResourceDeltaVisitor() { public boolean visit(IResourceDelta delta) { return 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(); } } }