package org.jetbrains.plugins.cucumber.steps;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.SmartPointerManager;
import com.intellij.psi.SmartPsiElementPointer;
import com.intellij.psi.search.SearchScope;
import com.intellij.util.CommonProcessors.CollectProcessor;
import org.apache.oro.text.regex.MalformedPatternException;
import org.apache.oro.text.regex.Pattern;
import org.apache.oro.text.regex.Perl5Compiler;
import org.apache.oro.text.regex.Perl5Matcher;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.cucumber.CucumberUtil;
import org.jetbrains.plugins.cucumber.psi.GherkinStep;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
/**
* @author yole, Andrey Vokin
*/
public abstract class AbstractStepDefinition {
private static final java.util.regex.Pattern ESCAPE_PATTERN
= java.util.regex.Pattern.compile("(\\$\\w+|#\\{.+?\\})");
private static final String CUCUMBER_START_PREFIX = "\\A";
private static final String CUCUMBER_END_SUFFIX = "\\z";
private final SmartPsiElementPointer<PsiElement> myElementPointer;
private volatile String myRegexText;
private volatile Pattern myRegex;
public AbstractStepDefinition(@NotNull final PsiElement element) {
myElementPointer = SmartPointerManager.getInstance(element.getProject()).createSmartPsiElementPointer(element);
}
public abstract List<String> getVariableNames();
public boolean matches(String stepName) {
final Pattern pattern = getPattern();
return pattern != null && new Perl5Matcher().contains(stepName, pattern);
}
@Nullable
public PsiElement getElement() {
return myElementPointer.getElement();
}
/**
* @return regexp pattern for step or null if regexp is malformed
*/
@Nullable
public Pattern getPattern() {
try {
final String cucumberRegex = getCucumberRegex();
if (cucumberRegex == null) return null;
if (myRegexText == null || !cucumberRegex.equals(myRegexText)) {
final StringBuilder patternText = new StringBuilder(ESCAPE_PATTERN.matcher(cucumberRegex).replaceAll("(.*)"));
if (patternText.toString().startsWith(CUCUMBER_START_PREFIX)) {
patternText.replace(0, CUCUMBER_START_PREFIX.length(), "^");
}
if (patternText.toString().endsWith(CUCUMBER_END_SUFFIX)) {
patternText.replace(patternText.length() - CUCUMBER_END_SUFFIX.length(), patternText.length(), "$");
}
myRegex = new Perl5Compiler().compile(patternText.toString(), Perl5Compiler.CASE_INSENSITIVE_MASK);
myRegexText = cucumberRegex;
}
return myRegex;
}
catch (final MalformedPatternException ignored) {
return null; // Bad regex?
}
}
@Nullable
public String getCucumberRegex() {
return getCucumberRegexFromElement(getElement());
}
@Nullable
protected abstract String getCucumberRegexFromElement(PsiElement element);
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AbstractStepDefinition that = (AbstractStepDefinition)o;
if (!myElementPointer.equals(that.myElementPointer)) return false;
return true;
}
@Override
public int hashCode() {
return myElementPointer.hashCode();
}
/**
* Set new value for step definitions (most likely provided by refactor->rename)
* @param newValue
*/
public void setCucumberRegex(@NotNull final String newValue) {
}
/**
* Checks if step definitions point supports certain step (i.e. some step definitions does not support some keywords)
*
* @param step Step to check
* @return true if supports.
*/
public boolean supportsStep(@NotNull final PsiElement step) {
return true;
}
/**
* Checks if step definition supports rename.
* @param newName if null -- check if definition supports renaming at all (regardless new name).
* If not null -- check if it can be renamed to the new (provided) name.
* @return true if rename is supportged
*/
public boolean supportsRename(@Nullable final String newName) {
return true;
}
/**
* Finds all steps points to this definition in some scope
*
* @param searchScope scope to find steps
* @return steps
*/
@NotNull
public Collection<GherkinStep> findSteps(@NotNull final SearchScope searchScope) {
final String regex = getCucumberRegex();
final PsiElement element = getElement();
if ((regex == null) || (element == null)) {
return Collections.emptyList();
}
final CollectProcessor<PsiReference> consumer = new CollectProcessor<>();
CucumberUtil.findGherkinReferencesToElement(element, regex, consumer, searchScope);
// We use hash to get rid of duplicates
final Collection<GherkinStep> results = new HashSet<>(consumer.getResults().size());
for (final PsiReference reference : consumer.getResults()) {
final PsiElement step = reference.getElement();
if (step instanceof GherkinStep) {
results.add((GherkinStep)step);
}
}
return results;
}
}