blob: a6fbc0c33c46a687c6b0ba069c9f07762b54f685 [file] [log] [blame]
/*
* Copyright 2000-2013 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.psi.impl.source.xml;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.NullableComputable;
import com.intellij.psi.PsiFile;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.xml.XmlElementDescriptor;
import com.intellij.xml.actions.validate.ValidateXmlActionHandler;
import com.intellij.xml.actions.validate.ErrorReporter;
import org.apache.xerces.impl.Constants;
import org.apache.xerces.impl.xs.SubstitutionGroupHandler;
import org.apache.xerces.impl.xs.XSComplexTypeDecl;
import org.apache.xerces.impl.xs.XSElementDecl;
import org.apache.xerces.impl.xs.XSGrammarBucket;
import org.apache.xerces.impl.xs.models.CMBuilder;
import org.apache.xerces.impl.xs.models.CMNodeFactory;
import org.apache.xerces.impl.xs.models.XSCMValidator;
import org.apache.xerces.xni.QName;
import org.apache.xerces.xni.grammars.Grammar;
import org.apache.xerces.xni.grammars.XMLGrammarDescription;
import org.apache.xerces.xni.grammars.XMLGrammarPool;
import org.apache.xerces.xni.grammars.XSGrammar;
import org.apache.xerces.xs.XSElementDeclaration;
import org.apache.xerces.xs.XSModel;
import org.apache.xerces.xs.XSTypeDefinition;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author Dmitry Avdeev
*/
class XsContentDFA extends XmlContentDFA {
private final XSCMValidator myContentModel;
private final SubstitutionGroupHandler myHandler;
private final int[] myState;
private final XmlElementDescriptor[] myElementDescriptors;
@Nullable
public static XmlContentDFA createContentDFA(@NotNull XmlTag parentTag) {
final PsiFile file = parentTag.getContainingFile().getOriginalFile();
if (!(file instanceof XmlFile)) return null;
XSModel xsModel = ApplicationManager.getApplication().runReadAction(new NullableComputable<XSModel>() {
@Override
public XSModel compute() {
return getXSModel((XmlFile)file);
}
});
if (xsModel == null) {
return null;
}
XSElementDeclaration decl = getElementDeclaration(parentTag, xsModel);
if (decl == null) {
return null;
}
return new XsContentDFA(decl, parentTag);
}
public XsContentDFA(@NotNull XSElementDeclaration decl, final XmlTag parentTag) {
XSComplexTypeDecl definition = (XSComplexTypeDecl)decl.getTypeDefinition();
myContentModel = definition.getContentModel(new CMBuilder(new CMNodeFactory()));
myHandler = new SubstitutionGroupHandler(new XSGrammarBucket());
myState = myContentModel.startContentModel();
myElementDescriptors = ApplicationManager.getApplication().runReadAction(new Computable<XmlElementDescriptor[]>() {
@Override
public XmlElementDescriptor[] compute() {
XmlElementDescriptor parentTagDescriptor = parentTag.getDescriptor();
assert parentTagDescriptor != null;
return parentTagDescriptor.getElementsDescriptors(parentTag);
}
});
}
@Override
public List<XmlElementDescriptor> getPossibleElements() {
final List vector = myContentModel.whatCanGoHere(myState);
ArrayList<XmlElementDescriptor> list = new ArrayList<XmlElementDescriptor>();
for (Object o : vector) {
if (o instanceof XSElementDecl) {
final XSElementDecl elementDecl = (XSElementDecl)o;
XmlElementDescriptor descriptor = ContainerUtil.find(myElementDescriptors, new Condition<XmlElementDescriptor>() {
@Override
public boolean value(XmlElementDescriptor elementDescriptor) {
return elementDecl.getName().equals(elementDescriptor.getName());
}
});
ContainerUtil.addIfNotNull(descriptor, list);
}
}
return list;
}
@Override
public void transition(XmlTag xmlTag) {
myContentModel.oneTransition(createQName(xmlTag), myState, myHandler);
}
private static QName createQName(XmlTag tag) {
//todo don't use intern to not pollute PermGen
String namespace = tag.getNamespace();
return new QName(tag.getNamespacePrefix().intern(),
tag.getLocalName().intern(),
tag.getName().intern(),
namespace.isEmpty() ? null : namespace.intern());
}
@Nullable
private static XSElementDeclaration getElementDeclaration(XmlTag tag, XSModel xsModel) {
List<XmlTag> ancestors = new ArrayList<XmlTag>();
for (XmlTag t = tag; t != null; t = t.getParentTag()) {
ancestors.add(t);
}
Collections.reverse(ancestors);
XSElementDeclaration declaration = null;
SubstitutionGroupHandler fSubGroupHandler = new SubstitutionGroupHandler(new XSGrammarBucket());
CMBuilder cmBuilder = new CMBuilder(new CMNodeFactory());
for (XmlTag ancestor : ancestors) {
if (declaration == null) {
declaration = xsModel.getElementDeclaration(ancestor.getLocalName(), ancestor.getNamespace());
if (declaration == null) return null;
else continue;
}
XSTypeDefinition typeDefinition = declaration.getTypeDefinition();
if (!(typeDefinition instanceof XSComplexTypeDecl)) {
return null;
}
XSCMValidator model = ((XSComplexTypeDecl)typeDefinition).getContentModel(cmBuilder);
int[] ints = model.startContentModel();
for (XmlTag subTag : ancestor.getParentTag().getSubTags()) {
QName qName = createQName(subTag);
Object o = model.oneTransition(qName, ints, fSubGroupHandler);
if (subTag == ancestor) {
if (o instanceof XSElementDecl) {
declaration = (XSElementDecl)o;
break;
}
else return null;
}
}
}
return declaration;
}
@Nullable
private static XSModel getXSModel(XmlFile file) {
ValidateXmlActionHandler handler = new ValidateXmlActionHandler(false) {
@Override
protected SAXParser createParser() throws SAXException, ParserConfigurationException {
SAXParser parser = super.createParser();
parser.getXMLReader().setFeature(Constants.XERCES_FEATURE_PREFIX + Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE, true);
return parser;
}
};
handler.setErrorReporter(new ErrorReporter(handler) {
int count;
@Override
public void processError(SAXParseException ex, ValidateXmlActionHandler.ProblemType warning) throws SAXException {
if (warning != ValidateXmlActionHandler.ProblemType.WARNING && count++ > 100) {
throw new SAXException(ex);
}
}
@Override
public boolean isUniqueProblem(SAXParseException e) {
return true;
}
});
handler.doValidate(file);
XMLGrammarPool grammarPool = ValidateXmlActionHandler.getGrammarPool(file);
if (grammarPool == null) {
return null;
}
Grammar[] grammars = grammarPool.retrieveInitialGrammarSet(XMLGrammarDescription.XML_SCHEMA);
return grammars.length == 0 ? null : ((XSGrammar)grammars[0]).toXSModel(ContainerUtil.map(grammars, new Function<Grammar, XSGrammar>() {
@Override
public XSGrammar fun(Grammar grammar) {
return (XSGrammar)grammar;
}
}, new XSGrammar[0]));
}
}