| /******************************************************************************* |
| * Copyright (c) 2007, 2009 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; |
| |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.IAdaptable; |
| import org.eclipse.core.runtime.IConfigurationElement; |
| import org.eclipse.core.runtime.IContributor; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.wst.validation.internal.ConfigurationConstants; |
| import org.eclipse.wst.validation.internal.ConfigurationManager; |
| import org.eclipse.wst.validation.internal.ContentTypeWrapper; |
| import org.eclipse.wst.validation.internal.ExtensionConstants; |
| import org.eclipse.wst.validation.internal.MarkerManager; |
| import org.eclipse.wst.validation.internal.Misc; |
| import org.eclipse.wst.validation.internal.NullValidator; |
| import org.eclipse.wst.validation.internal.SummaryReporter; |
| import org.eclipse.wst.validation.internal.Tracing; |
| import org.eclipse.wst.validation.internal.ValManager; |
| import org.eclipse.wst.validation.internal.ValMessages; |
| import org.eclipse.wst.validation.internal.ValOperation; |
| import org.eclipse.wst.validation.internal.ValPrefManagerGlobal; |
| import org.eclipse.wst.validation.internal.ValPrefManagerProject; |
| import org.eclipse.wst.validation.internal.ValType; |
| import org.eclipse.wst.validation.internal.ValidationConfiguration; |
| import org.eclipse.wst.validation.internal.ValidatorExtensionReader; |
| import org.eclipse.wst.validation.internal.ValidatorMetaData; |
| import org.eclipse.wst.validation.internal.core.ValidatorLauncher; |
| import org.eclipse.wst.validation.internal.delegates.ValidatorDelegateDescriptor; |
| import org.eclipse.wst.validation.internal.delegates.ValidatorDelegatesRegistry; |
| import org.eclipse.wst.validation.internal.model.FilterGroup; |
| import org.eclipse.wst.validation.internal.operations.IWorkbenchContext; |
| import org.eclipse.wst.validation.internal.operations.WorkbenchContext; |
| import org.eclipse.wst.validation.internal.plugin.ValidationPlugin; |
| import org.eclipse.wst.validation.internal.provisional.core.IMessage; |
| import org.eclipse.wst.validation.internal.provisional.core.IValidator; |
| import org.eclipse.wst.validation.internal.provisional.core.IValidatorExtender; |
| |
| /** |
| * Represents a validator. This gets instantiated through one of the validator extension points. |
| * <p> |
| * <b>This class is not API</b>. |
| * </p> |
| * |
| * @author karasiuk |
| * |
| */ |
| public abstract class Validator implements Comparable<Validator> { |
| // Remember if you add a new instance variable, make sure that you update the copy and become methods |
| |
| /** |
| * The level of configuration for the validator. |
| * <ul> |
| * <li>Extension - Defined by an extension point.</li> |
| * <li>Global - Defined by a global preference.</li> |
| * <li>Project - Defined by a project property.</li> |
| * </ul> |
| */ |
| public enum Level {Extension, Global, Project}; |
| |
| protected boolean _buildValidation = true; |
| |
| /** If this is a delegating validator, then this field holds the validator that will be delegated to. */ |
| private String _delegatingId; |
| |
| /** |
| * If this validator is also used to control an ISource validator, the id of the ISource validator is |
| * registered here. |
| */ |
| private String _sourceId; |
| |
| protected boolean _manualValidation = true; |
| |
| /** An optional customized marker id for this validator. */ |
| private String _markerId; |
| |
| /** |
| * Version of the filter definition. By increasing this number the framework can know that a plug-in has |
| * changed it's filters. |
| */ |
| private int _version = 1; |
| |
| /** Map simple message id's to message settings. */ |
| private Map<String, MessageSeveritySetting> _messageSettings; |
| |
| /** The project that you are defined in. This can be null which means that you are a global validator. */ |
| protected IProject _project; |
| |
| /** How many times has a global field in this validator been changed since it was created (or copied)? */ |
| protected transient int _changeCountGlobal; |
| |
| /** How many times has a message field in this validator been changed since it was created (or copied)? */ |
| protected transient int _changeCountMessages; |
| |
| /** Has the validator been migrated from an earlier version in this session, but not yet saved? */ |
| private boolean _migrated; |
| |
| public Validator(IProject project){ |
| _project = project; |
| } |
| |
| void setMigrated(boolean migrated){ |
| _migrated = migrated; |
| } |
| |
| /** |
| * Create a new validator based on a abstract validator. |
| * |
| * @param validator |
| * The validator that is being wrapped. |
| * |
| * @param project |
| * The project that you are defined in. This can be null which |
| * means that you are a global validator. |
| */ |
| public static Validator create(IConfigurationElement validator, IProject project) { |
| V2 v2 = new V2(validator, project); |
| return v2; |
| } |
| |
| /** |
| * Create a new validator based on validator meta data. |
| * |
| * @param project |
| * The project that you are defined in. This can be null which |
| * means that you are a global validator. |
| */ |
| public static Validator create(ValidatorMetaData vmd, ValidationConfiguration config, IProject project){ |
| V1 v1 = new V1(vmd, config, project); |
| return v1; |
| } |
| |
| /** |
| * If you are a version 1 validator, answer yourself as one, otherwise answer null. |
| */ |
| public V1 asV1Validator(){ |
| return null; |
| } |
| |
| /** |
| * If you are a version 2 validator, answer yourself as one, otherwise answer null. |
| */ |
| public V2 asV2Validator() { |
| return null; |
| } |
| |
| /** |
| * The project is being cleaned, this method gives the validator a chance to do any special cleanup. |
| * The default is to do nothing. |
| * |
| * @param project the project being built. |
| * @param monitor the monitor that should be used for reporting progress if the clean takes a long time. |
| */ |
| public void clean(IProject project, ValOperation operation, IProgressMonitor monitor){ |
| } |
| |
| /** |
| * Compare yourself based on Validator name. |
| */ |
| public int compareTo(Validator validator) { |
| return getName().compareTo(validator.getName()); |
| } |
| |
| /** Answer a deep copy of yourself. */ |
| public Validator copy(){ |
| return copy(false); |
| } |
| |
| public abstract Validator copy(boolean includeChangeCounts); |
| |
| /** |
| * Update your direct, non transient fields from the fields in v. |
| */ |
| protected void copyLocal(Validator v, boolean includeChangeCounts){ |
| _buildValidation = v._buildValidation; |
| _delegatingId = v._delegatingId; |
| _manualValidation = v._manualValidation; |
| _markerId = v._markerId; |
| _messageSettings = v._messageSettings; |
| _sourceId = v._sourceId; |
| _version = v._version; |
| _migrated = v._migrated; |
| |
| if (includeChangeCounts){ |
| _changeCountGlobal = v._changeCountGlobal; |
| _changeCountMessages = v._changeCountMessages; |
| } |
| } |
| |
| /** |
| * Should the validation framework first clear the markers that this |
| * validator has placed on this resource? This method can be overridden by |
| * validator implementors to provide a validator specific behavior. |
| * |
| * @param event |
| * The validation event that triggered the validation. |
| * @return true if the validation framework should first clear all the |
| * markers that this validator produced. This is the default |
| * behavior. Return false to leave the markers unchanged. It then |
| * becomes the responsibility of the validator to manage it's own |
| * markers for this resource, for this validation event. |
| */ |
| public boolean shouldClearMarkers(ValidationEvent event){ |
| return true; |
| } |
| |
| /** |
| * Answer true if this validator, based on it's filters, should validate |
| * this resource. This method does not check to see if global validation or |
| * project validation has been suspended or not. |
| * |
| * @param resource |
| * The resource to be checked. |
| * @param isManual |
| * If true then this validator must also be enabled for manual |
| * validation. |
| * @param isBuild |
| * If true then this validator must also be enabled for builder |
| * based validation. |
| * |
| * @return true if the resource should be validated. |
| */ |
| public boolean shouldValidate(IResource resource, boolean isManual, boolean isBuild){ |
| return shouldValidate(resource, isManual, isBuild, new ContentTypeWrapper()); |
| } |
| |
| /** |
| * Answer true if this validator, based on it's filters, should validate |
| * this resource. This method does not check to see if global validation or |
| * project validation has been suspended or not. |
| * |
| * @param resource |
| * The resource to be checked. |
| * @param isManual |
| * If true then this validator must also be enabled for manual |
| * validation. |
| * @param isBuild |
| * If true then this validator must also be enabled for builder |
| * based validation. |
| * @param contentTypeWrapper |
| * For repeated calls on the same resource, it is more efficient |
| * to remember the content type. |
| * @return true if the resource should be validated. |
| * @see Friend#shouldValidate(Validator, IResource, boolean, boolean, ContentTypeWrapper) |
| */ |
| boolean shouldValidate(IResource resource, boolean isManual, boolean isBuild, |
| ContentTypeWrapper contentTypeWrapper){ |
| |
| if (isManual && !_manualValidation)return false; |
| if (isBuild && !_buildValidation)return false; |
| |
| return shouldValidate(resource, contentTypeWrapper); |
| } |
| |
| /** |
| * Answer true if this validator, based on it's filters, should validate |
| * this resource. This method does not check to see if global validation or |
| * project validation has been suspended or not. |
| * |
| * @param resource |
| * The resource to be checked. |
| * @param valType |
| * The context to use when performing the check. |
| * |
| * @return true if the resource should be validated. |
| */ |
| public boolean shouldValidate(IResource resource, ValType valType){ |
| return shouldValidate(resource, valType, new ContentTypeWrapper()); |
| } |
| |
| /** |
| * Answer true if this validator, based on it's filters, should validate |
| * this resource. This method does not check to see if global validation or |
| * project validation has been suspended or not. |
| * |
| * @param resource |
| * The resource to be checked. |
| * @param valType |
| * The context to use when performing the check. |
| * @param contentTypeWrapper |
| * For repeated calls on the same resource, it is more efficient |
| * to remember the content type. |
| * |
| * @return true if the resource should be validated. |
| * |
| * @see Friend#shouldValidate(Validator, IResource, ValType, ContentTypeWrapper) |
| */ |
| boolean shouldValidate(IResource resource, ValType valType, ContentTypeWrapper contentTypeWrapper){ |
| if (Tracing.matchesExtraDetail(getId())){ |
| Tracing.log("Validator-01: checking if " + getId() + " should validate " + resource); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| if (valType == ValType.Manual && !_manualValidation)return false; |
| if (valType == ValType.Build && !_buildValidation)return false; |
| |
| boolean result = shouldValidate(resource, contentTypeWrapper); |
| if (Tracing.matchesExtraDetail(getId())){ |
| Tracing.log("Validator-02: result = " + result); //$NON-NLS-1$ |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Answer true if this validator, based on it's filters, should validate this project. This method |
| * does not check to see if global validation or project validation has been suspended or not. |
| * |
| * @param project the project to be checked |
| * @param type The type of validation request |
| * |
| * @return true if the project should be validated. |
| */ |
| public boolean shouldValidateProject(IProject project, ValType type){ |
| return shouldValidateProject(project, type == ValType.Manual, type == ValType.Build); |
| } |
| |
| /** |
| * Answer true if this validator, based on it's filters, should validate this project. This method |
| * does not check to see if global validation or project validation has been suspended or not. |
| * |
| * @param project the project to be checked |
| * @param isManual if true then this validator must also be enabled for manual validation. |
| * @param isBuild if true then this validator must also be enabled for builder based validation. |
| * |
| * @return true if the project should be validated. |
| */ |
| public boolean shouldValidateProject(IProject project, boolean isManual, boolean isBuild){ |
| if (Tracing.matchesExtraDetail(getId())){ |
| Tracing.log("Validator-03: checking if " + getId() + " should validate " + project); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| if (isManual && !_manualValidation)return false; |
| if (isBuild && !_buildValidation)return false; |
| if (project == null || !project.isOpen())return false; |
| boolean result = shouldValidateProject(project); |
| if (Tracing.matchesExtraDetail(getId())){ |
| Tracing.log("Validator-04: result " + result); //$NON-NLS-1$ |
| } |
| return result; |
| } |
| |
| /** |
| * Validate the resource. |
| * |
| * @param resource |
| * The resource to be validated. |
| * @param kind |
| * The kind of resource change, see IResourceDelta for values. |
| * @param operation |
| * The operation that this validation is running under. This can |
| * be null. |
| * @param monitor |
| * A way to report progress. This can be null. |
| * |
| * @return the result of doing the validation, it can be, but usually isn't |
| * null. |
| */ |
| public abstract ValidationResult validate(IResource resource, int kind, ValOperation operation, IProgressMonitor monitor); |
| |
| /** |
| * Validate the resource. |
| * |
| * @param resource |
| * The resource to be validated. |
| * @param kind |
| * The kind of resource change, see IResourceDelta for values. |
| * @param operation |
| * The operation that this validation is running under. This can |
| * be null. |
| * @param monitor |
| * A way to report progress. This can be null. |
| * @param event |
| * An event that describes in more detail what should be |
| * validated and why it should be validated. This can be null. |
| * |
| * @return the result of doing the validation, it can be, but usually isn't |
| * null. |
| */ |
| public ValidationResult validate(IResource resource, int kind, ValOperation operation, IProgressMonitor monitor, ValidationEvent event){ |
| // The reason that the resource and kind are still specified, is that I didn't want to remove a public method in the service |
| // stream. |
| return validate(resource, kind, operation, monitor); |
| } |
| |
| /** |
| * This method will be called before any validation takes place. It allows validators to perform any |
| * initialization that they might need. |
| * |
| * @param project the project that is being validated. For the very first call in the validation phase, |
| * this will be null. That is the signal to the validator that a top level validation is starting. |
| * Subsequently, the project will be set, as each of the individual projects are validated. |
| * |
| * @param state a way to pass arbitrary, validator specific, data from one invocation of a validator to |
| * the next, during the validation phase. |
| * |
| * @param monitor the monitor that should be used for reporting progress if the clean takes a long time. |
| */ |
| public void validationStarting(IProject project, ValidationState state, IProgressMonitor monitor){ |
| // subclasses need to override this, if they wish to let their validators know about this event |
| } |
| |
| /** |
| * This method will be called when validation is complete. It allows validators to perform any |
| * cleanup that they might need to do. |
| * |
| * @param project the project that was validated. The very last call in the validation will set this to |
| * null so that the validator knows that all the projects have now been validated. |
| * |
| * @param state a way to pass arbitrary, validator specific, data from one invocation of a validator to |
| * the next, during the validation phase. |
| * |
| * @param monitor the monitor that should be used for reporting progress if the clean takes a long time. |
| */ |
| public void validationFinishing(IProject project, ValidationState state, IProgressMonitor monitor){ |
| // subclasses need to override this, if they wish to let their validators know about this event |
| } |
| |
| void add(MessageSeveritySetting message){ |
| // I can't use getMessageSettings() here, as that will put us into an infinite loop |
| if (_messageSettings == null){ |
| _messageSettings = new HashMap<String, MessageSeveritySetting>(10); |
| } |
| _messageSettings.put(message.getId(), message); |
| bumpChangeCountMessages(); |
| } |
| |
| public IValidator asIValidator(){ |
| return null; |
| } |
| |
| protected abstract boolean shouldValidate(IResource resource, ContentTypeWrapper contentTypeWrapper); |
| protected abstract boolean shouldValidateProject(IProject project); |
| |
| public abstract String getId(); |
| |
| public MessageSeveritySetting getMessage(String id){ |
| return getMessageSettings().get(id); |
| } |
| |
| /** |
| * Answer all the message settings that this validator has defined. |
| * |
| * @return an empty map if the validator did not define any message settings. |
| */ |
| public Map<String, MessageSeveritySetting> getMessageSettings(){ |
| Map<String, MessageSeveritySetting> settings = _messageSettings; |
| if (settings == null){ |
| settings = new HashMap<String, MessageSeveritySetting>(10); |
| init(settings); |
| if (ValManager.getDefault().mustUseGlobalValidators(getProject())){ |
| ValPrefManagerGlobal gp = ValPrefManagerGlobal.getDefault(); |
| gp.loadMessages(this, settings); |
| } |
| else { |
| ValPrefManagerProject vp = new ValPrefManagerProject(getProject()); |
| vp.loadMessages(this, settings); |
| } |
| _messageSettings = settings; |
| } |
| return settings; |
| } |
| |
| /** |
| * Answer a hash code for the configurable fields so that we can quickly determine if two |
| * validators are the same. |
| */ |
| public int hashCodeForConfig(){ |
| int h = 0; |
| if (_buildValidation)h += 101; |
| if (_delegatingId != null)h += _delegatingId.hashCode(); |
| if (_manualValidation)h += 201; |
| if (_messageSettings != null){ |
| for (MessageSeveritySetting ms : _messageSettings.values())h += ms.hashCode(); |
| } |
| if (_sourceId != null)h += _sourceId.hashCode(); |
| h += _version; |
| return h; |
| } |
| |
| private void init(Map<String, MessageSeveritySetting> settings) { |
| for (MessageSeveritySetting ms : ValidatorExtensionReader.getDefault().addMessages(this)){ |
| settings.put(ms.getId(), ms); |
| } |
| } |
| |
| public abstract String getName(); |
| |
| /** |
| * Answer the project that you were enabled on. |
| * |
| * @return null if you are a global (i.e. workspace level) validator. |
| */ |
| public IProject getProject(){ |
| return _project; |
| } |
| |
| /** |
| * Answer the name of the class that implements the validator. |
| */ |
| public abstract String getValidatorClassname(); |
| |
| /** |
| * Is this validator currently enabled for validations that are triggered manually? |
| */ |
| public boolean isManualValidation() { |
| return _manualValidation; |
| } |
| |
| /** |
| * Set whether this validator should be triggered as part of a manual validation. |
| * |
| * @param manualValidation |
| * @return true if the setting changed. |
| */ |
| public boolean setManualValidation(boolean manualValidation) { |
| return setManualValidation2(manualValidation); |
| } |
| |
| protected final boolean setManualValidation2(boolean manualValidation) { |
| boolean changed = false; |
| if (_manualValidation != manualValidation){ |
| bumpChangeCountGlobal(); |
| changed = true; |
| _manualValidation = manualValidation; |
| } |
| return changed; |
| } |
| |
| /** |
| * Is this validator currently enabled for validations that are triggered by builds? |
| */ |
| public boolean isBuildValidation() { |
| return _buildValidation; |
| } |
| |
| /** |
| * Has the validator changed since it was last created or copied? Or was it migrated from an earlier version. |
| */ |
| public boolean isChanged(){ |
| if (_changeCountGlobal > 0 || _changeCountMessages > 0 || _migrated)return true; |
| return false; |
| } |
| |
| /** |
| * Has the validator's implementation been loaded yet? This is used by some test cases to ensure that |
| * plug-ins are not loaded too early. |
| */ |
| abstract boolean isLoaded(); |
| |
| |
| /** |
| * Set whether this validator should be triggered by the build process. |
| * |
| * @param buildValidation |
| * @return true if the setting changed. |
| */ |
| public boolean setBuildValidation(boolean buildValidation) { |
| return setBuildValidation2(buildValidation); |
| } |
| |
| protected final boolean setBuildValidation2(boolean buildValidation) { |
| boolean changed = false; |
| if (_buildValidation != buildValidation){ |
| bumpChangeCountGlobal(); |
| changed = true; |
| _buildValidation = buildValidation; |
| } |
| return changed; |
| } |
| |
| /** |
| * Get the id of the "real" validator, that is the validator that will be called when this delegating |
| * validator is asked to validate something. If this isn't a delegating validator answer null. |
| */ |
| public String getDelegatingId() { |
| return _delegatingId; |
| } |
| |
| /** |
| * Set the id of the "real" validator, that is the validator that will be called when this delegating |
| * validator is asked to validate something. |
| * |
| * @param delegating the id of the validator that is actually going to perform the validation. |
| */ |
| public void setDelegatingId(String delegating) { |
| if (!Misc.same(_delegatingId, delegating)){ |
| _delegatingId = delegating; |
| bumpChangeCountGlobal(); |
| } |
| } |
| |
| public int getVersion() { |
| return _version; |
| } |
| |
| public void setVersion(int version) { |
| if (_version != version){ |
| _version = version; |
| bumpChangeCountGlobal(); |
| } |
| } |
| |
| @Override |
| public String toString() { |
| return getName(); |
| } |
| |
| /** |
| * A validator that uses version 1 of the validation framework. |
| * @author karasiuk |
| * |
| */ |
| public static class V1 extends Validator { |
| private ValidatorMetaData _vmd; |
| |
| /** |
| * Create a new version 1 validator. |
| * @param vmd |
| * @param config this is used to set the global enablement options. In some case this can be null. |
| */ |
| public V1(ValidatorMetaData vmd, ValidationConfiguration config, IProject project){ |
| super(project); |
| _vmd = vmd; |
| if (config != null){ |
| setBuildValidation(config.isBuildEnabled(vmd)); |
| setManualValidation(config.isManualEnabled(vmd)); |
| } |
| setDelegatingId(ValidatorDelegatesRegistry.getInstance().getDefaultDelegate(_vmd.getValidatorUniqueName())); |
| if (_vmd.getMarkerIds() != null && _vmd.getMarkerIds().length > 0)setMarkerId(_vmd.getMarkerIds()[0]); |
| resetChangeCounters(); |
| } |
| |
| @Override |
| public void validationFinishing(IProject project, ValidationState state, IProgressMonitor monitor) { |
| if (project != null) { |
| IValidator v = asIValidator(); |
| if (v instanceof IValidatorExtender) { |
| IValidatorExtender vExt = (IValidatorExtender) v; |
| vExt.validationFinishing(project, state, monitor); |
| } |
| } |
| } |
| |
| @Override |
| public IValidator asIValidator() { |
| IValidator v = null; |
| try { |
| v = _vmd.getValidator(); |
| } |
| catch (InstantiationException e){ |
| ValidationPlugin.getPlugin().handleException(e); |
| return null; |
| } |
| return v; |
| } |
| |
| public V1 asV1Validator() { |
| return this; |
| } |
| |
| public Validator copy(boolean includeChangeCounts) { |
| V1 v = new V1Copy(_vmd, null, _project); |
| v.copyLocal(this, includeChangeCounts); |
| |
| return v; |
| } |
| |
| public String getName() { |
| return _vmd.getValidatorDisplayName(); |
| } |
| |
| public ValidatorMetaData getVmd(){ |
| return _vmd; |
| } |
| |
| public String getValidatorClassname(){ |
| String name = ""; //$NON-NLS-1$ |
| try { |
| name = _vmd.getValidator().getClass().getName(); |
| } |
| catch (Exception e){ |
| // eat it |
| } |
| return name; |
| } |
| public String getId() { |
| return _vmd.getValidatorUniqueName(); |
| } |
| |
| @Override |
| boolean isLoaded() { |
| return _vmd.isActive(); |
| } |
| |
| @Override |
| public boolean setBuildValidation(boolean buildValidation) { |
| boolean changed = super.setBuildValidation(buildValidation); |
| _vmd.setBuildValidation(buildValidation); |
| return changed; |
| } |
| |
| @Override |
| public boolean setManualValidation(boolean manualValidation) { |
| boolean changed = super.setManualValidation(manualValidation); |
| _vmd.setManualValidation(manualValidation); |
| return changed; |
| } |
| |
| @Override |
| protected boolean shouldValidate(IResource resource, ContentTypeWrapper contentTypeWrapper) { |
| return _vmd.isApplicableTo(resource); |
| } |
| |
| @Override |
| protected boolean shouldValidateProject(IProject project) { |
| // TODO determine if this can be optimized |
| return true; |
| } |
| |
| @Override |
| public ValidationResult validate(IResource resource, int kind, ValOperation operation, |
| IProgressMonitor monitor) { |
| |
| if (monitor == null)monitor = new NullProgressMonitor(); |
| |
| ValidationResult vr = new ValidationResult(); |
| IValidator v = asIValidator(); |
| if (v == null)return null; |
| |
| try { |
| IProject project = resource.getProject(); |
| SummaryReporter reporter = new SummaryReporter(project, monitor); |
| IWorkbenchContext helper = _vmd.getHelper(project); |
| if (helper instanceof WorkbenchContext){ |
| WorkbenchContext wc = (WorkbenchContext)helper; |
| List<String> files = new LinkedList<String>(); |
| // [213631] The JSP validator expects full paths not relative paths, but the XML validator |
| // expects relative paths. |
| files.add(wc.getPortableName(resource)); |
| wc.setValidationFileURIs(files); |
| } |
| try { |
| ValidatorLauncher.getLauncher().start(helper, v, reporter); |
| } |
| finally { |
| try { |
| v.cleanup(reporter); |
| } |
| finally { |
| helper.cleanup(reporter); |
| } |
| } |
| |
| vr.incrementError(reporter.getSeverityHigh()); |
| vr.incrementWarning(reporter.getSeverityNormal()); |
| vr.incrementInfo(reporter.getSeverityLow()); |
| |
| } |
| catch (Exception e){ |
| ValidationPlugin.getPlugin().handleException(e); |
| } |
| return vr; |
| } |
| |
| /* |
| * GRK - Because I didn't want to try to make a true copy of the V1 validator, (because I didn't |
| * want to copy the vmd object), I came up with this approach to only copy the fields that |
| * the preference page was worried about. |
| */ |
| public final static class V1Copy extends V1 { |
| public V1Copy(ValidatorMetaData vmd, ValidationConfiguration vc, IProject project){ |
| super(vmd, vc, project); |
| } |
| |
| @Override |
| public boolean setManualValidation(boolean bool) { |
| return setManualValidation2(bool); |
| } |
| |
| @Override |
| public boolean setBuildValidation(boolean bool) { |
| return setBuildValidation2(bool); |
| } |
| |
| } |
| |
| } |
| |
| /** |
| * A validator that uses version 2 of the validation framework. |
| * @author karasiuk |
| * |
| */ |
| public final static class V2 extends Validator implements IAdaptable { |
| private AbstractValidator _validator; |
| |
| private List<FilterGroup> _groups = new LinkedList<FilterGroup>(); |
| private FilterGroup[] _groupsArray; |
| |
| /** The full id of the extension. */ |
| private String _id; |
| |
| /** Name of the validator. */ |
| private String _name; |
| |
| /** |
| * We don't want to create the validator too early, as it may trigger new plug-ins to be loaded. |
| * We delay that as long as possible, by starting with just the config element. |
| */ |
| private IConfigurationElement _validatorConfigElement; |
| |
| private String _validatorClassName; |
| |
| /** |
| * An array containing the validator group IDs for which this validator is a member. |
| */ |
| private String[] _validatorGroupIds; |
| |
| /** |
| * If this validator is a delegating validator, then this is the "real" validator (i.e. the one that |
| * does the work). |
| */ |
| private AbstractValidator _delegated; |
| |
| /** How many times has a group field in this validator been changed since it was created (or copied)? */ |
| protected transient int _changeCountGroups; |
| |
| private Level _level; |
| |
| /** |
| * Do we still need to invoke the validateStarting method for this validator, for the null project? |
| * |
| * Because we do not want to activate a validator's plug-in too soon, we do not activate the validator |
| * as a reaction to the global validation starting event. Instead we mark it pending, and wait until |
| * we are sure that we have something to validate. |
| * |
| * If this flag is true, it means that the validateStarting method still needs to be called for this validator. |
| */ |
| private AtomicBoolean _pendingValidationStarted = new AtomicBoolean(); |
| |
| V2(IConfigurationElement configElement, IProject project){ |
| super(project); |
| assert configElement != null; |
| _validatorConfigElement = configElement; |
| _validatorClassName = configElement.getAttribute(ExtensionConstants.AttribClass); |
| |
| IConfigurationElement[] groupReferenceElements = configElement.getChildren(ExtensionConstants.Group.elementGroup); |
| List<String> validatorGroupIDs = new ArrayList<String>(); |
| for (IConfigurationElement groupElement : groupReferenceElements) { |
| String id = groupElement.getAttribute(ExtensionConstants.Group.attId); |
| if (id != null)validatorGroupIDs.add(id); |
| } |
| _validatorGroupIds = validatorGroupIDs.toArray(new String[validatorGroupIDs.size()]); |
| |
| init(); |
| } |
| |
| private V2(IProject project, String validatorClassName, AbstractValidator validator){ |
| super(project); |
| assert validator != null; |
| |
| _validatorClassName = validatorClassName; |
| _validator = validator; |
| init(); |
| } |
| |
| private void init(){ |
| try { |
| String id = ConfigurationManager.getManager().getConfiguration(_project).getDelegateForTarget(_validatorClassName); |
| if (id == null) id = ValidatorDelegatesRegistry.getInstance().getDefaultDelegate(_validatorClassName); |
| setDelegatingId(id); |
| } |
| catch (InvocationTargetException e){ |
| ValidationPlugin.getPlugin().handleException(e); |
| } |
| resetChangeCounters(); |
| } |
| |
| public synchronized void add(FilterGroup fg) { |
| assert fg != null; |
| _groupsArray = null; |
| _groups.add(fg); |
| bumpChangeCountGroups(); |
| } |
| |
| @Override |
| public IValidator asIValidator() { |
| AbstractValidator av = getDelegatedValidator(); |
| if (av instanceof IValidator)return (IValidator)av; |
| return super.asIValidator(); |
| } |
| |
| @Override |
| public V2 asV2Validator() { |
| return this; |
| } |
| |
| /** |
| * Let the validator know that a clean is about to happen. |
| * |
| * @param project the project that is being cleaned. This can be null which means that the workspace |
| * is being cleaned (in which case a separate call will be made for each open project). |
| * |
| */ |
| @Override |
| public void clean(IProject project, ValOperation operation, IProgressMonitor monitor) { |
| getDelegatedValidator().clean(project, operation.getState(), monitor); |
| } |
| |
| @Override |
| public Validator copy(boolean includeChangeCounts) { |
| V2 v = null; |
| if (_validatorConfigElement != null)v = new V2(_validatorConfigElement, _project); |
| else v = new V2(_project, _validatorClassName, _validator); |
| v.copyLocal(this, includeChangeCounts); |
| |
| if (includeChangeCounts)v._changeCountGroups = _changeCountGroups; |
| |
| FilterGroup[] groups = getGroups(); |
| v._groupsArray = new FilterGroup[groups.length]; |
| for (int i=0; i<groups.length; i++){ |
| v._groupsArray[i] = groups[i]; |
| v._groups.add(groups[i]); |
| } |
| |
| v._id = _id; |
| v._name = _name; |
| v._validatorGroupIds = _validatorGroupIds; |
| v._pendingValidationStarted = _pendingValidationStarted; |
| |
| return v; |
| } |
| |
| public int getChangeCountGroups(){ |
| return _changeCountGroups; |
| } |
| |
| public void bumpChangeCountGroups(){ |
| _changeCountGroups++; |
| } |
| |
| public Level getLevel() { |
| return _level; |
| } |
| |
| public void setLevel(Level level) { |
| assert _level == null; |
| _level = level; |
| } |
| |
| /** |
| * Answer the actual validator that is going to perform the validation. If this is a normal validator this |
| * method will simply answer itself. However if this is a delegating validator, then this will answer the |
| * "real" validator. |
| */ |
| public AbstractValidator getDelegatedValidator(){ |
| AbstractValidator delegated = _delegated; |
| if (delegated != null)return delegated; |
| else if (getDelegatingId() == null)return getValidator(); |
| try { |
| ValidatorDelegateDescriptor vdd = ValidatorDelegatesRegistry.getInstance() |
| .getDescriptor(getValidatorClassname(), getDelegatingId()); |
| if (vdd == null)return getValidator(); |
| delegated = vdd.getValidator2(); |
| } |
| catch (Exception e){ |
| ValidationPlugin.getPlugin().handleException(e); |
| delegated = new NullValidator(); |
| } |
| delegated.setParent(this); |
| _delegated = delegated; |
| return delegated; |
| } |
| |
| @Override |
| public String getId() { |
| return _id; |
| } |
| |
| /** |
| * Answer the validator's filter groups. |
| * @return an empty array if the validator does not have any filter groups. |
| */ |
| public synchronized FilterGroup[] getGroups(){ |
| FilterGroup[] groups = _groupsArray; |
| if (groups == null){ |
| groups = new FilterGroup[_groups.size()]; |
| _groups.toArray(groups); |
| _groupsArray = groups; |
| } |
| return groups; |
| } |
| |
| @Override |
| public String getName() { |
| return _name; |
| } |
| |
| public AbstractValidator getValidator() { |
| if (_validator == null){ |
| try { |
| _validator = (AbstractValidator)_validatorConfigElement.createExecutableExtension(ExtensionConstants.AttribClass); |
| } |
| catch (Exception e){ |
| ValidationPlugin.getPlugin().handleException(e); |
| IContributor contrib = _validatorConfigElement.getContributor(); |
| String message = NLS.bind(ValMessages.ErrConfig, contrib.getName()); |
| ValidationPlugin.getPlugin().logMessage(IStatus.ERROR, message); |
| _validator = new NullValidator(); |
| } |
| _validator.setParent(this); |
| _validatorConfigElement = null; |
| |
| } |
| return _validator; |
| } |
| |
| @Override |
| public String getValidatorClassname(){ |
| return _validatorClassName; |
| } |
| |
| public String[] getValidatorGroups(){ |
| return _validatorGroupIds; |
| } |
| |
| @Override |
| public int hashCodeForConfig() { |
| int h = super.hashCodeForConfig(); |
| if (_id != null)h += _id.hashCode(); |
| if (_groups != null){ |
| for (FilterGroup fg : _groups)h += fg.hashCodeForConfig(); |
| } |
| return h; |
| } |
| |
| @Override |
| public boolean isChanged() { |
| if (_changeCountGroups > 0)return true; |
| return super.isChanged(); |
| } |
| |
| @Override |
| boolean isLoaded() { |
| return _validator != null; |
| } |
| |
| @Override |
| public boolean shouldClearMarkers(ValidationEvent event) { |
| return getValidator().shouldClearMarkers(event); |
| } |
| |
| /** |
| * Answer true if this validator, based on it's filters, should validate this resource. |
| * |
| * @return true if the resource should be validated. |
| */ |
| @Override |
| protected boolean shouldValidate(IResource resource, ContentTypeWrapper contentTypeWrapper) { |
| if (resource.isDerived()){ |
| return false; |
| } |
| FilterGroup[] groups = getGroups(); |
| IProject project = resource.getProject(); |
| for (FilterGroup group : groups){ |
| if (!group.shouldValidate(project, resource, contentTypeWrapper))return false; |
| } |
| return true; |
| } |
| |
| |
| @Override |
| public void setDelegatingId(String delegating) { |
| super.setDelegatingId(delegating); |
| _delegated = null; |
| } |
| |
| public synchronized void setGroups(List<FilterGroup> groups){ |
| _groups = groups; |
| _groupsArray = null; |
| bumpChangeCountGroups(); |
| } |
| |
| public void setId(String id) { |
| if (!Misc.same(_id, id)){ |
| _id = id; |
| bumpChangeCountGlobal(); |
| } |
| } |
| |
| public void setName(String name) { |
| if (!Misc.same(_name, name)){ |
| _name = name; |
| bumpChangeCountGlobal(); |
| } |
| } |
| |
| @Override |
| public ValidationResult validate(IResource resource, int kind, ValOperation operation, IProgressMonitor monitor){ |
| return validate(resource, kind, operation, monitor, null); |
| } |
| |
| @Override |
| public ValidationResult validate(IResource resource, int kind, ValOperation operation, IProgressMonitor monitor, ValidationEvent event) { |
| ValidationResult vr = null; |
| if (operation == null)operation = new ValOperation(); |
| if (monitor == null)monitor = new NullProgressMonitor(); |
| try { |
| if (event == null)event = new ValidationEvent(resource, kind, null); |
| vr = getDelegatedValidator().validate(event, operation.getState(), monitor); |
| if (vr == null)vr = getDelegatedValidator().validate(resource, kind, operation.getState(), monitor); |
| } |
| catch (Exception e){ |
| try { |
| String msg = NLS.bind(ValMessages.LogValEnd, getName(), resource.getLocationURI()); |
| ValidationPlugin.getPlugin().logMessage(IStatus.ERROR, msg); |
| } |
| catch (Exception e2 ){ |
| // ignore it |
| } |
| ValidationPlugin.getPlugin().handleException(e); |
| } |
| |
| if (vr != null){ |
| if (vr.getValidationException() != null){ |
| ValidationPlugin.getPlugin().handleException(vr.getValidationException()); |
| } |
| updateResults(vr); |
| if (vr.getDependsOn() != null){ |
| ValidationFramework.getDefault().getDependencyIndex().set(getId(), resource, vr.getDependsOn()); |
| } |
| IResource[] validated = vr.getValidated(); |
| if (validated != null){ |
| for (int i=0; i<validated.length; i++){ |
| operation.addValidated(getId(), validated[i]); |
| } |
| } |
| |
| ValidatorMessage[] msgs = vr.getMessages(); |
| // if (sanityTest(msgs.length, resource)){ |
| MarkerManager mm = MarkerManager.getDefault(); |
| for (ValidatorMessage m : msgs){ |
| mm.createMarker(m, getId()); |
| } |
| // } |
| // else { |
| // setBuildValidation(false); |
| // setManualValidation(false); |
| // } |
| } |
| return vr; |
| } |
| |
| /** |
| * Perform a simple sanity test to ensure that the validator is configured correctly. |
| * @param numberofMessages number of messages that the validator produced. |
| * @return true if the test passed |
| */ |
| // private boolean sanityTest(int numberofMessages, IResource resource) { |
| // if (numberofMessages < 201)return true; |
| // |
| // String resName = ""; //$NON-NLS-1$ |
| // if (resource != null)resName = resource.getName(); |
| // String message = NLS.bind(ValMessages.ConfigError, new Object[]{ |
| // getName(), getId(), String.valueOf(numberofMessages), resName}); |
| // ValidationPlugin.getPlugin().logMessage(IStatus.ERROR, message); |
| // |
| // return false; |
| // } |
| |
| /** |
| * If the validator is using a report helper then update it with any of the messages that were |
| * added directly to the validation result. |
| * @param vr |
| */ |
| private void updateResults(ValidationResult vr) { |
| ReporterHelper rh = vr.getReporterHelper(); |
| if (rh == null)return; |
| ClassLoader classloader = getDelegatedValidator().getClass().getClassLoader(); |
| for (IMessage message : rh.getMessages()){ |
| Object target = message.getTargetObject(); |
| if (target != null){ |
| IResource res = null; |
| if (target instanceof IResource)res = (IResource)target; |
| if (res == null){ |
| target = message.getAttribute(IMessage.TargetResource); |
| if (target != null && target instanceof IResource)res = (IResource)target; |
| } |
| if (res != null){ |
| |
| ValidatorMessage vm = ValidatorMessage.create(message.getText(classloader), res); |
| if (getMarkerId() != null)vm.setType(getMarkerId()); |
| vr.add(vm); |
| int markerSeverity = IMarker.SEVERITY_INFO; |
| int sev = message.getSeverity(); |
| if ((sev & IMessage.HIGH_SEVERITY) != 0)markerSeverity = IMarker.SEVERITY_ERROR; |
| else if ((sev & IMessage.NORMAL_SEVERITY) != 0)markerSeverity = IMarker.SEVERITY_WARNING; |
| vm.setAttribute(IMarker.SEVERITY, markerSeverity); |
| vm.setAttribute(IMarker.LINE_NUMBER, message.getLineNumber()); |
| int offset = message.getOffset(); |
| if (offset != IMessage.OFFSET_UNSET){ |
| vm.setAttribute(IMarker.CHAR_START, offset); |
| int len = message.getLength(); |
| if (len != IMessage.OFFSET_UNSET){ |
| vm.setAttribute(IMarker.CHAR_START, offset); |
| vm.setAttribute(IMarker.CHAR_END, offset+len); |
| } |
| } |
| String groupName = message.getGroupName(); |
| if (groupName != null){ |
| vm.setAttribute(ConfigurationConstants.VALIDATION_MARKER_GROUP, groupName); |
| } |
| |
| copyAttributes(message, vm); |
| } |
| } |
| } |
| } |
| |
| @SuppressWarnings("unchecked") |
| private void copyAttributes(IMessage message, ValidatorMessage vm) { |
| // I made this a separate method, so that I could localize the suppression of unchecked warnings. |
| Map attributes = message.getAttributes(); |
| if (attributes != null){ |
| for (Iterator it = attributes.entrySet().iterator(); it.hasNext();){ |
| Map.Entry me = (Map.Entry)it.next(); |
| String key = (String)me.getKey(); |
| vm.setAttribute(key, me.getValue()); |
| } |
| } |
| } |
| |
| @Override |
| public void validationStarting(IProject project, ValidationState state, IProgressMonitor monitor) { |
| if (project == null)_pendingValidationStarted.set(true); |
| else { |
| AbstractValidator val = getDelegatedValidator(); |
| if (_pendingValidationStarted.getAndSet(false)){ |
| val.validationStarting(null, state, monitor); |
| } |
| val.validationStarting(project, state, monitor); |
| } |
| } |
| |
| @Override |
| public void validationFinishing(IProject project, ValidationState state, IProgressMonitor monitor) { |
| if (project == null){ |
| if (!_pendingValidationStarted.getAndSet(false))getDelegatedValidator().validationFinishing(null, state, monitor); |
| } |
| else getDelegatedValidator().validationFinishing(project, state, monitor); |
| } |
| |
| @SuppressWarnings("unchecked") |
| public Object getAdapter(Class adapter) { |
| return Platform.getAdapterManager().getAdapter(this, adapter); |
| } |
| |
| public synchronized void remove(FilterGroup group) { |
| _groups.remove(group); |
| _groupsArray = null; |
| bumpChangeCountGroups(); |
| } |
| |
| @Override |
| public void resetChangeCounters() { |
| super.resetChangeCounters(); |
| _changeCountGroups = 0; |
| } |
| |
| @Override |
| protected boolean shouldValidateProject(IProject project) { |
| FilterGroup[] groups = getGroups(); |
| ContentTypeWrapper ctw = new ContentTypeWrapper(); |
| for (FilterGroup group : groups){ |
| if (!group.shouldValidate(project, null, ctw))return false; |
| } |
| return true; |
| } |
| |
| public synchronized void replaceFilterGroup(FilterGroup existing, FilterGroup merged) { |
| remove(existing); |
| add(merged); |
| } |
| |
| } |
| |
| public String getSourceId() { |
| return _sourceId; |
| } |
| |
| public void setSourceId(String sourceId) { |
| if (!Misc.same(_sourceId, sourceId)){ |
| _sourceId = sourceId; |
| bumpChangeCountGlobal(); |
| } |
| } |
| |
| |
| void setMessages(Map<String, MessageSeveritySetting> map) { |
| _messageSettings = map; |
| bumpChangeCountMessages(); |
| } |
| |
| public int getChangeCountGlobal() { |
| return _changeCountGlobal; |
| } |
| |
| public boolean hasGlobalChanges(){ |
| return _migrated || _changeCountGlobal > 0; |
| } |
| |
| public int getChangeCountMessages() { |
| return _changeCountMessages; |
| } |
| |
| public void bumpChangeCountMessages(){ |
| _changeCountMessages++; |
| } |
| |
| public void resetChangeCounters() { |
| _changeCountGlobal = 0; |
| _changeCountMessages = 0; |
| } |
| |
| public void bumpChangeCountGlobal(){ |
| _changeCountGlobal++; |
| } |
| |
| /** |
| * Answer true if you have the same configuration settings as validator. |
| * @param validator this can be null. |
| */ |
| public boolean sameConfig(Validator validator) { |
| if (validator == null)return false; |
| return hashCodeForConfig() == validator.hashCodeForConfig(); |
| } |
| |
| public String getMarkerId() { |
| return _markerId; |
| } |
| |
| public void setMarkerId(String markerId) { |
| _markerId = markerId; |
| if (markerId != null)MarkerManager.getDefault().getMarkers().add(markerId); |
| } |
| |
| } |