blob: 98cb0b4923d6db52d6249fd35b6594e71dfeae24 [file] [log] [blame]
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* 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 com.intellij.xml.impl.schema;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.Nullable;
import java.util.HashSet;
import java.util.Set;
/**
* @author Dmitry Avdeev
*/
public abstract class XmlSchemaTagsProcessor {
public final static ThreadLocal<Boolean> PROCESSING_FLAG = new ThreadLocal<Boolean>();
private final Set<XmlTag> myVisited = new HashSet<XmlTag>();
protected final XmlNSDescriptorImpl myNsDescriptor;
private final String[] myTagsToIgnore;
public XmlSchemaTagsProcessor(XmlNSDescriptorImpl nsDescriptor, String... tagsToIgnore) {
myNsDescriptor = nsDescriptor;
myTagsToIgnore = ArrayUtil.append(tagsToIgnore, "annotation");
}
public final void startProcessing(XmlTag tag) {
try {
PROCESSING_FLAG.set(Boolean.TRUE);
processTag(tag, null);
}
finally {
PROCESSING_FLAG.set(null);
}
}
private void processTag(XmlTag tag, @Nullable XmlTag context) {
if (myVisited.contains(tag)) return;
myVisited.add(tag);
if (!XmlNSDescriptorImpl.checkSchemaNamespace(tag)) {
processTagWithSubTags(tag, context, null);
return;
}
String tagName = tag.getLocalName();
if (checkTagName(tagName, "element", "attribute")) {
XmlAttribute ref = tag.getAttribute("ref");
if (ref != null) {
XmlTag resolved = resolveTagReference(ref);
if (resolved != null) {
tagStarted(resolved, resolved.getLocalName(), tag, tag);
}
}
else {
tagStarted(tag, tag.getLocalName(), context, null);
}
}
else if (checkTagName(tagName, "group")) {
String value = tag.getAttributeValue("ref");
if (value != null) {
XmlTag group = myNsDescriptor.findGroup(value);
if (group == null) group = resolveTagReference(tag.getAttribute("ref"));
processTagWithSubTags(group, tag, tag);
}
}
else if (checkTagName(tagName, "attributeGroup")) {
String ref = tag.getAttributeValue("ref");
if (ref == null) return;
XmlTag group;
XmlTag parentTag = tag.getParentTag();
assert parentTag != null;
if (XmlNSDescriptorImpl.equalsToSchemaName(parentTag, "attributeGroup") &&
ref.equals(parentTag.getAttributeValue("name"))) {
group = resolveTagReference(tag.getAttribute("ref"));
if (group == null) group = myNsDescriptor.findAttributeGroup(ref);
}
else {
group = myNsDescriptor.findAttributeGroup(ref);
if (group == null) group = resolveTagReference(tag.getAttribute("ref"));
}
processTagWithSubTags(group, tag, null);
}
else if (checkTagName(tagName, "restriction", "extension")) {
processTagWithSubTags(resolveTagReference(tag.getAttribute("base")), tag, null);
processTagWithSubTags(tag, context, null);
}
else if (!checkTagName(tagName, myTagsToIgnore)) {
processTagWithSubTags(tag, context, null);
}
}
private void processTagWithSubTags(@Nullable XmlTag tag, XmlTag ctx, @Nullable XmlTag ref) {
if (tag == null) return;
tagStarted(tag, tag.getLocalName(), ctx, ref);
XmlTag[] subTags = tag.getSubTags();
for (XmlTag subTag : subTags) {
processTag(subTag, tag);
}
tagFinished(tag);
}
protected abstract void tagStarted(XmlTag tag, String tagName, XmlTag context, @Nullable XmlTag ref);
protected void tagFinished(XmlTag tag) {}
@Nullable
private static XmlTag resolveTagReference(XmlAttribute ref) {
PsiElement element = resolveReference(ref);
return element instanceof XmlTag ? (XmlTag)element : null;
}
@Nullable
static PsiElement resolveReference(XmlAttribute ref) {
if (ref != null) {
XmlAttributeValue value = ref.getValueElement();
if (value != null) {
PsiReference[] references = value.getReferences();
if (references.length > 0)
return references[0].resolve();
}
}
return null;
}
protected static boolean checkTagName(String tagName, String... names) {
return ArrayUtil.contains(tagName, names);
}
}