xinclude support for stubbed dom

This commit is contained in:
Dmitry Avdeev
2019-04-11 17:48:04 +03:00
parent 00edee2a03
commit fe57960112
8 changed files with 189 additions and 36 deletions
@@ -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<T extends AbstractDomChildDescriptionImpl, Stub extends DomStub> extends UserDataHolderBase implements InvocationHandler, DomElement,
SemElement {
public abstract class DomInvocationHandler<T extends AbstractDomChildDescriptionImpl, S extends DomStub> 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<T extends AbstractDomChildDescription
private final InvocationCache myInvocationCache;
private volatile Converter myScalarConverter = null;
private volatile SmartFMap<Method, Invocation> 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<T extends AbstractDomChildDescription
}
@Nullable
public Stub getStub() {
public S getStub() {
return myStub;
}
@@ -839,13 +837,15 @@ public abstract class DomInvocationHandler<T extends AbstractDomChildDescription
if (description instanceof DomChildDescriptionImpl) {
XmlName xmlName = ((DomChildDescriptionImpl)description).getXmlName();
List<DomStub> stubs = myStub.getChildrenByName(xmlName.getLocalName(), xmlName.getNamespaceKey());
return ContainerUtil.map(stubs, stub -> stub.getOrCreateHandler((DomChildDescriptionImpl)description, myManager).getProxy());
List<DomElement> elements =
ContainerUtil.map(stubs, stub -> stub.getOrCreateHandler((DomChildDescriptionImpl)description, myManager).getProxy());
return addIncludedElements(elements, xmlName);
}
else if (description instanceof CustomDomChildrenDescriptionImpl) {
List<DomStub> stubs = myStub.getChildrenStubs();
return ContainerUtil.mapNotNull(stubs, (NullableFunction<DomStub, DomElement>)stub -> {
if (stub instanceof ElementStub && stub.isCustom()) {
EvaluatedXmlName name = new DummyEvaluatedXmlName(stub.getName(), "");
List<? extends Stub> stubs = myStub.getChildrenStubs();
return ContainerUtil.mapNotNull(stubs, (NullableFunction<Stub, DomElement>)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<T extends AbstractDomChildDescription
return Collections.unmodifiableList(elements);
}
private List<? extends DomElement> addIncludedElements(List<? extends DomElement> elements, XmlName xmlName) {
assert myStub != null;
List<DomElement> result = new SmartList<>(elements);
for (Stub stub : myStub.getChildrenStubs()) {
if (stub instanceof XIncludeStub) {
((XIncludeStub)stub).resolve(this, result, xmlName);
}
}
return result;
}
private List<XmlTag> getCollectionSubTags(@NotNull AbstractCollectionChildDescription description, @NotNull XmlTag tag) {
if (description instanceof CollectionChildDescriptionImpl) {
return ((CollectionChildDescriptionImpl)description).getCollectionSubTags(this, tag);
@@ -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);
}
}
@@ -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<DomStub> {
myLocalName = localName;
}
@NotNull
@Override
public abstract List<DomStub> getChildrenStubs();
public String getName() {
return myLocalName.getString();
}
@@ -64,7 +61,7 @@ public abstract class DomStub extends ObjectStubBase<DomStub> {
}
public List<DomStub> getChildrenByName(final CharSequence name, @Nullable final String nsKey) {
final List<DomStub> stubs = getChildrenStubs();
final List<? extends Stub> stubs = getChildrenStubs();
if (stubs.isEmpty()) {
return Collections.emptyList();
}
@@ -73,10 +70,10 @@ public abstract class DomStub extends ObjectStubBase<DomStub> {
final List<DomStub> 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<DomStub> {
@Nullable
public AttributeStub getAttributeStub(final XmlName name) {
final List<DomStub> stubs = getChildrenStubs();
final List<? extends Stub> 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<DomStub> {
@Nullable
public ElementStub getElementStub(String name, int index) {
List<DomStub> stubs = getChildrenStubs();
List<? extends Stub> 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;
}
}
@@ -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<DomStub> myChildren = new SmartList<>();
private final List<Stub> 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<DomStub> getChildrenStubs() {
public List<? extends Stub> getChildrenStubs() {
return myChildren;
}
@@ -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<DomStub> children = parentStub.getChildrenStubs();
List<? extends Stub> children = parentStub.getChildrenStubs();
if (children.isEmpty()) return null;
XmlTag parentTag = parentStub.getHandler().getXmlTag();
if (parentTag == null) return null;
@@ -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<ElementStub> {
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<? extends Stub> getChildrenStubs() {
return Collections.emptyList();
}
@Override
public ObjectStubSerializer getStubType() {
return ourSerializer;
}
public void resolve(DomInvocationHandler parent, List<DomElement> 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<DomElement> 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<XIncludeStub, ElementStub>() {
@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";
}
};
}
@@ -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;
@@ -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<Foo> 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<Bar> {