DOM stubs: accessing attribute values

This commit is contained in:
Dmitry Avdeev
2012-08-10 14:23:25 +04:00
parent 999fcb6084
commit db22b1730b
12 changed files with 210 additions and 13 deletions
@@ -129,6 +129,9 @@ public class AttributeChildInvocationHandler extends DomInvocationHandler<Attrib
@Nullable
protected String getValue() {
if (myStub != null) {
return myStub.getValue();
}
final XmlAttribute attribute = (XmlAttribute)getXmlElement();
if (attribute != null) {
final XmlAttributeValue value = attribute.getValueElement();
@@ -13,6 +13,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.Stubbed;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlElement;
import com.intellij.psi.xml.XmlFile;
@@ -21,10 +22,13 @@ import com.intellij.semantic.SemElement;
import com.intellij.semantic.SemKey;
import com.intellij.semantic.SemService;
import com.intellij.util.*;
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.StubParentStrategy;
import net.sf.cglib.proxy.AdvancedProxy;
import net.sf.cglib.proxy.InvocationHandler;
import org.jetbrains.annotations.NotNull;
@@ -62,10 +66,10 @@ 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 private final Stub myStub;
@Nullable protected final Stub myStub;
protected DomInvocationHandler(Type type, DomParentStrategy parentStrategy,
final EvaluatedXmlName tagName,
@NotNull final EvaluatedXmlName tagName,
final T childDescription,
final DomManagerImpl manager,
boolean dynamic,
@@ -576,6 +580,12 @@ public abstract class DomInvocationHandler<T extends AbstractDomChildDescription
@NotNull
final AttributeChildInvocationHandler getAttributeChild(final AttributeChildDescriptionImpl description) {
final EvaluatedXmlName evaluatedXmlName = createEvaluatedXmlName(description.getXmlName());
if (myStub != null && description.getAnnotation(Stubbed.class) != null) {
AttributeStub stub = myStub.getAttributeStub(description.getXmlName());
if (stub != null) {
return new AttributeChildInvocationHandler(evaluatedXmlName, description, myManager, new StubParentStrategy(stub), stub);
}
}
final XmlTag tag = getXmlTag();
if (tag != null) {
@@ -743,6 +753,16 @@ public abstract class DomInvocationHandler<T extends AbstractDomChildDescription
}
public List<? extends DomElement> getCollectionChildren(final AbstractCollectionChildDescription description, final NotNullFunction<DomInvocationHandler, List<XmlTag>> tagsGetter) {
if (myStub != null && description.getAnnotation(Stubbed.class) != null) {
XmlName xmlName = ((DomChildDescriptionImpl)description).getXmlName();
List<DomStub> stubs = myStub.getChildrenByName(xmlName);
return ContainerUtil.map(stubs, new Function<DomStub, DomElement>() {
@Override
public DomElement fun(DomStub stub) {
return stub.getOrCreateHandler((DomChildDescriptionImpl)description, myManager).getProxy();
}
});
}
XmlTag tag = getXmlTag();
if (tag == null) return Collections.emptyList();
@@ -43,6 +43,9 @@ public class DomRootInvocationHandler extends DomInvocationHandler<AbstractDomCh
}
}, fileElement.getManager(), true, stub);
myParent = fileElement;
if (stub != null) {
stub.setHandler(this);
}
}
public void undefineInternal() {
@@ -16,7 +16,6 @@
package com.intellij.util.xml.stubs;
import com.intellij.psi.stubs.ObjectStubSerializer;
import com.intellij.psi.stubs.Stub;
import com.intellij.util.io.StringRef;
import java.util.Collections;
@@ -40,7 +39,7 @@ public class AttributeStub extends DomStub {
}
@Override
public List<? extends Stub> getChildrenStubs() {
public List<DomStub> getChildrenStubs() {
return Collections.emptyList();
}
@@ -15,8 +15,19 @@
*/
package com.intellij.util.xml.stubs;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.stubs.ObjectStubBase;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.io.StringRef;
import com.intellij.util.xml.EvaluatedXmlNameImpl;
import com.intellij.util.xml.XmlName;
import com.intellij.util.xml.impl.DomChildDescriptionImpl;
import com.intellij.util.xml.impl.DomInvocationHandler;
import com.intellij.util.xml.impl.DomManagerImpl;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* @author Dmitry Avdeev
@@ -25,6 +36,7 @@ import com.intellij.util.io.StringRef;
public abstract class DomStub extends ObjectStubBase<DomStub> {
protected final StringRef myName;
private DomInvocationHandler myHandler;
public DomStub(DomStub parent, StringRef name) {
super(parent);
@@ -34,7 +46,54 @@ public abstract class DomStub extends ObjectStubBase<DomStub> {
myName = name;
}
public abstract List<DomStub> getChildrenStubs();
public String getName() {
return myName.getString();
}
public List<DomStub> getChildrenByName(final XmlName name) {
return ContainerUtil.filter(getChildrenStubs(), new Condition<DomStub>() {
@Override
public boolean value(DomStub stub) {
return stub.getName().equals(name.getLocalName());
}
});
}
@Nullable
public AttributeStub getAttributeStub(final XmlName name) {
return (AttributeStub)ContainerUtil.find(getChildrenStubs(), new Condition<DomStub>() {
@Override
public boolean value(DomStub o) {
return o instanceof AttributeStub && o.getName().equals(name.getLocalName());
}
});
}
public synchronized DomInvocationHandler getOrCreateHandler(DomChildDescriptionImpl description, DomManagerImpl manager) {
if (myHandler == null) {
XmlName name = description.getXmlName();
EvaluatedXmlNameImpl evaluatedXmlName = EvaluatedXmlNameImpl.createEvaluatedXmlName(name, name.getNamespaceKey(), true);
myHandler = new DomInvocationHandler(description.getType(), new StubParentStrategy(this), evaluatedXmlName, description, manager, false, this) {
@Override
protected void undefineInternal() {
}
@Override
protected XmlTag setEmptyXmlTag() {
return null;
}
};
}
return myHandler;
}
public DomInvocationHandler getHandler() {
return myHandler;
}
public void setHandler(DomInvocationHandler handler) {
myHandler = handler;
}
}
@@ -16,7 +16,6 @@
package com.intellij.util.xml.stubs;
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.Nullable;
@@ -40,7 +39,7 @@ public class ElementStub extends DomStub {
}
@Override
public List<? extends Stub> getChildrenStubs() {
public List<DomStub> getChildrenStubs() {
return myChildren;
}
@@ -0,0 +1,69 @@
/*
* Copyright 2000-2012 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.util.xml.stubs;
import com.intellij.psi.xml.XmlElement;
import com.intellij.util.xml.impl.DomInvocationHandler;
import com.intellij.util.xml.impl.DomParentStrategy;
import org.jetbrains.annotations.NotNull;
/**
* @author Dmitry Avdeev
* Date: 8/9/12
*/
public class StubParentStrategy implements DomParentStrategy {
private final DomStub myStub;
public StubParentStrategy(DomStub stub) {
myStub = stub;
}
@Override
public DomInvocationHandler getParentHandler() {
DomStub parentStub = myStub.getParentStub();
return parentStub == null ? null : parentStub.getHandler();
}
@Override
public XmlElement getXmlElement() {
return null;
}
@NotNull
@Override
public DomParentStrategy refreshStrategy(DomInvocationHandler handler) {
return this;
}
@NotNull
@Override
public DomParentStrategy setXmlElement(@NotNull XmlElement element) {
return this;
}
@NotNull
@Override
public DomParentStrategy clearXmlElement() {
return this;
}
@Override
public String checkValidity() {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
}
@@ -81,14 +81,14 @@ public class EvaluatedXmlNameImpl implements EvaluatedXmlName {
if (myEqualToParent != that.myEqualToParent) return false;
if (myNamespaceKey != null ? !myNamespaceKey.equals(that.myNamespaceKey) : that.myNamespaceKey != null) return false;
if (myXmlName != null ? !myXmlName.equals(that.myXmlName) : that.myXmlName != null) return false;
if (!myXmlName.equals(that.myXmlName)) return false;
return true;
}
@Override
public int hashCode() {
int result = myXmlName != null ? myXmlName.hashCode() : 0;
int result = myXmlName.hashCode();
result = 31 * result + (myNamespaceKey != null ? myNamespaceKey.hashCode() : 0);
result = 31 * result + (myEqualToParent ? 1 : 0);
return result;
+1 -1
View File
@@ -1,4 +1,4 @@
<foo>
<bar attribute="xxx"></bar>
<bar string="xxx" int="666"></bar>
<bar></bar>
</foo>
@@ -26,5 +26,8 @@ import com.intellij.util.xml.GenericAttributeValue;
public interface Bar extends DomElement {
@Stubbed
GenericAttributeValue getAttribute();
GenericAttributeValue<String> getString();
@Stubbed
GenericAttributeValue<Integer> getInt();
}
@@ -20,7 +20,8 @@ public class DomStubBuilderTest extends DomStubTest {
doTest("foo.xml", "File:foo\n" +
" Element:foo\n" +
" Element:bar\n" +
" Attribute:attribute:xxx\n" +
" Attribute:int:666\n" +
" Attribute:string:xxx\n" +
" Element:bar\n");
}
@@ -15,11 +15,17 @@
*/
package com.intellij.util.xml.stubs;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.PsiManagerImpl;
import com.intellij.psi.stubs.ObjectStubTree;
import com.intellij.psi.stubs.StubTreeLoader;
import com.intellij.psi.xml.XmlFile;
import com.intellij.util.xml.DomFileElement;
import com.intellij.util.xml.DomManager;
import java.util.List;
/**
* @author Dmitry Avdeev
* Date: 8/8/12
@@ -27,9 +33,44 @@ import com.intellij.util.xml.DomManager;
public class DomStubUsingTest extends DomStubTest {
public void testFoo() throws Exception {
PsiFile file = myFixture.configureByFile("foo.xml");
DomFileElement<Foo> fileElement = prepare("foo.xml");
PsiFile file;
file = fileElement.getFile();
assertFalse(file.getNode().isParsed());
Foo foo = fileElement.getRootElement();
List<Bar> bars = foo.getBars();
assertFalse(file.getNode().isParsed());
assertEquals(2, bars.size());
Bar bar = bars.get(0);
String value = bar.getString().getStringValue();
assertEquals("xxx", value);
Object o = bar.getString().getValue();
assertEquals("xxx", o);
Integer integer = bar.getInt().getValue();
assertEquals(666, integer.intValue());
assertFalse(file.getNode().isParsed());
}
private DomFileElement<Foo> prepare(String path) {
PsiFile file = myFixture.configureByFile(path);
assertFalse(file.getNode().isParsed());
VirtualFile virtualFile = file.getVirtualFile();
ObjectStubTree tree = StubTreeLoader.getInstance().readOrBuild(getProject(), virtualFile, file);
assertNotNull(tree);
((PsiManagerImpl)getPsiManager()).cleanupForNextTest();
file = getPsiManager().findFile(virtualFile);
assertFalse(file.getNode().isParsed());
DomFileElement<Foo> fileElement = DomManager.getDomManager(getProject()).getFileElement((XmlFile)file, Foo.class);
assertNotNull(fileElement);
Foo foo = fileElement.getRootElement();
return fileElement;
}
}