package org.xpect.xtext.lib.tests;
import static com.google.common.collect.Iterables.filter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.xtext.common.types.JvmOperation;
import org.eclipse.xtext.diagnostics.Severity;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.util.CancelIndicator;
import org.eclipse.xtext.validation.CheckMode;
import org.eclipse.xtext.validation.IResourceValidator;
import org.eclipse.xtext.validation.Issue;
import org.xpect.XjmXpectMethod;
import org.xpect.XpectFile;
import org.xpect.XpectImport;
import org.xpect.XpectInvocation;
import org.xpect.XpectReplace;
import org.xpect.registry.AbstractDelegatingModule;
import org.xpect.registry.DefaultBinding;
import org.xpect.setup.XpectGuiceModule;
import org.xpect.setup.XpectSetupFactory;
import org.xpect.state.Configuration;
import org.xpect.state.Creates;
import org.xpect.state.XpectStateAnnotation;
import org.xpect.text.IRegion;
import org.xpect.text.Region;
import org.xpect.ui.services.XtResourceValidator;
import org.xpect.ui.util.XpectFileAccess;
import org.xpect.util.JvmAnnotationUtil;
import org.xpect.xtext.lib.setup.ThisResource;
import org.xpect.xtext.lib.setup.XtextValidatingSetup;
import org.xpect.xtext.lib.tests.ValidationTestModuleSetup.IssuesByLineProvider;
import org.xpect.xtext.lib.util.IssueOverlapsRangePredicate;
import org.xpect.xtext.lib.util.NextLine.NextLineProvider;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.inject.Binder;
import com.google.inject.Key;
/**
* @author Moritz Eysholdt - Initial contribution and API
*/
@XpectGuiceModule
@XpectImport(IssuesByLineProvider.class)
public class ValidationTestModuleSetup extends AbstractDelegatingModule {
@XpectStateAnnotation
@Retention(RetentionPolicy.RUNTIME)
public @interface ConsumedIssues {
Severity[]value();
}
@XpectStateAnnotation
@Retention(RetentionPolicy.RUNTIME)
public @interface IssuesByLine {
}
@XpectSetupFactory
@XpectReplace(XtextValidatingSetup.class)
public static class IssuesByLineProvider extends XtextValidatingSetup {
private Multimap<IRegion, Issue> issuesByLine = null;
public IssuesByLineProvider(@ThisResource XtextResource resource) {
super(resource);
}
@Override
protected List<Issue> collectIssues() {
return Lists.newArrayList(collectIssuesByLine().get(ValidationTestModuleSetup.UNMATCHED));
}
@Creates(IssuesByLine.class)
public Multimap<IRegion, Issue> collectIssuesByLine() {
if (issuesByLine == null) {
TestingResourceValidator validator = (TestingResourceValidator) getResource().getResourceServiceProvider().getResourceValidator();
issuesByLine = validator.validateDelegateAndMapByOffset(getResource(), CheckMode.ALL, CancelIndicator.NullImpl, null);
}
return issuesByLine;
}
}
public static class SeverityPredicate implements Predicate<Issue> {
private Severity[] severities;
public SeverityPredicate(Severity... severity) {
super();
this.severities = severity;
}
@Override
public boolean apply(Issue input) {
Severity e = input.getSeverity();
for (Severity s : severities) {
if (s == e)
return true;
}
return false;
}
}
public static class TestingResourceValidator extends XtResourceValidator {
protected Set<Severity> getExpectedSeverity(XpectInvocation inv) {
if (inv == null || inv.eIsProxy())
return null;
XjmXpectMethod method = inv.getMethod();
if (method == null || method.eIsProxy())
return null;
JvmOperation operation = method.getJvmMethod();
if (operation == null || operation.eIsProxy())
return null;
ConsumedIssues annotation = JvmAnnotationUtil.getJavaAnnotation(operation, ConsumedIssues.class);
if (annotation == null)
return null;
EnumSet<Severity> result = EnumSet.copyOf(Lists.newArrayList(annotation.value()));
return result;
}
@Override
public List<Issue> validateDelegate(Resource resource, CheckMode mode, CancelIndicator indicator, Configuration fileConfig) {
return Lists.newArrayList(validateDelegateAndMapByOffset(resource, mode, indicator, fileConfig).get(UNMATCHED));
}
public Multimap<IRegion, Issue> validateDelegateAndMapByOffset(Resource resource, CheckMode mode, CancelIndicator indicator, Configuration fileConfig) {
Multimap<IRegion, Issue> result = LinkedHashMultimap.create();
List<Issue> issuesFromDelegate = super.validateDelegate(resource, mode, indicator, fileConfig);
if (resource instanceof XtextResource && ((XtextResource) resource).getParseResult() != null) {
XtextResource xresource = (XtextResource) resource;
if (issuesFromDelegate != null && !issuesFromDelegate.isEmpty()) {
XpectFile xpectFile = XpectFileAccess.getXpectFile(resource);
ValidationTestConfig config = new ValidationTestConfig(xpectFile.<ValidationTestConfig> createSetupInitializer());
Set<Issue> issues = Sets.newLinkedHashSet(issuesFromDelegate);
Set<Issue> matched = Sets.newHashSet();
for (XpectInvocation inv : xpectFile.getInvocations()) {
Set<Severity> severities = getExpectedSeverity(inv);
if (severities != null) {
IRegion region = new NextLineProvider(xresource, inv).getNextLine();
List<Issue> selected = Lists.newArrayList(filter(issues, new IssueOverlapsRangePredicate(region, severities)));
result.putAll(region, selected);
matched.addAll(selected);
}
}
issues.removeAll(matched);
result.putAll(UNMATCHED, filter(issues, config.getIgnoreFilter()));
}
} else {
result.putAll(UNMATCHED, issuesFromDelegate);
}
if (fileConfig != null) {
fileConfig.addValue(IssuesByLine.class, result);
}
return ImmutableMultimap.copyOf(result);
}
}
public static final IRegion UNMATCHED = new Region("(unmatched)", -1, 0);
public void configure(Binder binder) {
binder.bind(IResourceValidator.class).to(TestingResourceValidator.class);
binder.bind(IResourceValidator.class).annotatedWith(DefaultBinding.class).to(getOriginalType(Key.get(IResourceValidator.class)));
}
}