| /******************************************************************************* |
| * 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 java.lang.reflect.InvocationTargetException; |
| import java.util.BitSet; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.TreeSet; |
| |
| import org.eclipse.core.resources.IFolder; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IResourceVisitor; |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.ISafeRunnable; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.QualifiedName; |
| import org.eclipse.core.runtime.SafeRunner; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.SubMonitor; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.wst.common.project.facet.core.FacetedProjectFramework; |
| import org.eclipse.wst.common.project.facet.core.events.IFacetedProjectEvent; |
| import org.eclipse.wst.common.project.facet.core.events.IFacetedProjectListener; |
| import org.eclipse.wst.validation.Friend; |
| import org.eclipse.wst.validation.IPerformanceMonitor; |
| import org.eclipse.wst.validation.IValidatorGroupListener; |
| import org.eclipse.wst.validation.PerformanceCounters; |
| import org.eclipse.wst.validation.ValidationFramework; |
| import org.eclipse.wst.validation.ValidationResult; |
| import org.eclipse.wst.validation.ValidationState; |
| import org.eclipse.wst.validation.Validator; |
| import org.eclipse.wst.validation.internal.model.GlobalPreferences; |
| import org.eclipse.wst.validation.internal.model.IValidatorVisitor; |
| import org.eclipse.wst.validation.internal.model.ProjectPreferences; |
| import org.eclipse.wst.validation.internal.operations.ManualValidatorsOperation; |
| import org.eclipse.wst.validation.internal.plugin.ValidationPlugin; |
| import org.osgi.service.prefs.BackingStoreException; |
| |
| /** |
| * A central place to keep track of all the validators. |
| * @author karasiuk |
| * |
| */ |
| public class ValManager implements IValChangedListener, IFacetedProjectListener, IProjectChangeListener { |
| |
| private static ValManager _me; |
| |
| /** |
| * Projects may be allowed to override the global validation settings. If that is the case then those |
| * project specific settings are saved here. If the key exists, but the value is null, then that |
| * means that the project has been checked and it does not have any specific settings. |
| */ |
| private Map<IProject, ProjectPreferences> _projectPreferences = |
| Collections.synchronizedMap(new HashMap<IProject, ProjectPreferences>(50)); |
| |
| private GlobalPreferences _globalPreferences; |
| |
| /** |
| * This number increases each time any of the validation configurations change. It is used to determine |
| * if information that we have cached in the ValProperty is stale or not. This starts off at zero, each time |
| * the workbench is started. |
| */ |
| private int _configNumber; |
| private ValidatorIdManager _idManager = new ValidatorIdManager(); |
| |
| private ValidatorProjectManager _projectManager = new ValidatorProjectManager(); |
| |
| private static final QualifiedName StatusBuild = new QualifiedName(ValidationPlugin.PLUGIN_ID, "sb"); //$NON-NLS-1$ |
| private static final QualifiedName StatusManual = new QualifiedName(ValidationPlugin.PLUGIN_ID, "sm"); //$NON-NLS-1$ |
| |
| public static synchronized ValManager getDefault(){ |
| if (_me == null)_me = new ValManager(); |
| return _me; |
| } |
| |
| private ValManager(){ |
| ValPrefManagerGlobal.getDefault().addListener(this); |
| ValPrefManagerProject.addListener(this); |
| FacetedProjectFramework.addListener(this, IFacetedProjectEvent.Type.PROJECT_MODIFIED); |
| EventManager.getManager().addProjectChangeListener(this); |
| } |
| |
| /** |
| * This needs to be called if the ValManager is ever deleted. |
| */ |
| public void dispose(){ |
| // currently nobody calls this method, because this instance is never removed, but the method is |
| // here for completeness none the less. |
| ValPrefManagerGlobal.getDefault().removeListener(this); |
| ValPrefManagerProject.removeListener(this); |
| FacetedProjectFramework.removeListener(this); |
| EventManager.getManager().removeProjectChangeListener(this); |
| } |
| |
| /** |
| * Answer all the registered validators. If you are planning on making changes to the validators, |
| * and then saving them in a preference store then you probably want the getValidatorsCopy method. |
| * Because if you make changes to the original validators, and since we only save differences, |
| * there won't be any differences. |
| * |
| * @return Answer an empty array if there are no validators. |
| * |
| * @see #getValidatorsCopy() |
| */ |
| public Validator[] getValidators(){ |
| return getValidators(null); |
| } |
| |
| /** |
| * Answer copies of all the registered validators. If you are going to be making changes to the validators |
| * and then saving them backing into the preference store, then this is the method to use. |
| * |
| * @return Answer an empty array if there are no validators. |
| */ |
| public Validator[] getValidatorsCopy(){ |
| Validator[] orig = getValidators(); |
| Validator[] copy = new Validator[orig.length]; |
| for (int i=0; i<orig.length; i++)copy[i] = orig[i].copy(); |
| return copy; |
| } |
| |
| /** |
| * Answer all the validators for the given project. |
| * <p> |
| * Individual projects may override the global validation preference |
| * settings. If the project has it's own settings, then those validators are |
| * returned via this method. |
| * </p> |
| * <p> |
| * The following approach is used. For version 1 validators, the validator |
| * is only returned if it is defined to operate on this project type. This |
| * is the way that the previous version of the framework did it. For version |
| * 2 validators, they are all returned. |
| * </p> |
| * |
| * @param project |
| * this may be null, in which case the global preferences are |
| * returned. |
| * @param respectOverrideSettings |
| * if this is true then the validators that get returned are |
| * based on the override settings. So for example, if the global |
| * preferences do not allow project overrides then none of the |
| * project settings are used. Normal validation would set this to true. |
| * The properties page would set this to false. |
| * |
| * @deprecated Use {@link #getValidators(IProject)} instead |
| */ |
| public synchronized Validator[] getValidators(IProject project, boolean respectOverrideSettings) throws ProjectUnavailableError { |
| return getValidators(project); |
| } |
| |
| /** |
| * Answer all the validators for the given project. |
| * <p> |
| * Individual projects may override the global validation preference |
| * settings. If the project has it's own settings, then those validators are |
| * returned via this method. |
| * </p> |
| * <p> |
| * The following approach is used. For version 1 validators, the validator |
| * is only returned if it is defined to operate on this project type. This |
| * is the way that the previous version of the framework did it. For version |
| * 2 validators, they are all returned. |
| * </p> |
| * |
| * @param project |
| * This may be null, in which case the global preferences are returned. |
| */ |
| public synchronized Validator[] getValidators(IProject project) throws ProjectUnavailableError { |
| Map<String,Validator> v2Vals = getV2Validators(project); |
| TreeSet<Validator> sorted = new TreeSet<Validator>(); |
| for (Validator v : v2Vals.values())sorted.add(v); |
| |
| try { |
| ValidationConfiguration vc = ConfigurationManager.getManager().getConfiguration(project); |
| if (project == null){ |
| // If the project is null we need to use this approach, since you can not use new ManualValidatorsOperation(null) |
| ValidatorMetaData[] vmds = vc.getValidators(); |
| for (ValidatorMetaData vmd : vmds){ |
| Validator v = Validator.create(vmd, vc, project); |
| sorted.add(v); |
| } |
| } |
| else { |
| ManualValidatorsOperation mvo = new ManualValidatorsOperation(project); |
| Set<ValidatorMetaData> vmds = mvo.getEnabledValidators(); |
| for (ValidatorMetaData vmd : vmds){ |
| Validator v = Validator.create(vmd, vc, project); |
| sorted.add(v); |
| } |
| } |
| } |
| catch (InvocationTargetException e){ |
| ValidationPlugin.getPlugin().handleException(e); |
| } |
| |
| Validator[] vals = new Validator[sorted.size()]; |
| sorted.toArray(vals); |
| return vals; |
| } |
| |
| /** |
| * Answer the V2 validators that are in effect for this project. The following approach is used: |
| * <ol> |
| * <li>The validators that are defined by the extension point are loaded.</li> |
| * <li>They are customized by any global preferences.</li> |
| * <li>If project customizations are allowed, they are customized by the project preferences. |
| * </ol> |
| * |
| * @param project |
| * This may be null, in which case only the global preferences are used. |
| * @return |
| */ |
| private Map<String,Validator> getV2Validators(IProject project){ |
| Map<String,Validator> extVals = ExtensionValidators.instance().getMapV2Copy(); |
| try { |
| List<Validator> vals = ValPrefManagerGlobal.getDefault().getValidators(); |
| for (Validator v : vals)extVals.put(v.getId(), v); |
| |
| if (!mustUseGlobalValidators(project)){ |
| //TODO should probably cache this vpm |
| ValPrefManagerProject vpm = new ValPrefManagerProject(project); |
| vals = vpm.getValidators(extVals); |
| for (Validator v : vals)extVals.put(v.getId(), v); |
| } |
| } |
| catch (BackingStoreException e){ |
| ValidationPlugin.getPlugin().handleException(e); |
| } |
| return extVals; |
| } |
| |
| |
| /** |
| * Answer true if we must use the global settings for this project. If the global preferences do not |
| * allow overrides, or if this project does not allow overrides then the global preferences must be used. |
| * |
| * @param project project that is being tested. It can be null, in which case the global preferences must be used. |
| * @return true if the global preferences must be used. |
| */ |
| public boolean mustUseGlobalValidators(IProject project){ |
| if (project == null)return true; |
| if (!getGlobalPreferences().getOverride())return true; |
| ProjectPreferences pp = getProjectPreferences2(project); |
| if (pp == null){ |
| ValPrefManagerProject vpm = new ValPrefManagerProject(project); |
| pp = new ProjectPreferences(project); |
| vpm.loadProjectPreferencesShallow(pp); |
| } |
| |
| return !pp.getOverride(); |
| } |
| |
| /** |
| * Answer the validator with the given id that is in effect for the given project. |
| * |
| * @param id The validator id. |
| * @param project |
| * @return null if the validator is not found |
| */ |
| public Validator getValidator(String id, IProject project){ |
| Validator[] vals = getValidators(project); |
| for (Validator v : vals){ |
| if (v.getId().equals(id))return v; |
| } |
| return null; |
| } |
| |
| /** |
| * @see ValidationFramework#getValidator(String, IProject) |
| */ |
| public Validator getValidatorWithId(String id, IProject project){ |
| Validator[] vals = getValidators(project); |
| for (Validator v : vals){ |
| if (v.getId().equals(id))return v; |
| } |
| return null; |
| } |
| |
| /** |
| * Answer true if the resource has any enabled validators. |
| * |
| * @param resource a file, folder or project. |
| * |
| * @param isManual if true then the validator must be turned on for manual validation. |
| * If false then the isManualValidation setting isn't used to filter out validators. |
| * |
| * @param isBuild if true then the validator must be turned on for build based validation. |
| * If false then the isBuildValidation setting isn't used to filter out validators. |
| */ |
| public boolean hasValidators(IResource resource, boolean isManual, boolean isBuild){ |
| if (resource instanceof IProject){ |
| IProject project = (IProject)resource; |
| return ValManager.getDefault().getValidators(project).length > 0; |
| } |
| else if (resource instanceof IFolder){ |
| IFolder folder = (IFolder)resource; |
| HasValidatorVisitor v = new HasValidatorVisitor(isManual, isBuild); |
| return v.hasValidator(folder); |
| } |
| else { |
| ContentTypeWrapper ctw = new ContentTypeWrapper(); |
| for (Validator val : ValManager.getDefault().getValidators(resource.getProject())){ |
| if (Friend.shouldValidate(val, resource, isManual, isBuild, ctw))return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Answer true if the project has disabled all of it's validators, or if project overrides are not |
| * allowed if global validation has been disabled. |
| * |
| * @param project the project that is being consulted, or null if only the global settings are to be |
| * checked. |
| */ |
| public boolean isDisabled(IProject project){ |
| GlobalPreferences gp = getGlobalPreferences(); |
| if (!gp.getOverride() || project == null)return gp.getDisableAllValidation(); |
| |
| ProjectPreferences pp = getProjectPreferences2(project); |
| if (pp == null)return gp.getDisableAllValidation(); |
| return pp.getSuspend(); |
| } |
| |
| /** |
| * Answer all the registered validators as they were defined by the extension points. That is |
| * answer the validators as if the user has never applied any customizations. |
| * |
| * @return Answer an empty array if there are no validators. |
| */ |
| public static Validator[] getDefaultValidators() throws InvocationTargetException { |
| Map<String,Validator> extVals = ExtensionValidators.instance().getMapV2(); |
| TreeSet<Validator> sorted = new TreeSet<Validator>(); |
| for (Validator v : extVals.values())sorted.add(v); |
| |
| IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); |
| GlobalConfiguration gc = new GlobalConfiguration(root); |
| gc.resetToDefault(); |
| for (ValidatorMetaData vmd : gc.getValidators()){ |
| Validator v = Validator.create(vmd, gc, null); |
| v.setBuildValidation(vmd.isBuildValidation()); |
| v.setManualValidation(vmd.isManualValidation()); |
| sorted.add(v); |
| } |
| |
| Validator[] val = new Validator[sorted.size()]; |
| sorted.toArray(val); |
| return val; |
| } |
| |
| public static Validator[] getDefaultValidators(IProject project) throws InvocationTargetException { |
| Map<String,Validator> extVals = ExtensionValidators.instance().getMap(project); |
| Validator[] val = new Validator[extVals.size()]; |
| extVals.values().toArray(val); |
| return val; |
| } |
| |
| /** |
| * Answer all the registered validators. |
| * |
| * @param project the project to use for getting the version 1 validator settings. This can |
| * be null in which case the global preferences are used. |
| * |
| * @return Answer an empty array if there are no validators. |
| */ |
| // Validator[] getValidators2(IProject project) throws ProjectUnavailableError { |
| // // If I use a local variable I don't need to synchronize the method. |
| // |
| // Validator[] validators = _validators; |
| // if (project == null && validators != null)return validators; |
| // |
| // Validator[] val = loadExtensions(false, project); |
| // ValPrefManagerGlobal vpm = ValPrefManagerGlobal.getDefault(); |
| // if (!vpm.loadPreferences(val)){ |
| // val = restoreDefaults2(project); |
| // saveStateTimestamp(); |
| // } |
| // else { |
| // if (getGlobalPreferences().getStateTimeStamp() != Platform.getStateStamp()) |
| // val = migrateSettings(val, project); |
| // } |
| // |
| // TreeSet<Validator> set = new TreeSet<Validator>(); |
| // for (Validator v : val)set.add(v); |
| // |
| // List<Validator> list = new LinkedList<Validator>(); |
| // try { |
| // ValidationConfiguration vc = ConfigurationManager.getManager().getConfiguration(project); |
| // for (ValidatorMetaData vmd : vc.getValidators()){ |
| // list.add(Validator.create(vmd, vc, project)); |
| // } |
| // |
| // } |
| // catch (InvocationTargetException e){ |
| // if (project != null && (!project.exists() || !project.isOpen())) |
| // throw new ProjectUnavailableError(project); |
| // ValidationPlugin.getPlugin().handleException(e); |
| // } |
| // |
| // set.addAll(list); |
| // val = new Validator[set.size()]; |
| // set.toArray(val); |
| // if (project == null)_validators = val; |
| // return val; |
| // } |
| |
| /** |
| * This method needs to be called whenever the validation configuration has changed. |
| */ |
| private void configHasChanged(){ |
| _configNumber++; |
| _projectManager.reset(); |
| } |
| |
| /** |
| * Answer the global validation preferences. |
| */ |
| public synchronized GlobalPreferences getGlobalPreferences(){ |
| GlobalPreferences gp = _globalPreferences; |
| if (gp == null){ |
| ValPrefManagerGlobal vpm = ValPrefManagerGlobal.getDefault(); |
| gp = new GlobalPreferences(); |
| vpm.loadGlobalPreferences(gp); |
| _globalPreferences = gp; |
| } |
| return gp; |
| } |
| |
| public ProjectPreferences getProjectPreferences(IProject project) { |
| ProjectPreferences pp = getProjectPreferences2(project); |
| if (pp != null)return pp; |
| |
| /* hopefully we rarely get this far */ |
| |
| Map<String,Validator> extVals = ExtensionValidators.instance().getMapV2Copy(); |
| try { |
| List<Validator> vals = ValPrefManagerGlobal.getDefault().getValidators(); |
| for (Validator v : vals)extVals.put(v.getId(), v); |
| |
| pp = getProjectPreferences(project, extVals); |
| } |
| catch (BackingStoreException e){ |
| ValidationPlugin.getPlugin().handleException(e); |
| } |
| return pp; |
| } |
| |
| |
| private ProjectPreferences getProjectPreferences(IProject project, Map<String, Validator> baseValidators) |
| throws BackingStoreException { |
| if (_projectPreferences.containsKey(project)){ |
| return _projectPreferences.get(project); |
| } |
| |
| ValPrefManagerProject vpm = new ValPrefManagerProject(project); |
| ProjectPreferences pp = new ProjectPreferences(project); |
| vpm.loadProjectPreferences(pp, baseValidators); |
| _projectPreferences.put(project, pp); |
| return pp; |
| } |
| |
| /** |
| * Answer the project specific validation preferences from the cache |
| * |
| * @param project |
| * |
| * @return null if the project is not in the cache. |
| */ |
| private ProjectPreferences getProjectPreferences2(IProject project){ |
| if (_projectPreferences.containsKey(project)){ |
| return _projectPreferences.get(project); |
| } |
| return null; |
| } |
| |
| /** |
| * Restore all the validation defaults, as defined by the individual validators via the |
| * validation extension point. |
| */ |
| // public synchronized void restoreDefaults() { |
| // getGlobalPreferences().resetToDefault(); |
| // _validators = null; |
| // getValidators(true); |
| // } |
| |
| |
| /** |
| * Run all the validators that are applicable to this resource. |
| * <p> |
| * If this is a manual validation both the version 1 and version 2 validators are run. If it |
| * is a build validation, then only the version 2 validators are run, because the old framework handles |
| * the running of the old validators. |
| * </p> |
| * |
| * @param project project that is being validated |
| * |
| * @param resource the resource that is being validated |
| * |
| * @param kind the kind of resource delta. It will be one of the IResourceDelta constants, like |
| * IResourceDelta.CHANGED for example. |
| * |
| * @param valType The type of validation request. |
| * @param buildKind the kind of build that triggered this validation. See IncrementalProjectBuilder for values. |
| * @param operation the operation that this validation is running under |
| * @param monitor the monitor to use to report progress |
| */ |
| public void validate(IProject project, final IResource resource, final int kind, ValType valType, |
| int buildKind, ValOperation operation, final IProgressMonitor monitor) { |
| |
| MarkerManager.getDefault().deleteMarkers(resource, operation.getStarted(), IResource.DEPTH_ZERO); |
| |
| IValidatorVisitor visitor = new IValidatorVisitor(){ |
| |
| public void visit(Validator validator, IProject project, ValType vt, |
| ValOperation operation, IProgressMonitor monitor) { |
| |
| Validator.V1 v1 = validator.asV1Validator(); |
| if (vt == ValType.Build && v1 != null)return; |
| |
| SubMonitor subMonitor = SubMonitor.convert(monitor); |
| String task = NLS.bind(ValMessages.LogValStart, validator.getName(), resource.getName()); |
| subMonitor.beginTask(task, 1); |
| validate(validator, operation, resource, kind, subMonitor.newChild(1)); |
| } |
| }; |
| SubMonitor sm = SubMonitor.convert(monitor, getValidators(project).length); |
| accept(visitor, project, resource, valType, operation, sm); |
| |
| } |
| |
| /** |
| * Validate a single resource with a single validator. This will call the validator whether the validator |
| * is enabled or not. |
| * <p> |
| * Callers of this method should ensure that the shouldValidate was tested before making this call. |
| * |
| * @param validator the validator |
| * @param operation the operation that the validation is running in. |
| * @param resource the resource to validate |
| * @param kind the kind of resource change. See IResourceDelta. |
| * @param monitor |
| */ |
| public void validate(Validator validator, ValOperation operation, IResource resource, int kind, |
| IProgressMonitor monitor){ |
| if (operation.isValidated(validator.getId(), resource))return; |
| long time = 0; |
| long cpuTime = -1; |
| String msg1 = NLS.bind(ValMessages.LogValStart, validator.getName(), resource.getName()); |
| monitor.subTask(msg1); |
| IPerformanceMonitor pm = ValidationFramework.getDefault().getPerformanceMonitor(); |
| if (pm.isCollecting()){ |
| time = System.currentTimeMillis(); |
| cpuTime = Misc.getCPUTime(); |
| } |
| |
| if (Tracing.matchesExtraDetail(validator.getId())){ |
| Tracing.log("ValManager-03: validating ", resource); //$NON-NLS-1$ |
| } |
| ValidationResult vr = validator.validate(resource, kind, operation, monitor); |
| if (pm.isCollecting()){ |
| if (cpuTime != -1){ |
| cpuTime = Misc.getCPUTime() - cpuTime; |
| } |
| int num = 0; |
| if (vr != null)num = vr.getNumberOfValidatedResources(); |
| PerformanceCounters pc = new PerformanceCounters(validator.getId(), |
| validator.getName(), resource.getName(), |
| num, System.currentTimeMillis()-time, cpuTime); |
| pm.add(pc); |
| } |
| if (ValidationPlugin.getPlugin().isDebugging() && !pm.isCollecting()){ |
| String msg = time != 0 ? |
| NLS.bind(ValMessages.LogValEndTime, new Object[]{validator.getName(), |
| validator.getId(), resource, Misc.getTimeMS(System.currentTimeMillis()-time)}) : |
| NLS.bind(ValMessages.LogValEnd, validator.getName(), resource); |
| Tracing.log("ValManager-01: " + msg); //$NON-NLS-1$ |
| } |
| if (vr != null){ |
| operation.getResult().mergeResults(vr); |
| if (vr.getSuspendValidation() != null)operation.suspendValidation(vr.getSuspendValidation(), validator); |
| } |
| } |
| |
| /** |
| * Accept a visitor for all the validators that are enabled for the given project. |
| * |
| * @param visitor |
| * @param project |
| * @param valType the type of validation |
| * @param operation |
| * @param monitor |
| */ |
| public void accept(IValidatorVisitor visitor, IProject project, ValType valType, |
| ValOperation operation, IProgressMonitor monitor){ |
| |
| if (isDisabled(project))return; |
| |
| for (Validator val : getValidators(project)){ |
| if (monitor.isCanceled())return; |
| if (!_projectManager.shouldValidate(val, project, valType))continue; |
| if (operation.isSuspended(val, project))continue; |
| try { |
| visitor.visit(val, project, valType, operation, monitor); |
| } |
| catch (Exception e){ |
| ValidationPlugin.getPlugin().handleException(e); |
| } |
| } |
| } |
| |
| /** |
| * Accept a visitor for all the validators that are enabled for the given project, resource, |
| * and validation mode. |
| * |
| * @param valType the type of validation request |
| */ |
| public void accept(IValidatorVisitor visitor, IProject project, IResource resource, |
| ValType valType, ValOperation operation, IProgressMonitor monitor){ |
| |
| if (isDisabled(project))return; |
| |
| Map<String,IValidatorGroupListener[]> groupListeners = new HashMap<String,IValidatorGroupListener[]>(); |
| |
| ValProperty vp = getValProperty(resource, valType, _configNumber); |
| if (vp != null){ |
| BitSet bs = vp.getConfigSet(); |
| for (Validator val : getValidators(project)){ |
| if (!monitor.isCanceled()) { |
| if (!bs.get(_idManager.getIndex(val.getId())))continue; |
| if (operation.isSuspended(val, project))continue; |
| Validator.V2 v2 = val.asV2Validator(); |
| if (v2 != null) { |
| notifyGroupListenersStarting(resource, operation.getState(), monitor, groupListeners, v2); |
| } |
| try { |
| visitor.visit(val, project, valType, operation, monitor); |
| } |
| catch (Exception e){ |
| ValidationPlugin.getPlugin().handleException(e); |
| } |
| } |
| } |
| notifyGroupFinishing(resource, operation.getState(), monitor, groupListeners); |
| return; |
| } |
| |
| vp = new ValProperty(); |
| vp.setConfigNumber(_configNumber); |
| ContentTypeWrapper ctw = new ContentTypeWrapper(); |
| for (Validator val : getValidators(project)){ |
| if (!monitor.isCanceled()) { |
| if (!_projectManager.shouldValidate(val, project, valType))continue; |
| if (Friend.shouldValidate(val, resource, valType, ctw)){ |
| vp.getConfigSet().set(_idManager.getIndex(val.getId())); |
| // we do the suspend check after figuring out if it needs to be validated, because we save |
| // this information for the session. |
| if (operation.isSuspended(val, project))continue; |
| Validator.V2 v2 = val.asV2Validator(); |
| if (v2 != null) { |
| notifyGroupListenersStarting(resource, operation.getState(), monitor, groupListeners, v2); |
| } |
| try { |
| visitor.visit(val, project, valType, operation, monitor); |
| } |
| catch (Exception e){ |
| ValidationPlugin.getPlugin().handleException(e); |
| } |
| } |
| } |
| } |
| notifyGroupFinishing(resource, operation.getState(), monitor, groupListeners); |
| putValProperty(vp, resource, valType); |
| } |
| |
| /** |
| * Let the group listeners know that validation might be starting for the group of validators. |
| */ |
| private void notifyGroupListenersStarting(final IResource resource, |
| final ValidationState state, final IProgressMonitor monitor, |
| Map<String, IValidatorGroupListener[]> groupListeners, Validator.V2 v2) { |
| |
| String[] groups = v2.getValidatorGroups(); |
| for (String group : groups) { |
| if (!groupListeners.containsKey(group)) { |
| IValidatorGroupListener[] createdListeners = null; |
| try { |
| createdListeners = ValidatorGroupExtensionReader.getDefault().createListeners(group); |
| } |
| catch (CoreException e){ |
| String msg = NLS.bind(ValMessages.ErrConfig, v2.getId()); |
| Status status = new Status(IStatus.ERROR, ValidationPlugin.PLUGIN_ID, msg); |
| CoreException core = new CoreException(status); |
| ValidationPlugin.getPlugin().handleException(core); |
| ValidationPlugin.getPlugin().handleException(e); |
| |
| // we create this to ensure that we don't signal the same exception over and over. |
| createdListeners = new IValidatorGroupListener[0]; |
| } |
| |
| // create and notify just this once |
| final IValidatorGroupListener[] listeners = createdListeners; |
| |
| groupListeners.put(group, listeners); |
| for (final IValidatorGroupListener listener : listeners) { |
| SafeRunner.run(new ISafeRunnable() { |
| public void run() throws Exception { |
| listener.validationStarting(resource, monitor, state); |
| } |
| |
| public void handleException(Throwable exception) { |
| ValidationPlugin.getPlugin().handleException(exception); |
| } |
| }); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Let the group listeners know that validation is finished for the group of validators. |
| */ |
| private void notifyGroupFinishing(final IResource resource, |
| final ValidationState state, final IProgressMonitor monitor, |
| Map<String, IValidatorGroupListener[]> groupListeners) { |
| for (final IValidatorGroupListener[] listeners : groupListeners.values()) { |
| for (final IValidatorGroupListener listener : listeners) { |
| SafeRunner.run(new ISafeRunnable() { |
| public void run() throws Exception { |
| listener.validationFinishing(resource, monitor, state); |
| } |
| |
| public void handleException(Throwable exception) { |
| ValidationPlugin.getPlugin().handleException(exception); |
| } |
| }); |
| } |
| } |
| } |
| |
| private ValProperty getValProperty(IResource resource, ValType valType, int configNumber) { |
| ValProperty vp = null; |
| try { |
| if (valType == ValType.Build)vp = (ValProperty)resource.getSessionProperty(StatusBuild); |
| else if (valType == ValType.Manual)vp = (ValProperty)resource.getSessionProperty(StatusManual); |
| } |
| catch (CoreException e){ |
| // don't care about this one |
| } |
| if (vp == null)return null; |
| if (vp.getConfigNumber() != _configNumber)return null; |
| return vp; |
| } |
| |
| /** |
| * Let the validation manager know that a project has been changed. |
| * |
| * @param project The project that has been opened, created, or had it's description change. |
| */ |
| public void projectChanged(IProject project){ |
| _projectManager.change(project); |
| } |
| |
| /** |
| * Let the validation manager know that a project has been removed. |
| * |
| * @param project The project that has been closed or deleted. |
| * |
| */ |
| public void projectRemoved(IProject project){ |
| _projectManager.remove(project); |
| } |
| |
| private void putValProperty(ValProperty vp, IResource resource, ValType valType) { |
| try { |
| if (valType == ValType.Build)resource.setSessionProperty(StatusBuild, vp); |
| else if (valType == ValType.Manual)resource.setSessionProperty(StatusManual, vp); |
| } |
| catch (CoreException e){ |
| ValidationPlugin.getPlugin().handleException(e, IStatus.WARNING); |
| } |
| } |
| |
| /** |
| * Let each of the enabled validators know that a clean has been requested. |
| * |
| * @param project the project that is being cleaned, or null if the entire workspace is being cleaned. |
| * @param monitor |
| */ |
| void clean(final IProject project, final ValOperation operation, final IProgressMonitor monitor) { |
| IValidatorVisitor visitor = new IValidatorVisitor(){ |
| |
| public void visit(Validator validator, IProject project, ValType valType, |
| ValOperation operation, IProgressMonitor monitor) { |
| validator.clean(project, monitor); |
| } |
| |
| }; |
| accept(visitor, project, ValType.Build, operation, monitor); |
| } |
| |
| /** |
| * Let each of the enabled validators know that a clean has been requested. |
| * |
| * @param project the project that is being cleaned, or null if the entire workspace is being cleaned. |
| * @param monitor |
| */ |
| public void clean(IProject project, IProgressMonitor monitor){ |
| IValidatorVisitor visitor = new IValidatorVisitor(){ |
| |
| public void visit(Validator validator, IProject project, ValType valType, |
| ValOperation operation, IProgressMonitor monitor) { |
| validator.clean(project, monitor); |
| } |
| |
| }; |
| ValidationFramework.getDefault().getDependencyIndex().clear(project); |
| ValOperation operation = new ValOperation(); |
| accept(visitor, project, ValType.Build, operation, monitor); |
| } |
| |
| public void validatorsForProjectChanged(IProject project, boolean validationSettingChanged) { |
| if (validationSettingChanged){ |
| if (project != null)_projectPreferences.remove(project); |
| configHasChanged(); |
| } |
| } |
| |
| private class HasValidatorVisitor implements IResourceVisitor { |
| |
| private boolean _hasValidator; |
| private boolean _isManual; |
| private boolean _isBuild; |
| |
| public HasValidatorVisitor(boolean isManual, boolean isBuild){ |
| _isManual = isManual; |
| _isBuild = isBuild; |
| } |
| |
| public boolean hasValidator(IFolder folder){ |
| try { |
| folder.accept(this); |
| } |
| catch (CoreException e){ |
| ValidationPlugin.getPlugin().handleException(e); |
| } |
| return _hasValidator; |
| } |
| |
| public boolean visit(IResource resource) throws CoreException { |
| if (resource instanceof IFolder)return true; |
| if (hasValidators(resource, _isManual, _isBuild)){ |
| _hasValidator = true; |
| return false; |
| } |
| return true; |
| } |
| } |
| |
| /** |
| * Map validator id's to an index number on a bit set, so that we can quickly determine if a |
| * particular validator needs to validate a particular resource. |
| * @author karasiuk |
| * |
| */ |
| private static class ValidatorIdManager { |
| |
| /** |
| * Map validator id's to Integers. The integers correspond to bits in the ValProperty instances. |
| */ |
| private HashMap<String, Integer> _map = new HashMap<String, Integer>(100); |
| private HashMap<Integer, String> _reverseMap = new HashMap<Integer, String>(100); |
| |
| /** Next available bit. */ |
| private int _next; |
| |
| /** |
| * Answer the index number for this validator. If we haven't seen it yet allocate a new index number. |
| * @param id validator id. |
| * @return index into the validator bit mask. |
| */ |
| public int getIndex(String id){ |
| Integer i = _map.get(id); |
| if (i != null)return i; |
| |
| i = _next++; |
| _map.put(id, i); |
| _reverseMap.put(i, id); |
| |
| return i; |
| } |
| |
| /** |
| * Answer the validator id for the index. |
| * @param index |
| * @return null if the index number has not been set. |
| */ |
| public String getId(Integer index){ |
| return _reverseMap.get(index); |
| } |
| |
| public void reset(){ |
| _map.clear(); |
| _reverseMap.clear(); |
| _next = 0; |
| } |
| |
| /** |
| * Answer the ids for the bit in the bitset. This is used for debugging. |
| * @param bs |
| */ |
| public String[] getIds(BitSet bs){ |
| List<String> list = new LinkedList<String>(); |
| for(int i=bs.nextSetBit(0); i>=0; i=bs.nextSetBit(i+1)) { |
| String id = getId(i); |
| if (id != null)list.add(id); |
| } |
| String[] s = new String[list.size()]; |
| return list.toArray(s); |
| } |
| } |
| |
| /** |
| * This is used to keep track of which validators are enabled with which projects. We want to ensure |
| * that we don't activate a validator (and it's plug-in) if it has nothing to validate in the workspace. |
| * The ValManager keeps a single instance of this class. |
| * @author karasiuk |
| * |
| */ |
| private static class ValidatorProjectManager { |
| |
| private ValProjectMap _manual = new ValProjectMap(ValType.Manual); |
| private ValProjectMap _build = new ValProjectMap(ValType.Build); |
| |
| /** |
| * Should this validator attempt to validate any resources in this project? |
| * |
| * @param validator |
| * The validator that is being tested. |
| * @param project |
| * The project that is being tested. This can be null, which |
| * means that all projects will be tested. |
| * @param type |
| * The type of validation operation. |
| * @return true if the validator should attempt to validate. |
| */ |
| public synchronized boolean shouldValidate(Validator validator, IProject project, ValType type){ |
| if (type == ValType.Build)return _build.shouldValidate(validator, project); |
| if (type == ValType.Manual)return _manual.shouldValidate(validator, project); |
| |
| return false; |
| } |
| |
| /** |
| * A project has been created, opened, or had it's description changed. |
| * @param project |
| */ |
| public void change(IProject project) { |
| reset(); |
| } |
| |
| public void remove(IProject project) { |
| reset(); |
| } |
| |
| |
| public synchronized void reset(){ |
| _build.reset(); |
| _manual.reset(); |
| } |
| |
| /** |
| * This is used to keep track of which validators are enabled for which projects. We want to ensure |
| * that we don't activate a validator (and it's plug-in) if it has nothing to validate in the workspace. |
| * @author karasiuk |
| * |
| */ |
| private static class ValProjectMap { |
| /** |
| * Map a validator to the projects that it validates. |
| * <p> |
| * I've gone back and forth on whether the key should |
| * be a Validator or the validator id. I'm back to it being the id because I was |
| * running into cases where because of copying I wasn't getting the matches that I expected. If I run into |
| * false matches, it is probably because reset isn't being called when it should be. |
| */ |
| private Map<String, Set<IProject>> _map = new HashMap<String, Set<IProject>>(50); |
| |
| private ValType _type; |
| |
| /** Have we been initialized yet? */ |
| private boolean _initialized; |
| |
| public ValProjectMap(ValType type){ |
| _type = type; |
| } |
| |
| /** |
| * Should this validator attempt to validate any resources in this project? |
| * |
| * @param validator |
| * The validator that is being tested. |
| * @param project |
| * The project that is being tested. This can be null, which |
| * means that all projects will be tested, and if any of them return true, |
| * then true is answered for this method. |
| * |
| * @return true if the validator should attempt to validate. |
| */ |
| public synchronized boolean shouldValidate(Validator validator, IProject project){ |
| if (!_initialized)load(); |
| String vid = validator.getId(); |
| Set<IProject> projects = _map.get(vid); |
| if (projects == null)return false; |
| if (project == null)return projects.size() > 0; |
| return projects.contains(project); |
| } |
| |
| private void load() { |
| ValManager vm = ValManager.getDefault(); |
| IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects(); |
| Tracing.log("ValManager-02: loading " + projects.length + " projects"); //$NON-NLS-1$//$NON-NLS-2$ |
| for (IProject project : projects){ |
| if (!project.isOpen())continue; |
| Validator[] vals = vm.getValidators(project); |
| for (Validator v : vals){ |
| String vid = v.getId(); |
| Set<IProject> set = _map.get(vid); |
| if (set == null){ |
| set = new HashSet<IProject>(50); |
| _map.put(vid, set); |
| } |
| |
| if (v.shouldValidateProject(project, _type))set.add(project); |
| } |
| } |
| _initialized = true; |
| } |
| |
| public synchronized void reset(){ |
| _initialized = false; |
| _map.clear(); |
| } |
| } |
| |
| } |
| |
| public void handleEvent(IFacetedProjectEvent event) { |
| projectChanged(event.getProject().getProject()); |
| } |
| |
| public void projectChanged(IProject project, int type) { |
| switch (type){ |
| case IProjectChangeListener.ProjectClosed: |
| case IProjectChangeListener.ProjectDeleted: |
| projectRemoved(project); |
| break; |
| case IProjectChangeListener.ProjectOpened: |
| case IProjectChangeListener.ProjectChanged: |
| case IProjectChangeListener.ProjectAdded: |
| projectChanged(project); |
| break; |
| } |
| |
| } |
| } |