blob: 1877a33db3170314733064fbc0dd937e72100b1f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2008 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.wst.validation.internal;
import org.eclipse.core.resources.IProject;
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.IWorkspace;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.wst.validation.Validator;
import org.eclipse.wst.validation.internal.model.IValidatorVisitor;
/**
* Keep track of a validation operation when it is triggered as part of a build.
* @author karasiuk
*
*/
public class ValOperationManager implements IResourceChangeListener {
/*
* I tried various tests to see what sort of change events I would get. This is what I
* observed using Eclipse 3.4:
*
* Auto Build On
*
* Clean All
* - workspace, clean, pre
* - workspace, clean post
* - workspace, auto, post
*
* Clean Some
* - project1, clean, pre
* - project1, clean, post
* - project2, clean, pre
* - project2, clean, post
* - workspace, Auto, post
*
* Build Working Set - NA
* Build Project - NA
* Build All - NA
*
* Ctrl-S
* - workspace, auto, pre
* - workspace, auto, post
*
* Auto build Off
*
* Clean All
* - same as (auto build on), but no workspace,auto,post event
*
* Clean Some
* - same as (auto build on), but no workspace,auto,post event
*
* Build Working Set
* - project1, incremental, pre
* - project1, incremental, post
* - project2, incremental, pre
* - project2, incremental, post
*
* Build Project
* - same as above
*
* Build All
* - workspace, incremental, pre
* - workspace, incremental, post
*
* Ctrl-S - NA
*
* For the case where a subset of the projects are built there is no way to guess whether they are part of the
* same operation or not. Eclipse threats them as independent events, and so will the validation framework.
*
* So for example, if the user selected two projects (p1 and p2) and built them, the framework would call the
* validators like this:
*
* validation starting on null
* validation starting on P1
* - individual events per resource
* validation finished on P1
* validation finished on null
*
* validation starting on null
* validation starting on P2
* - individual events per resource
* validation finished on P2
* validation finished on null
*/
private static ValOperationManager _me;
/**
* This operation is in affect for a build cycle. At the end of the build it is reinitialized.
*/
private ValOperation _operation;
/**
* In the very common case of doing a clean all (with auto build turned on), Eclipse signals two
* workspace, auto build, post events. One at the end of the clean and one at the end of the
* real build.
*
* If we are doing a clean all, with auto build turned on, we increment this by one,
* so that we know to throw away the first workspace, auto build, post event.
*/
private int _discardAutoPost;
public static synchronized ValOperationManager getDefault(){
if (_me == null)_me = new ValOperationManager();
return _me;
}
private ValOperationManager(){}
public void resourceChanged(IResourceChangeEvent event) {
int type = event.getType();
int kind = event.getBuildKind();
if (kind == IncrementalProjectBuilder.CLEAN_BUILD && ((type & IResourceChangeEvent.PRE_BUILD) != 0)){
processClean(event);
}
if (isBuildStarting(event)){
_operation = new ValOperation();
_operation.setMultiProject(true);
IValidatorVisitor visitor = new IValidatorVisitor(){
public void visit(Validator validator, IProject project, ValType valType,
ValOperation operation, IProgressMonitor monitor) {
validator.validationStarting(project, operation.getState(), monitor);
}
};
ValManager.getDefault().accept(visitor, null, ValType.Build, _operation, new NullProgressMonitor());
}
if (isBuildFinished(event)){
ValOperationJob finished = new ValOperationJob(getOperation());
finished.schedule();
_operation = null;
}
if (Tracing.isLogging()){
String kindName = null;
if (kind == IncrementalProjectBuilder.AUTO_BUILD)kindName = "Auto"; //$NON-NLS-1$
else if (kind == IncrementalProjectBuilder.CLEAN_BUILD)kindName = "Clean"; //$NON-NLS-1$
else if (kind == IncrementalProjectBuilder.FULL_BUILD)kindName = "Full"; //$NON-NLS-1$
else if (kind == IncrementalProjectBuilder.INCREMENTAL_BUILD)kindName = "Incremental"; //$NON-NLS-1$
else kindName = String.valueOf(kind);
StringBuffer b = new StringBuffer(100);
String sourceName = "unknown"; //$NON-NLS-1$
if (event.getSource() instanceof IResource) {
IResource res = (IResource) event.getSource();
sourceName = res.getName();
}
else if (event.getSource() instanceof IWorkspace) {
sourceName = "Workspace"; //$NON-NLS-1$
}
b.append("ValOperationManager-01: A resource has changed, source="+sourceName+", kind="+kindName+", event type=("+type); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
if ((type & IResourceChangeEvent.POST_BUILD) != 0)b.append(", post build"); //$NON-NLS-1$
if ((type & IResourceChangeEvent.PRE_BUILD) != 0){
b.append(", pre build"); //$NON-NLS-1$
}
b.append(')');
IResourceDelta rd = event.getDelta();
if (rd == null)b.append(", there was no resource delta"); //$NON-NLS-1$
Tracing.log(b);
}
}
/**
* Determine if we are starting a new build cycle.
* @param event
* @return
*/
private boolean isBuildStarting(IResourceChangeEvent event) {
int type = event.getType();
int kind = event.getBuildKind();
boolean isWorkspace = event.getSource() instanceof IWorkspace;
boolean preBuild = (type & IResourceChangeEvent.PRE_BUILD) != 0;
if (ResourcesPlugin.getWorkspace().isAutoBuilding()){
if (isWorkspace && preBuild && kind == IncrementalProjectBuilder.CLEAN_BUILD){
_discardAutoPost = 1;
return true;
}
if (isWorkspace && preBuild && kind == IncrementalProjectBuilder.AUTO_BUILD)return true;
}
else {
if (isWorkspace && preBuild && kind == IncrementalProjectBuilder.INCREMENTAL_BUILD)return true;
}
return false;
}
/**
* Determine if we are at the end of a build cycle. This will give callers the ability to
* clear caches etc.
*
* @param event
* @return return true if we are just finishing a build.
*/
private boolean isBuildFinished(IResourceChangeEvent event) {
if (_operation == null)return false;
int type = event.getType();
int kind = event.getBuildKind();
boolean isWorkspace = event.getSource() instanceof IWorkspace;
boolean postBuild = (type & IResourceChangeEvent.POST_BUILD) != 0;
if (ResourcesPlugin.getWorkspace().isAutoBuilding()){
if (isWorkspace && postBuild && kind == IncrementalProjectBuilder.AUTO_BUILD){
if (_discardAutoPost == 1)_discardAutoPost = 0;
else return true;
}
}
else {
if (isWorkspace && postBuild && kind == IncrementalProjectBuilder.INCREMENTAL_BUILD)return true;
}
return false;
}
private void processClean(IResourceChangeEvent event){
// Originally I was using this to monitor IProject build requests as well, but that is not not needed
// since these will be handled by the IncrementalProjectBuilder.clean() method.
IProgressMonitor monitor = new NullProgressMonitor();
Object source = event.getSource();
if (source instanceof IWorkspace) {
ValManager.getDefault().clean(null, getOperation(), monitor);
}
}
/**
* Answer the current validation operation. If we are not in a multiple project validation
* we will return a new one.
*/
public ValOperation getOperation() {
/*
* If we don't have a current operation, we create a new one. The only time we save
* the operation is when we are sure that we are in a multi project validation.
*/
if (_operation == null)return new ValOperation();
return _operation;
}
}