blob: 23aabcebefc04ce7f02a06b82be950b3fdc7b7aa [file] [log] [blame]
/*
* Copyright 2005 Sascha Weinreuter
*
* 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.lang.xpath.xslt.context;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.impl.source.resolve.ResolveCache;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.Processor;
import org.intellij.lang.xpath.context.VariableContext;
import org.intellij.lang.xpath.psi.XPathElement;
import org.intellij.lang.xpath.psi.XPathVariable;
import org.intellij.lang.xpath.psi.XPathVariableReference;
import org.intellij.lang.xpath.psi.impl.ResolveUtil;
import org.intellij.lang.xpath.xslt.XsltSupport;
import org.intellij.lang.xpath.xslt.impl.XsltIncludeIndex;
import org.intellij.lang.xpath.xslt.psi.XsltElementFactory;
import org.intellij.lang.xpath.xslt.psi.XsltParameter;
import org.intellij.lang.xpath.xslt.psi.XsltTemplate;
import org.intellij.lang.xpath.xslt.psi.XsltVariable;
import org.intellij.lang.xpath.xslt.quickfix.CreateParameterFix;
import org.intellij.lang.xpath.xslt.quickfix.CreateVariableFix;
import org.intellij.lang.xpath.xslt.util.ElementProcessor;
import org.intellij.lang.xpath.xslt.util.XsltCodeInsightUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public class XsltVariableContext implements VariableContext<XsltVariable> {
public static final XsltVariableContext INSTANCE = new XsltVariableContext();
private final ResolveCache.Resolver RESOLVER = new ResolveCache.Resolver() {
@Nullable
public PsiElement resolve(@NotNull PsiReference psiReference, boolean incompleteCode) {
return resolveInner((XPathVariableReference)psiReference);
}
};
@NotNull
public XsltVariable[] getVariablesInScope(XPathElement element) {
final XmlTag context = getContextTagImpl(element);
final VariantsProcessor processor = new VariantsProcessor(context);
ResolveUtil.treeWalkUp(processor, context);
processForwardGlobals(context, processor);
return processor.getResult();
}
public XPathVariable resolve(final XPathVariableReference reference) {
return (XPathVariable) ResolveCache.getInstance(reference.getProject()).resolveWithCaching(reference, RESOLVER, false, false);
}
@Nullable
private XPathVariable resolveInner(XPathVariableReference reference) {
final XmlTag context = getContextTagImpl(reference);
final VariableResolveProcessor processor = new VariableResolveProcessor(reference.getReferencedName(), context);
final XPathVariable variable = (XPathVariable)ResolveUtil.treeWalkUp(processor, context);
if (variable != null) {
return variable;
}
if (!processForwardGlobals(context, processor)) {
final XmlFile file = PsiTreeUtil.getParentOfType(context, XmlFile.class, true);
if (file != null) {
XsltIncludeIndex.processBackwardDependencies(file, new Processor<XmlFile>() {
public boolean process(XmlFile xmlFile) {
processor.processExternalFile(xmlFile, context);
return processor.shouldContinue();
}
});
}
}
return (XPathVariable)processor.getResult();
}
private static boolean processForwardGlobals(XmlTag context, VariableProcessor processor) {
while (context != null && !XsltSupport.isTopLevelElement(context)) {
context = context.getParentTag();
}
while (context != null && processor.shouldContinue()) {
processor.process(context);
context = PsiTreeUtil.getNextSiblingOfType(context, XmlTag.class);
}
return !processor.shouldContinue();
}
@Nullable
protected XmlTag getContextTagImpl(XPathElement element) {
return PsiTreeUtil.getContextOfType(element, XmlTag.class, true);
}
@NotNull
public IntentionAction[] getUnresolvedVariableFixes(XPathVariableReference reference) {
return new IntentionAction[] {
new CreateVariableFix(reference),
new CreateParameterFix(reference)
};
}
public boolean isReferenceTo(PsiElement element, XPathVariableReference reference) {
if (element instanceof XsltParameter) {
final XsltTemplate template = XsltCodeInsightUtil.getTemplate(element, false);
if (template == null || template.getMatchExpression() == null) return false;
final XPathVariable t = reference.resolve();
final PsiReference[] references = element.getReferences();
for (PsiReference r : references) {
if (r.isReferenceTo(t)) return true;
}
}
return false;
}
public boolean canResolve() {
return true;
}
static abstract class VariableProcessor extends ElementProcessor<XmlTag> {
public VariableProcessor(XmlTag context) {
super(context);
}
protected boolean followImport() {
return true;
}
protected final void processTemplate(XmlTag tag) {
// not interested
}
protected abstract void processVarOrParamImpl(XmlTag tag);
protected final void processVarOrParam(XmlTag tag) {
if (tag != myRoot) {
processVarOrParamImpl(tag);
}
}
protected abstract boolean shouldContinue();
}
static class VariantsProcessor extends VariableProcessor {
private final List<XsltVariable> myNames = new ArrayList<XsltVariable>();
public VariantsProcessor(XmlTag context) {
super(context);
}
public XsltVariable[] getResult() {
return myNames.toArray(new XsltVariable[myNames.size()]);
}
protected void processVarOrParamImpl(XmlTag tag) {
if (XsltSupport.isVariableOrParam(tag)) {
myNames.add(XsltElementFactory.getInstance().wrapElement(tag, XsltVariable.class));
}
}
protected boolean shouldContinue() {
return true;
}
}
static class VariableResolveProcessor extends VariableProcessor implements ResolveUtil.ResolveProcessor {
private final String myName;
private PsiElement myResult = null;
public VariableResolveProcessor(final String name, XmlTag context) {
super(context);
myName = name;
}
public PsiElement getResult() {
return myResult;
}
protected boolean shouldContinue() {
return myResult == null;
}
protected void processVarOrParamImpl(XmlTag tag) {
if (XsltSupport.isVariableOrParam(tag)) {
final String name = tag.getAttributeValue("name");
if (myName.equals(name)) {
myResult = XsltElementFactory.getInstance().wrapElement(tag, XsltVariable.class);
}
}
}
}
}