/*
* Copyright 2011-present Greg Shrago
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.intellij.grammar.psi.impl;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import org.intellij.grammar.KnownAttribute;
import org.intellij.grammar.generator.BnfConstants;
import org.intellij.grammar.generator.ParserGeneratorUtil;
import org.intellij.grammar.java.JavaHelper;
import org.intellij.grammar.psi.*;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.List;
/**
* @author gregsh
*/
public class BnfReferenceImpl<T extends BnfCompositeElement> extends PsiReferenceBase<T> {
public BnfReferenceImpl(@NotNull T element, TextRange range) {
super(element, range);
}
@Override
public PsiElement resolve() {
PsiFile containingFile = myElement.getContainingFile();
String referenceName = getRangeInElement().substring(myElement.getText());
PsiElement result = containingFile instanceof BnfFile? ((BnfFile)containingFile).getRule(referenceName) : null;
if (result == null && GrammarUtil.isExternalReference(myElement)) {
PsiElement parent = myElement.getParent();
int paramCount = parent instanceof BnfSequence ? ((BnfSequence)parent).getExpressionList().size() - 1 :
parent instanceof BnfExternalExpression? ((BnfExternalExpression)parent).getExpressionList().size() - 1 : 0;
BnfRule rule = PsiTreeUtil.getParentOfType(myElement, BnfRule.class);
String parserClass = ParserGeneratorUtil.getAttribute(rule, KnownAttribute.PARSER_UTIL_CLASS);
// paramCount + 2 (builder and level)
JavaHelper helper = JavaHelper.getJavaHelper(myElement);
for (String className = parserClass; className != null; className = helper.getSuperClassName(className)) {
List<NavigatablePsiElement> methods = helper.findClassMethods(className, JavaHelper.MethodType.STATIC, referenceName, paramCount + 2);
result = ContainerUtil.getFirstItem(methods);
if (result != null) break;
}
}
return result;
}
@NotNull
@Override
public Object[] getVariants() {
List<LookupElement> list = ContainerUtil.newArrayList();
boolean isExternal = GrammarUtil.isExternalReference(myElement);
if (!isExternal || PsiTreeUtil.getParentOfType(myElement, BnfExternalExpression.class) != null) {
PsiFile containingFile = myElement.getContainingFile();
List<BnfRule> rules = containingFile instanceof BnfFile ? ((BnfFile)containingFile).getRules() : Collections.emptyList();
for (BnfRule rule : rules) {
boolean fakeRule = ParserGeneratorUtil.Rule.isFake(rule);
boolean privateRule = ParserGeneratorUtil.Rule.isPrivate(rule);
if (isExternal && !ParserGeneratorUtil.Rule.isMeta(rule)) continue;
String idText = rule.getId().getText();
LookupElementBuilder e = LookupElementBuilder.create(rule, idText)
.withIcon(rule.getIcon(0))
.withBoldness(!privateRule)
.withStrikeoutness(fakeRule);
if (!Comparing.equal(idText, rule.getName())) {
e = e.withLookupString(rule.getName());
}
list.add(e);
}
}
if (isExternal) {
BnfRule rule = PsiTreeUtil.getParentOfType(myElement, BnfRule.class);
String parserClass = ParserGeneratorUtil.getAttribute(rule, KnownAttribute.PARSER_UTIL_CLASS);
if (StringUtil.isNotEmpty(parserClass)) {
JavaHelper helper = JavaHelper.getJavaHelper(myElement);
for (String className = parserClass; className != null; className = helper.getSuperClassName(className)) {
for (NavigatablePsiElement element : helper.findClassMethods(className, JavaHelper.MethodType.STATIC, "*", -1, BnfConstants.PSI_BUILDER_CLASS, "int")) {
List<String> methodTypes = helper.getMethodTypes(element);
if ("boolean".equals(ContainerUtil.getFirstItem(methodTypes))) {
list.add(LookupElementBuilder.createWithIcon((PsiNamedElement)element));
}
}
}
}
}
return ArrayUtil.toObjectArray(list);
}
}