diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/DomInvocationHandler.java b/xml/dom-impl/src/com/intellij/util/xml/impl/DomInvocationHandler.java index 9057339cb653..1f4de200cf41 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/impl/DomInvocationHandler.java +++ b/xml/dom-impl/src/com/intellij/util/xml/impl/DomInvocationHandler.java @@ -28,6 +28,7 @@ import com.intellij.psi.SmartPointerManager; import com.intellij.psi.SmartPsiElementPointer; import com.intellij.psi.XmlElementFactory; import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.stubs.Stub; import com.intellij.psi.util.PsiUtilCore; import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlElement; @@ -40,10 +41,7 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.util.xml.*; import com.intellij.util.xml.events.DomEvent; import com.intellij.util.xml.reflect.*; -import com.intellij.util.xml.stubs.AttributeStub; -import com.intellij.util.xml.stubs.DomStub; -import com.intellij.util.xml.stubs.ElementStub; -import com.intellij.util.xml.stubs.StubParentStrategy; +import com.intellij.util.xml.stubs.*; import net.sf.cglib.proxy.AdvancedProxy; import net.sf.cglib.proxy.InvocationHandler; import org.jetbrains.annotations.NotNull; @@ -62,8 +60,8 @@ import java.util.List; /** * @author peter */ -public abstract class DomInvocationHandler extends UserDataHolderBase implements InvocationHandler, DomElement, - SemElement { +public abstract class DomInvocationHandler extends UserDataHolderBase implements InvocationHandler, DomElement, + SemElement { private static final Logger LOG = Logger.getInstance("#com.intellij.util.xml.impl.DomInvocationHandler"); public static final Method ACCEPT_METHOD = ReflectionUtil.getMethod(DomElement.class, "accept", DomElementVisitor.class); public static final Method ACCEPT_CHILDREN_METHOD = ReflectionUtil.getMethod(DomElement.class, "acceptChildren", DomElementVisitor.class); @@ -81,14 +79,14 @@ public abstract class DomInvocationHandler myAccessorInvocations = SmartFMap.emptyMap(); - @Nullable protected Stub myStub; + @Nullable protected S myStub; protected DomInvocationHandler(Type type, DomParentStrategy parentStrategy, @NotNull final EvaluatedXmlName tagName, final T childDescription, final DomManagerImpl manager, boolean dynamic, - @Nullable Stub stub) { + @Nullable S stub) { myManager = manager; myParentStrategy = parentStrategy; myTagName = tagName; @@ -129,7 +127,7 @@ public abstract class DomInvocationHandler stubs = myStub.getChildrenByName(xmlName.getLocalName(), xmlName.getNamespaceKey()); - return ContainerUtil.map(stubs, stub -> stub.getOrCreateHandler((DomChildDescriptionImpl)description, myManager).getProxy()); + List elements = + ContainerUtil.map(stubs, stub -> stub.getOrCreateHandler((DomChildDescriptionImpl)description, myManager).getProxy()); + return addIncludedElements(elements, xmlName); } else if (description instanceof CustomDomChildrenDescriptionImpl) { - List stubs = myStub.getChildrenStubs(); - return ContainerUtil.mapNotNull(stubs, (NullableFunction)stub -> { - if (stub instanceof ElementStub && stub.isCustom()) { - EvaluatedXmlName name = new DummyEvaluatedXmlName(stub.getName(), ""); + List stubs = myStub.getChildrenStubs(); + return ContainerUtil.mapNotNull(stubs, (NullableFunction)stub -> { + if (stub instanceof ElementStub && ((ElementStub)stub).isCustom()) { + EvaluatedXmlName name = new DummyEvaluatedXmlName(((ElementStub)stub).getName(), ""); return new CollectionElementInvocationHandler(name, (CustomDomChildrenDescriptionImpl)description, myManager, (ElementStub)stub).getProxy(); } return null; @@ -877,6 +877,17 @@ public abstract class DomInvocationHandler addIncludedElements(List elements, XmlName xmlName) { + assert myStub != null; + List result = new SmartList<>(elements); + for (Stub stub : myStub.getChildrenStubs()) { + if (stub instanceof XIncludeStub) { + ((XIncludeStub)stub).resolve(this, result, xmlName); + } + } + return result; + } + private List getCollectionSubTags(@NotNull AbstractCollectionChildDescription description, @NotNull XmlTag tag) { if (description instanceof CollectionChildDescriptionImpl) { return ((CollectionChildDescriptionImpl)description).getCollectionSubTags(this, tag); diff --git a/xml/dom-impl/src/com/intellij/util/xml/stubs/DomElementTypeHolder.java b/xml/dom-impl/src/com/intellij/util/xml/stubs/DomElementTypeHolder.java index 91320a6e73be..8586ee062c80 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/stubs/DomElementTypeHolder.java +++ b/xml/dom-impl/src/com/intellij/util/xml/stubs/DomElementTypeHolder.java @@ -26,5 +26,6 @@ public class DomElementTypeHolder { SerializationManager.getInstance().registerSerializer(ElementStubSerializer.INSTANCE); SerializationManager.getInstance().registerSerializer(AttributeStubSerializer.INSTANCE); SerializationManager.getInstance().registerSerializer(FileStubSerializer.INSTANCE); + SerializationManager.getInstance().registerSerializer(XIncludeStub.ourSerializer); } } diff --git a/xml/dom-impl/src/com/intellij/util/xml/stubs/DomStub.java b/xml/dom-impl/src/com/intellij/util/xml/stubs/DomStub.java index 36753ae46c40..5eb0bf65b50f 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/stubs/DomStub.java +++ b/xml/dom-impl/src/com/intellij/util/xml/stubs/DomStub.java @@ -17,6 +17,7 @@ package com.intellij.util.xml.stubs; import com.intellij.openapi.util.Comparing; import com.intellij.psi.stubs.ObjectStubBase; +import com.intellij.psi.stubs.Stub; import com.intellij.util.SmartList; import com.intellij.util.io.StringRef; import com.intellij.util.xml.EvaluatedXmlNameImpl; @@ -50,10 +51,6 @@ public abstract class DomStub extends ObjectStubBase { myLocalName = localName; } - @NotNull - @Override - public abstract List getChildrenStubs(); - public String getName() { return myLocalName.getString(); } @@ -64,7 +61,7 @@ public abstract class DomStub extends ObjectStubBase { } public List getChildrenByName(final CharSequence name, @Nullable final String nsKey) { - final List stubs = getChildrenStubs(); + final List stubs = getChildrenStubs(); if (stubs.isEmpty()) { return Collections.emptyList(); } @@ -73,10 +70,10 @@ public abstract class DomStub extends ObjectStubBase { final List result = new SmartList<>(); //noinspection ForLoopReplaceableByForEach for (int i = 0, size = stubs.size(); i < size; i++) { - final DomStub stub = stubs.get(i); - if (XmlUtil.getLocalName(stub.getName()).equals(name) && - Comparing.equal(s, stub.getNamespaceKey())) { - result.add(stub); + final Stub stub = stubs.get(i); + if (stub instanceof DomStub && XmlUtil.getLocalName(((DomStub)stub).getName()).equals(name) && + Comparing.equal(s, ((DomStub)stub).getNamespaceKey())) { + result.add((DomStub)stub); } } return result; @@ -84,16 +81,16 @@ public abstract class DomStub extends ObjectStubBase { @Nullable public AttributeStub getAttributeStub(final XmlName name) { - final List stubs = getChildrenStubs(); + final List stubs = getChildrenStubs(); if (stubs.isEmpty()) { return null; } //noinspection ForLoopReplaceableByForEach for (int i = 0, size = stubs.size(); i < size; i++) { - final DomStub stub = stubs.get(i); + final Stub stub = stubs.get(i); if (stub instanceof AttributeStub && - stub.getName().equals(name.getLocalName())) { + ((AttributeStub)stub).getName().equals(name.getLocalName())) { return (AttributeStub)stub; } } @@ -102,10 +99,10 @@ public abstract class DomStub extends ObjectStubBase { @Nullable public ElementStub getElementStub(String name, int index) { - List stubs = getChildrenStubs(); + List stubs = getChildrenStubs(); int i = 0; - for (DomStub stub : stubs) { - if (stub instanceof ElementStub && name.equals(stub.getName()) && i++ == index) { + for (Stub stub : stubs) { + if (stub instanceof ElementStub && name.equals(((ElementStub)stub).getName()) && i++ == index) { return (ElementStub)stub; } } diff --git a/xml/dom-impl/src/com/intellij/util/xml/stubs/ElementStub.java b/xml/dom-impl/src/com/intellij/util/xml/stubs/ElementStub.java index a104af08e18a..739170716760 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/stubs/ElementStub.java +++ b/xml/dom-impl/src/com/intellij/util/xml/stubs/ElementStub.java @@ -17,6 +17,7 @@ package com.intellij.util.xml.stubs; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.stubs.ObjectStubSerializer; +import com.intellij.psi.stubs.Stub; import com.intellij.util.SmartList; import com.intellij.util.io.StringRef; import org.jetbrains.annotations.NotNull; @@ -29,7 +30,7 @@ import java.util.List; */ public class ElementStub extends DomStub { - private final List myChildren = new SmartList<>(); + private final List myChildren = new SmartList<>(); private final int myIndex; private final boolean myCustom; @@ -51,13 +52,13 @@ public class ElementStub extends DomStub { myValue = value; } - void addChild(DomStub child) { + void addChild(Stub child) { myChildren.add(child); } @NotNull @Override - public List getChildrenStubs() { + public List getChildrenStubs() { return myChildren; } diff --git a/xml/dom-impl/src/com/intellij/util/xml/stubs/StubParentStrategy.java b/xml/dom-impl/src/com/intellij/util/xml/stubs/StubParentStrategy.java index 0cdfd247d070..360d4d352bb0 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/stubs/StubParentStrategy.java +++ b/xml/dom-impl/src/com/intellij/util/xml/stubs/StubParentStrategy.java @@ -17,6 +17,7 @@ package com.intellij.util.xml.stubs; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Comparing; +import com.intellij.psi.stubs.Stub; import com.intellij.psi.xml.XmlElement; import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; @@ -76,7 +77,7 @@ public class StubParentStrategy implements DomParentStrategy { public XmlElement getXmlElement() { DomStub parentStub = myStub.getParentStub(); if (parentStub == null) return null; - List children = parentStub.getChildrenStubs(); + List children = parentStub.getChildrenStubs(); if (children.isEmpty()) return null; XmlTag parentTag = parentStub.getHandler().getXmlTag(); if (parentTag == null) return null; diff --git a/xml/dom-impl/src/com/intellij/util/xml/stubs/XIncludeStub.java b/xml/dom-impl/src/com/intellij/util/xml/stubs/XIncludeStub.java new file mode 100644 index 000000000000..a49f10c7a16a --- /dev/null +++ b/xml/dom-impl/src/com/intellij/util/xml/stubs/XIncludeStub.java @@ -0,0 +1,131 @@ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.util.xml.stubs; + +import com.intellij.openapi.fileTypes.PlainTextLanguage; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiFileFactory; +import com.intellij.psi.PsiFileSystemItem; +import com.intellij.psi.impl.source.PsiFileImpl; +import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceSet; +import com.intellij.psi.stubs.*; +import com.intellij.psi.xml.XmlFile; +import com.intellij.util.xml.DomElement; +import com.intellij.util.xml.DomElementVisitor; +import com.intellij.util.xml.XmlName; +import com.intellij.util.xml.impl.DomFileElementImpl; +import com.intellij.util.xml.impl.DomInvocationHandler; +import com.intellij.util.xmlb.JDOMXIncluder; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.regex.Matcher; + +public class XIncludeStub extends ObjectStubBase { + + private final String myHref; + private final String myXpointer; + + public XIncludeStub(ElementStub parent, String href, String xpointer) { + super(parent); + myHref = href; + myXpointer = xpointer; + parent.addChild(this); + } + + public String getHref() { + return myHref; + } + + public String getXpointer() { + return myXpointer; + } + + @NotNull + @Override + public List getChildrenStubs() { + return Collections.emptyList(); + } + + @Override + public ObjectStubSerializer getStubType() { + return ourSerializer; + } + + public void resolve(DomInvocationHandler parent, List included, XmlName xmlName) { + if (StringUtil.isEmpty(myHref) || StringUtil.isEmpty(myXpointer)) { + return; + } + Matcher matcher = JDOMXIncluder.XPOINTER_PATTERN.matcher(myXpointer); + if (!matcher.matches()) { + return; + } + String group = matcher.group(1); + matcher = JDOMXIncluder.CHILDREN_PATTERN.matcher(group); + if (!matcher.matches()) { + return; + } + String tagName = matcher.group(1); + + XmlFile file = parent.getFile(); + PsiFileImpl dummy = (PsiFileImpl)PsiFileFactory.getInstance(file.getProject()).createFileFromText(PlainTextLanguage.INSTANCE, myHref); + dummy.setOriginalFile(file); + PsiFileSystemItem resolve = new FileReferenceSet(dummy).resolve(); + if (!(resolve instanceof XmlFile)) { + return; + } + DomFileElementImpl element = parent.getManager().getFileElement((XmlFile)resolve); + if (element == null) { + return; + } + DomElement rootElement = element.getRootElement(); + if (!tagName.equals(rootElement.getXmlElementName())) { + return; + } + rootElement.acceptChildren(new DomElementVisitor() { + @Override + public void visitDomElement(DomElement element) { + if (element.getXmlElementName().equals(xmlName.getLocalName())) { + included.add(element); + } + } + }); + } + + @Override + public String toString() { + return "href=" + getHref() + " xpointer=" + getXpointer(); + } + + public final static ObjectStubSerializer ourSerializer = new ObjectStubSerializer() { + + @NotNull + @Override + public String getExternalId() { + return "XIncludeStub"; + } + + @Override + public void serialize(@NotNull XIncludeStub stub, @NotNull StubOutputStream dataStream) throws IOException { + dataStream.writeUTFFast(StringUtil.notNullize(stub.getHref())); + dataStream.writeUTFFast(StringUtil.notNullize(stub.getXpointer())); + } + + @NotNull + @Override + public XIncludeStub deserialize(@NotNull StubInputStream dataStream, ElementStub parentStub) throws IOException { + return new XIncludeStub(parentStub, dataStream.readUTFFast(), dataStream.readUTFFast()); + } + + @Override + public void indexStub(@NotNull XIncludeStub stub, @NotNull IndexSink sink) { + + } + + @Override + public String toString() { + return "XInclide"; + } + }; +} diff --git a/xml/dom-impl/src/com/intellij/util/xml/stubs/builder/DomStubBuilderVisitor.java b/xml/dom-impl/src/com/intellij/util/xml/stubs/builder/DomStubBuilderVisitor.java index f2b5e7e44e93..2ff840a86ece 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/stubs/builder/DomStubBuilderVisitor.java +++ b/xml/dom-impl/src/com/intellij/util/xml/stubs/builder/DomStubBuilderVisitor.java @@ -28,6 +28,8 @@ import com.intellij.util.xml.reflect.CustomDomChildrenDescription; import com.intellij.util.xml.reflect.DomChildrenDescription; import com.intellij.util.xml.stubs.AttributeStub; import com.intellij.util.xml.stubs.ElementStub; +import com.intellij.util.xml.stubs.XIncludeStub; +import com.intellij.xml.util.XmlIncludeHandler; import java.lang.reflect.Type; import java.util.HashMap; @@ -44,6 +46,13 @@ class DomStubBuilderVisitor { } void visitXmlElement(XmlElement element, ElementStub parent, int index) { + + if (XmlIncludeHandler.isXInclude(element)) { + XmlTag tag = (XmlTag)element; + new XIncludeStub(parent, tag.getAttributeValue("href"), tag.getAttributeValue("xpointer")); + return; + } + DomInvocationHandler handler = myManager.getDomHandler(element); if (handler == null || handler.getAnnotation(Stubbed.class) == null && !handler.getChildDescription().isStubbed()) return; diff --git a/xml/dom-tests/tests/com/intellij/util/xml/stubs/DomStubBuilderTest.java b/xml/dom-tests/tests/com/intellij/util/xml/stubs/DomStubBuilderTest.java index 1f9b828f1b5a..6e99c5875f04 100644 --- a/xml/dom-tests/tests/com/intellij/util/xml/stubs/DomStubBuilderTest.java +++ b/xml/dom-tests/tests/com/intellij/util/xml/stubs/DomStubBuilderTest.java @@ -21,6 +21,7 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.impl.PsiManagerEx; import com.intellij.psi.impl.source.PsiFileImpl; import com.intellij.psi.stubs.ObjectStubTree; +import com.intellij.psi.stubs.Stub; import com.intellij.psi.stubs.StubTreeLoader; import com.intellij.psi.xml.XmlFile; import com.intellij.testFramework.PlatformTestUtil; @@ -62,11 +63,11 @@ public class DomStubBuilderTest extends DomStubTest { final ElementStub rootStub = getRootStub("foo.xml"); assertEquals("", rootStub.getValue()); - final DomStub fooStub = assertOneElement(rootStub.getChildrenStubs()); + final Stub fooStub = assertOneElement(rootStub.getChildrenStubs()); final ElementStub fooElementStub = assertInstanceOf(fooStub, ElementStub.class); assertEquals("", fooElementStub.getValue()); - final DomStub idStub = ContainerUtil.getFirstItem(fooStub.getChildrenStubs()); + final Stub idStub = ContainerUtil.getFirstItem(fooStub.getChildrenStubs()); final ElementStub idElementStub = assertInstanceOf(idStub, ElementStub.class); assertEquals("foo", idElementStub.getValue()); } @@ -107,6 +108,7 @@ public class DomStubBuilderTest extends DomStubTest { myFixture.copyFileToProject("include.xml"); doBuilderTest("inclusion.xml", "File:foo\n" + " Element:foo\n" + + " XInclide:href=include.xml xpointer=xpointer(/foo/*)\n" + " Element:bar\n" + " Attribute:string:xxx\n" + " Attribute:int:666\n" + @@ -118,7 +120,7 @@ public class DomStubBuilderTest extends DomStubTest { DomFileElement element = DomManager.getDomManager(getProject()).getFileElement((XmlFile)file, Foo.class); assert element != null; - assertEquals(2, element.getRootElement().getBars().size()); + assertEquals(3, element.getRootElement().getBars().size()); } public static class TestExtender extends DomExtender {