blob: 0d9676b4627fecb6e69680628c5b2a45c4397faf [file] [log] [blame]
/*
* Copyright 2007 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.plugins.relaxNG.references;
import com.intellij.openapi.util.Key;
import com.intellij.patterns.XmlAttributeValuePattern;
import com.intellij.patterns.XmlNamedElementPattern;
import com.intellij.psi.PsiElement;
import com.intellij.psi.XmlElementVisitor;
import com.intellij.psi.search.PsiElementProcessor;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlDocument;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.ProcessingContext;
import com.intellij.xml.util.XmlUtil;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
class ResolvingVisitor extends XmlElementVisitor implements PsiElementProcessor {
static final Key<Set<XmlFile>> VISITED_KEY = Key.create("visited");
private final XmlAttributeValuePattern myPattern;
private final ProcessingContext myProcessingContext;
private XmlNamedElementPattern myIncludePattern;
public ResolvingVisitor(XmlAttributeValuePattern pattern, ProcessingContext context) {
myPattern = pattern;
myProcessingContext = context;
myProcessingContext.put(VISITED_KEY, new THashSet<XmlFile>());
}
@Override
public void visitXmlDocument(@Nullable XmlDocument document) {
if (document != null) {
final XmlTag rootTag = document.getRootTag();
if (rootTag != null) {
visitXmlTag(rootTag);
}
}
}
public void setIncludePattern(XmlNamedElementPattern includePattern) {
myIncludePattern = includePattern;
}
@Override
public void visitXmlAttribute(XmlAttribute attribute) {
if (myIncludePattern != null && myIncludePattern.accepts(attribute, myProcessingContext)) {
final String value = attribute.getValue();
if (value == null) return;
final XmlFile xmlFile = XmlUtil.findXmlFile(attribute.getContainingFile(), value);
if (xmlFile != null) {
processInclude(xmlFile, attribute);
}
} else {
processAttribute(attribute);
}
}
private void processAttribute(XmlAttribute attribute) {
myPattern.accepts(attribute.getValueElement(), myProcessingContext);
}
@Override
@SuppressWarnings({ "ForLoopReplaceableByForEach" })
public void visitXmlTag(XmlTag tag) {
visitAttributes(tag);
}
protected void visitAttributes(XmlTag tag) {
final XmlAttribute[] xmlAttributes = tag.getAttributes();
for (XmlAttribute attribute : xmlAttributes) {
attribute.accept(this);
}
}
protected void visitSubTags(XmlTag tag) {
final XmlTag[] tags = tag.getSubTags();
for (XmlTag subTag : tags) {
if (shouldContinue()) {
subTag.accept(this);
}
}
}
private void processInclude(XmlFile xmlFile, XmlAttribute attribute) {
final Set<XmlFile> set = myProcessingContext.get(VISITED_KEY);
if (set.contains(xmlFile)) {
return;
}
set.add(xmlFile);
final XmlDocument document = xmlFile.getDocument();
if (document == null) return;
final XmlTag rootTag = document.getRootTag();
if (rootTag == null) return;
rootTag.processElements(this, attribute);
}
@Override
public boolean execute(@NotNull PsiElement element) {
element.accept(this);
return shouldContinue();
}
protected boolean shouldContinue() {
return true;
}
}