| /* |
| * 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])); |
| } |
| |
| } |