/*******************************************************************************
* Copyright (c) 2007, 2014 Spring IDE Developers
* 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:
* Spring IDE Developers - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.core.model.validation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Dictionary;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.springframework.ide.eclipse.core.MarkerUtils;
import org.springframework.ide.eclipse.core.SpringCore;
import org.springframework.ide.eclipse.core.internal.model.validation.ValidationRuleDefinition;
import org.springframework.ide.eclipse.core.java.typehierarchy.TypeHierarchyEngine;
import org.springframework.ide.eclipse.core.model.IResourceModelElement;
import org.springframework.ide.eclipse.core.model.ISourceModelElement;
import org.springframework.ide.eclipse.core.project.IProjectContributorState;
import org.springframework.ide.eclipse.core.project.IProjectContributorStateAware;
/**
* Base {@link IValidationContext} implementation that handles creation of {@link ValidationProblem} s instances.
* @author Torsten Juergeleit
* @author Christian Dupuis
* @since 2.0
*/
public abstract class AbstractValidationContext implements IValidationContext, IProjectContributorStateAware,
IProjectContributorState {
private IResourceModelElement contextElement;
private IProjectContributorState contributorState;
private ValidationRuleDefinition currentRuleDefinition;
private Set<ValidationProblem> problems;
private IResourceModelElement rootElement;
public AbstractValidationContext(IResourceModelElement rootElement, IResourceModelElement contextElement) {
this.rootElement = rootElement;
this.contextElement = contextElement;
this.problems = new LinkedHashSet<ValidationProblem>();
}
/**
* {@inheritDoc}
*/
public void addProblems(ValidationProblem... problems) {
if (problems != null) {
addProblems(Arrays.asList(problems));
}
}
/**
* {@inheritDoc}
*/
public void error(IResourceModelElement element, String problemId, String message,
ValidationProblemAttribute... attributes) {
addProblems(createProblems(element, problemId, IValidationProblemMarker.SEVERITY_ERROR, message, attributes));
}
/**
* {@inheritDoc}
*/
public <T> T get(Class<T> clazz) {
return contributorState.get(clazz);
}
/**
* {@inheritDoc}
*/
public <T> T get(Class<T> clazz, String filterText) {
return contributorState.get(clazz, filterText);
}
/**
* {@inheritDoc}
*/
public IResourceModelElement getContextElement() {
return contextElement;
}
/**
* {@inheritDoc}
*/
public Set<ValidationProblem> getProblems() {
return problems;
}
/**
* {@inheritDoc}
*/
public IResourceModelElement getRootElement() {
return rootElement;
}
/**
* {@inheritDoc}
*/
public boolean hold(Object obj) {
return contributorState.hold(obj);
}
/**
* {@inheritDoc}
*/
public boolean hold(Object obj, Dictionary<String, String> attributes) {
return contributorState.hold(obj, attributes);
}
/**
* {@inheritDoc}
*/
public void info(IResourceModelElement element, String problemId, String message,
ValidationProblemAttribute... attributes) {
addProblems(createProblems(element, problemId, IValidationProblemMarker.SEVERITY_INFO, message, attributes));
}
/**
* {@inheritDoc}
*/
public void setCurrentRuleDefinition(ValidationRuleDefinition ruleDefinition) {
currentRuleDefinition = ruleDefinition;
}
/**
* {@inheritDoc}
*/
public void setProjectContributorState(IProjectContributorState contributorState) {
this.contributorState = contributorState;
}
/**
* {@inheritDoc}
*/
public void warning(IResourceModelElement element, String problemId, String message,
ValidationProblemAttribute... attributes) {
addProblems(createProblems(element, problemId, IValidationProblemMarker.SEVERITY_WARNING, message, attributes));
}
/**
* @return the TypeHierarchyEngine instance to be used for the validation operations
* @since 3.6.3
*/
public TypeHierarchyEngine getTypeHierarchyEngine() {
if (contributorState != null && contributorState.get(TypeHierarchyEngine.class) != null) {
return contributorState.get(TypeHierarchyEngine.class);
}
return SpringCore.getTypeHierarchyEngine();
}
/**
* Add the given problems to the internal state.
* <p>
* This implementation will make sure that progress is reported correctly.
*/
private void addProblems(Collection<ValidationProblem> problems) {
if (problems != null) {
for (ValidationProblem problem : problems) {
if (problem.getSeverity() == IValidationProblemMarker.SEVERITY_ERROR) {
this.problems.add(problem);
getProgressReportingState().incrementErrorCount();
}
else if (problem.getSeverity() == IValidationProblemMarker.SEVERITY_WARNING) {
this.problems.add(problem);
getProgressReportingState().incrementWarningCount();
}
else if (problem.getSeverity() == IValidationProblemMarker.SEVERITY_INFO) {
this.problems.add(problem);
getProgressReportingState().incrementInfoCount();
}
// IValidationProblemMarker.SEVERITY_UNKOWN falls through
}
}
}
private ValidationProgressState getProgressReportingState() {
if (contributorState != null && contributorState.get(ValidationProgressState.class) != null) {
return contributorState.get(ValidationProgressState.class);
}
// Fall back
return new ValidationProgressState();
}
/**
* Create a single root {@link ValidationProblem} from the provided information.
*/
protected final ValidationProblem createProblem(IResourceModelElement element, String problemId, int severity,
String message, ValidationProblemAttribute... attributes) {
// Get the line number from the element
int line = getLineNumber(element);
// Allow subclasses to decorate the error message
message = decorateErrorMessage(new StringBuilder(message)).toString();
// Add the element Id to the list of problem attributes
String elementId = element.getElementID();
List<ValidationProblemAttribute> attributeList = new ArrayList<ValidationProblemAttribute>(Arrays
.asList(attributes));
attributeList.add(new ValidationProblemAttribute(MarkerUtils.ELEMENT_ID_KEY, elementId));
return new ValidationProblem((currentRuleDefinition != null ? currentRuleDefinition.getId() : "UNKOWN"),
problemId, getSeverity(problemId, severity), message, element.getElementResource(), line, attributeList
.toArray(new ValidationProblemAttribute[attributeList.size()]));
}
/**
* Create {@link ValidationProblem}s for the given information.
* <p>
* Subclasses may override this method to change the creation of validation problems, e.g. to automatically create
* markers on other resources for certain error types.
* @since 2.0.3
*/
protected Set<ValidationProblem> createProblems(IResourceModelElement element, String problemId, int severity,
String message, ValidationProblemAttribute... attributes) {
Set<ValidationProblem> problems = new LinkedHashSet<ValidationProblem>(2);
problems.add(createProblem(element, problemId, severity, message, attributes));
return problems;
}
/**
* Create the textual representation for the given <code>context</code> element.
* @return a textual representation o
* @since 2.6.0
*/
protected StringBuilder decorateErrorMessage(StringBuilder builder) {
return builder;
}
/**
* Retrieve the line number from the given <code>element</code>.
*/
protected int getLineNumber(IResourceModelElement element) {
int line = (element instanceof ISourceModelElement ? ((ISourceModelElement) element).getElementStartLine() : -1);
// If the current element does not provide a valid line number -> iterate up the parent
// hierarchy
if (line == -1 && element.getElementParent() != null
&& element.getElementParent() instanceof IResourceModelElement) {
return getLineNumber((IResourceModelElement) element.getElementParent());
}
return line;
}
/**
* Returns the {@link IProjectContributorState} for subclass implementations.
*/
protected IProjectContributorState getProjectContributorState() {
return contributorState;
}
/**
* Calculates the severity of the given message checking the enablement state of the current rule and severity
* configuration of the check.
* @since 2.3.1
*/
protected int getSeverity(String messageId, int defaultSeverity) {
if (currentRuleDefinition != null
&& currentRuleDefinition.isEnabled(getRootElement().getElementResource().getProject())) {
Integer severity = currentRuleDefinition.getMessageSeverities().get(messageId);
if (severity != null) {
return severity;
}
}
return defaultSeverity;
}
}