blob: 60cb32b093cb5de9488fb99a9d3602ddab8fcc53 [file] [log] [blame]
/*
* Copyright 2000-2014 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;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.psi.PsiElement;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.xml.events.DomEvent;
import com.intellij.util.xml.impl.DomTestCase;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author peter
*/
public class DomChildrenTest extends DomTestCase {
private MyElement createElement(final String xml) throws IncorrectOperationException {
return createElement(xml, MyElement.class);
}
public void testGetChild() throws Throwable {
final MyElement element = createElement("<a>" + "<child>foo</child>" + "</a>");
element.toString();
assertEquals("foo", element.getMyChild().getValue());
assertEquals("foo", element.getChild().getValue());
assertSame(element.getChild(), element.getMyChild());
assertNull(element.getChild239().getXmlTag());
}
public void testGetChild239() throws Throwable {
final MyElement element = createElement("<a><child/><child-239/></a>");
assertNotNull(element.getChild239().getXmlTag());
}
public void testGetChildren() throws Throwable {
final MyElement element = createElement("<a>" + "<child-element>foo</child-element>" + "<child-element>bar</child-element>" + "</a>");
assertSubBars(element.getMyChildren());
assertSubBars(element.getChildElements());
assertSubBars(element.getMyChildren2());
}
public void testGetChildrenMultipleTimes() throws Throwable {
final MyElement element = createElement("<a>" + "<child-element>" + " <child-element/>" + "</child-element>" + "</a>");
for (int i = 0; i < 239; i++) {
final List<MyElement> children = element.getChildElements();
final MyElement child = children.get(0);
final List<MyElement> children1 = child.getChildElements();
final MyElement child1 = children1.get(0);
assertNotNull(child1.getChild());
}
}
private void assertSubBars(final List<? extends MyElement> subBars) {
assertEquals(2, subBars.size());
assertEquals("foo", subBars.get(0).getValue());
assertEquals("bar", subBars.get(1).getValue());
}
public void testClassChoosers() throws Throwable {
getTypeChooserManager().registerTypeChooser(MyAbstractElement.class, new MyTypeChooser());
try {
MyElement element = createElement("<a>" +
"<abstract-child>Foo</abstract-child>" +
"<abstract-element>Foo</abstract-element>" +
"<abstract-element>bar</abstract-element>" + "</a>");
assertFalse(element.getAbstractChild()instanceof MyBarConcreteElement);
final List<MyAbstractElement> abstractElements = element.getAbstractElements();
assertTrue(abstractElements.get(0)instanceof MyFooConcreteElement);
assertTrue(abstractElements.get(1)instanceof MyBarConcreteElement);
}
finally {
getTypeChooserManager().unregisterTypeChooser(MyAbstractElement.class);
}
}
public void testAddConcreteElements() throws Throwable {
getTypeChooserManager().registerTypeChooser(MyAbstractElement.class, new MyTypeChooser());
try {
MyElement element = createElement("<a/>");
element.addBarChild();
element.addFooChild();
element.addAbstractElement(MyFooConcreteElement.class);
element.addAbstractElement(MyFooConcreteElement.class, 1);
element.addAbstractElement(2, MyBarConcreteElement.class);
Class[] classes = new Class[]{MyBarConcreteElement.class, MyFooConcreteElement.class, MyBarConcreteElement.class,
MyFooConcreteElement.class, MyFooConcreteElement.class};
final List<MyAbstractElement> abstractElements = element.getAbstractElements();
for (int i = 0; i < abstractElements.size(); i++) {
MyAbstractElement abstractElement = abstractElements.get(i);
assertTrue(String.valueOf(i) + " " + abstractElement.getClass(), classes[i].isInstance(abstractElement));
assertEquals(String.valueOf(i), classes[i].getName(), abstractElement.getXmlTag().getAttributeValue("foo"));
}
}
finally {
getTypeChooserManager().unregisterTypeChooser(MyAbstractElement.class);
}
}
public void testIndexedChild() throws Throwable {
MyElement element = createElement("<a>" + "<child>foo</child>" + "<child>bar</child>" + "</a>");
assertCached(element.getChild2(), element.getXmlTag().getSubTags()[1]);
assertEquals(0, element.getChildElements().size());
}
public void testDefiningIndexedChild() throws Throwable {
final MyElement element = createElement("<a/>");
final XmlTag tag = element.getChild2().ensureTagExists();
final XmlTag[] subTags = element.getXmlTag().findSubTags("child");
assertEquals(2, subTags.length);
assertSame(tag, subTags[1]);
assertCached(element.getChild(), subTags[0]);
final DomElement element1 = element.getChild();
putExpected(new DomEvent(element1, true));
final DomElement element2 = element.getChild().getAttr();
putExpected(new DomEvent(element2, true));
final DomElement element3 = element.getChild().isGenericValue();
putExpected(new DomEvent(element3, true));
final DomElement element4 = element.getChild2();
putExpected(new DomEvent(element4, true));
final DomElement element5 = element.getChild2().getAttr();
putExpected(new DomEvent(element5, true));
final DomElement element6 = element.getChild2().isGenericValue();
putExpected(new DomEvent(element6, true));
assertResultsAndClear();
}
public void testAddChild() throws Throwable {
final MyElement element = createElement("<a><child-element/></a>");
assertEquals(1, element.getChildElements().size());
final MyElement firstChild = element.getChildElements().get(0);
final XmlTag firstChildTag = element.getXmlTag().findSubTags("child-element")[0];
assertCached(firstChild, firstChildTag);
final MyElement child = element.addChildElement();
assertEquals(Arrays.asList(firstChild, child), element.getChildElements());
final XmlTag childTag = element.getXmlTag().findSubTags("child-element")[1];
putExpected(new DomEvent(element, false));
final DomElement element1 = child.getAttr();
putExpected(new DomEvent(element1, true));
final DomElement element2 = child.isGenericValue();
putExpected(new DomEvent(element2, true));
final MyElement newChild = element.addChildElement(1);
assertEquals(Arrays.asList(firstChild, newChild, child), element.getChildElements());
final XmlTag newChildTag = element.getXmlTag().findSubTags("child-element")[1];
putExpected(new DomEvent(element, false));
final DomElement element3 = newChild.getAttr();
putExpected(new DomEvent(element3, true));
final DomElement element4 = newChild.isGenericValue();
putExpected(new DomEvent(element4, true));
final MyElement lastChild = element.addChildElement(239);
assertEquals(Arrays.asList(firstChild, newChild, child, lastChild), element.getChildElements());
final XmlTag lastChildTag = element.getXmlTag().findSubTags("child-element")[3];
putExpected(new DomEvent(element, false));
final DomElement element5 = lastChild.getAttr();
putExpected(new DomEvent(element5, true));
final DomElement element6 = lastChild.isGenericValue();
putExpected(new DomEvent(element6, true));
assertResultsAndClear();
assertCached(firstChild, firstChildTag);
assertCached(newChild, newChildTag);
assertCached(child, childTag);
assertCached(lastChild, lastChildTag);
assertSame(firstChildTag, element.getXmlTag().findSubTags("child-element")[0]);
assertSame(newChildTag, element.getXmlTag().findSubTags("child-element")[1]);
assertSame(childTag, element.getXmlTag().findSubTags("child-element")[2]);
assertSame(lastChildTag, element.getXmlTag().findSubTags("child-element")[3]);
}
public void testUndefineCollectionChild() throws Throwable {
final MyElement element = createElement("<a><child-element/><child-element/><child-element/></a>");
final MyElement child1 = element.getChildElements().get(0);
final MyElement child2 = element.getChildElements().get(1);
final MyElement child3 = element.getChildElements().get(2);
final List<XmlTag> oldChildren = new ArrayList<XmlTag>(Arrays.asList(element.getXmlTag().getSubTags()));
assertTrue(child2.isValid());
assertEquals(element, child2.getParent());
WriteCommandAction.runWriteCommandAction(null, new Runnable() {
@Override
public void run() {
child2.undefine();
assertFalse(child2.isValid());
oldChildren.remove(1);
assertEquals(oldChildren, Arrays.asList(element.getXmlTag().getSubTags()));
assertEquals(Arrays.asList(child1, child3), element.getChildElements());
assertCached(child1, element.getXmlTag().findSubTags("child-element")[0]);
assertCached(child3, element.getXmlTag().findSubTags("child-element")[1]);
}
});
myCallRegistry.putExpected(new DomEvent(element, false));
myCallRegistry.assertResultsAndClear();
}
public void testUndefineFixedChild() throws Throwable {
new WriteCommandAction.Simple(getProject()) {
@Override
protected void run() throws Throwable {
final MyElement child = createElement("<a><child/></a>").getChild();
incModCount();
child.ensureTagExists();
assertTrue(child.isValid());
assertNotNull(child.getXmlElement());
incModCount();
child.undefine();
assertTrue(child.isValid());
assertNull(child.getXmlElement());
child.ensureTagExists();
incModCount();
assertTrue(child.isValid());
assertNotNull(child.getXmlElement());
}
}.execute().throwException();
}
public void testAttributes() throws Throwable {
final MyElement element = createElement("<a/>");
final GenericAttributeValue<String> attr = element.getAttr();
assertSame(element.getXmlTag(), attr.getXmlTag());
assertNull(attr.getValue());
assertNull(attr.getXmlAttribute());
assertEquals(attr, element.getAttr());
attr.setValue("239");
assertEquals("239", attr.getValue());
final XmlAttribute attribute = element.getXmlTag().getAttribute("attr", null);
assertSame(attribute, attr.getXmlAttribute());
assertSame(attribute, attr.getXmlElement());
assertSame(attribute, attr.ensureXmlElementExists());
assertSame(attribute.getValueElement(), attr.getXmlAttributeValue());
attr.setValue(null);
assertFalse(attribute.isValid());
assertNull(element.getXmlTag().getAttributeValue("attr"));
assertNull(attr.getValue());
assertNull(attr.getXmlAttribute());
}
public void testUndefineLastFixedChildWithNotEmptyCollection() throws Throwable {
new WriteCommandAction.Simple(getProject()) {
@Override
protected void run() throws Throwable {
final MyElement element = createElement("<a>" + "<child>1</child>" + "<child attr=\"\">2</child>" + "<child/></a>");
final MyElement child = element.getChild();
final MyElement child2 = element.getChild2();
assertEquals("", child2.getAttr().getValue());
assertTrue(child.isValid());
assertTrue(child2.isValid());
child.undefine();
assertTrue(child.isValid());
assertTrue(child2.isValid());
assertTrue(child2.equals(element.getChild2()));
assertNotNull(child.getXmlTag());
child2.undefine();
assertTrue(child.isValid());
assertTrue(child2.isValid());
assertTrue(child2.equals(element.getChild2()));
assertEquals(child, element.getChild());
assertEquals("", child.getValue());
assertNull(element.getChild2().getValue());
myCallRegistry.putExpected(new DomEvent(child, false));
myCallRegistry.putExpected(new DomEvent(child2, false));
myCallRegistry.assertResultsAndClear();
}
}.execute().throwException();
}
public void testUndefineNotLastFixedChild() throws Throwable {
new WriteCommandAction.Simple(getProject()) {
@Override
protected void run() throws Throwable {
final MyElement element = createElement("<a>" + "<child>1</child>" + "<child attr=\"\">2</child>" + "</a>");
final MyElement child = element.getChild();
final MyElement child2 = element.getChild2();
assertTrue(child.isValid());
assertTrue(child2.isValid());
child.undefine();
assertTrue(child.isValid());
assertTrue(child2.isValid());
assertEquals(element, child.getParent());
assertEquals(element, child2.getParent());
assertEquals(child, element.getChild());
assertEquals(child2, element.getChild2());
myCallRegistry.putExpected(new DomEvent(child, false));
myCallRegistry.assertResultsAndClear();
XmlTag[] subTags = element.getXmlTag().getSubTags();
assertEquals(2, subTags.length);
assertCached(child, subTags[0]);
assertCached(child2, subTags[1]);
}
}.execute().throwException();
}
public void testUndefineLastFixedChild() throws Throwable {
new WriteCommandAction.Simple(getProject()) {
@Override
protected void run() throws Throwable {
final MyElement element =
createElement("<a>" + "<child>1</child>" + "<child attr=\"\">2</child>" + "<child attr=\"\">2</child>" + "</a>");
final MyElement child = element.getChild();
final MyElement child2 = element.getChild2();
child2.undefine();
myCallRegistry.putExpected(new DomEvent(child2, false));
myCallRegistry.assertResultsAndClear();
XmlTag[] subTags = element.getXmlTag().getSubTags();
assertTrue(child.isValid());
assertTrue(child2.isValid());
assertEquals(1, subTags.length);
assertNull(child2.getXmlTag());
assertEquals(element, child.getParent());
assertEquals(element, child2.getParent());
}
}.execute().throwException();
}
public void testUndefineFixedChildWithNoTag() throws Throwable {
new WriteCommandAction.Simple(getProject()) {
@Override
protected void run() throws Throwable {
final MyElement element = createElement("<a/>");
final MyElement child = element.getChild();
child.undefine();
myCallRegistry.assertResultsAndClear();
assertTrue(child.isValid());
assertEquals(0, element.getXmlTag().getSubTags().length);
assertSame(element, child.getParent());
}
}.execute().throwException();
}
public void testGenericValuesCollection() throws Throwable {
final MyElement element = createElement("<a><generic-child>239</generic-child></a>");
assertEquals(239, (int)element.getGenericChildren().get(0).getValue());
}
public void testIsBooleanGenericValueMethod() throws Throwable {
assertNull(createElement("").isGenericValue().getValue());
}
public void testGetParentOfType() throws Throwable {
getTypeChooserManager().registerTypeChooser(MyAbstractElement.class, new MyTypeChooser());
try {
final MyElement element = createElement("");
final MyAbstractElement abstractChild = element.getAbstractChild();
final MyElement child239 = abstractChild.getChild239();
assertSame(child239, child239.getParentOfType(MyElement.class, false));
assertSame(abstractChild, child239.getParentOfType(MyElement.class, true));
assertSame(abstractChild, child239.getParentOfType(MyAbstractElement.class, true));
assertSame(DomUtil.getFileElement(element), child239.getParentOfType(DomFileElement.class, false));
assertSame(DomUtil.getFileElement(element), DomUtil.getFileElement(element).getParentOfType(DomFileElement.class, false));
assertNull(DomUtil.getFileElement(element).getParentOfType(DomFileElement.class, true));
}
finally {
getTypeChooserManager().unregisterTypeChooser(MyAbstractElement.class);
}
}
public void testChildrenValidAfterUndefine() throws Throwable {
final MyElement element = createElement("<a><child/></a>");
final MyElement child = element.getChild();
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
element.undefine();
}
});
assertTrue(element.isValid());
assertFalse(child.isValid());
}
public void testGetCompositeCollection() throws Throwable {
final MyElement element = createElement("");
final MyFooConcreteElement foo = element.addFooChild();
final MyElement child = element.addChildElement();
final MyBarConcreteElement bar = element.addBarChild();
assertEquals(Arrays.asList(foo, bar, child), element.getCompositeList());
}
public void testAddToCompositeCollection() throws Throwable {
final MyElement element = createElement("");
final MyBarConcreteElement bar = element.addBarComposite();
final MyElement child = element.addChildComposite();
final MyElement child2 = element.addChildElement();
final MyFooConcreteElement foo1 = element.addFooComposite(2);
final MyFooConcreteElement foo2 = element.addFooComposite(0);
assertEquals(Arrays.asList(foo2, bar, child, foo1, child2), element.getCompositeList());
}
public void testCreateElementWithRequiredChild() throws Throwable {
final MyElement element = createElement("").addChildElement();
assertNotNull(element.getAttr().getXmlAttribute());
assertNotNull(element.isGenericValue().getXmlTag());
assertTrue(element.getMyChildren2().isEmpty());
}
public void testSetRequiredChildValue() throws Throwable {
final MyElement element = createElement("<a/>").getChild();
element.isGenericValue().setStringValue("true");
assertEquals(1, element.getXmlTag().findSubTags("generic-value").length);
assertTrue(element.isGenericValue().getValue());
}
public void testGetFixedPath() throws Throwable {
final MyElement element = createElement("");
final MyElement child2 = element.getChild2();
assertEquals(Arrays.asList("getChild2", "getChild2", "getChild239", "getAbstractChild", "getChild2"),
getFixedPath(child2.getChild2().getChild239().getAbstractChild().getChild2()));
assertEquals(Arrays.asList("getChild2"), getFixedPath(child2));
assertEquals(Arrays.asList(), getFixedPath(element));
assertEquals(Arrays.asList("getAttr"), getFixedPath(element.getAttr()));
assertNull(DomUtil.getFixedPath(element.addChildElement().getChild()));
}
public void testGetDomElementCustomChild() throws Throwable {
final MyElement myElement = createElement("<a><abstract-child/><generic-value/><bar/></a>", MyElement.class);
final XmlTag tag = myElement.getXmlTag();
assertEquals(1, myElement.getCustomChildren().size());
assertInstanceOf(getDomManager().getDomElement(tag.getSubTags()[0]), MyAbstractElement.class);
assertInstanceOf(getDomManager().getDomElement(tag.getSubTags()[1]), GenericDomValue.class);
assertInstanceOf(getDomManager().getDomElement(tag.getSubTags()[2]), MyElement.class);
}
public void testNoChildrenForMalformedTags() throws Throwable {
final MyElement myElement = createElement("<a><</a>", MyElement.class);
final XmlTag tag = myElement.getXmlTag();
assertEquals(0, myElement.getCustomChildren().size());
}
public void testCustomNameChildren() throws Throwable {
final MyElement myElement = createElement("<a><foo/><bar/><foo xmlns=\"z\"/></a>", MyElement.class);
final XmlTag tag = myElement.getXmlTag();
final List<MyElement> customChildren = myElement.getCustomChildren();
assertOrderedEquals(customChildren, myElement.getGenericInfo().getCustomNameChildrenDescription().get(0).getValues(myElement));
assertOrderedCollection(customChildren, new Consumer<MyElement>() {
@Override
public void consume(final MyElement element) {
assertInstanceOf(element, MyElement.class);
assertEquals(tag.getSubTags()[0], element.getXmlTag());
}
}, new Consumer<MyElement>() {
@Override
public void consume(final MyElement element) {
assertInstanceOf(element, MyElement.class);
assertEquals(tag.getSubTags()[1], element.getXmlTag());
}
}, new Consumer<MyElement>() {
@Override
public void consume(final MyElement element) {
assertInstanceOf(element, MyElement.class);
assertEquals(tag.getSubTags()[2], element.getXmlTag());
}
});
}
private List<String> getFixedPath(final DomElement element) {
return ContainerUtil.map2List(DomUtil.getFixedPath(element), new Function<JavaMethod, String>() {
@Override
public String fun(final JavaMethod s) {
return s.getName();
}
});
}
public void testElementsWithoutXmlGetItLater() throws Throwable {
final MyElement element = createElement("<a/>");
final MyElement child = element.getChild();
final MyElement child2 = element.getChild2();
final PsiElement tag = element.getXmlTag().add(createTag("<child/>"));
incModCount();
assertTrue(child.isValid());
assertEquals(tag, child.getXmlTag());
assertTrue(child2.isValid());
assertNull(child2.getXmlTag());
}
public void testHigherOrderFixedsAreNotCustom() {
final MyElement element = createElement("<a><child/><child/><child/></a>");
XmlTag third = element.getXmlTag().getSubTags()[2];
assertNull(getDomManager().getDomElement(third));
assertEmpty(element.getCustomChildren());
}
public interface MyElement extends DomElement {
@Attribute
@Required
GenericAttributeValue<String> getAttr();
String getValue();
@SubTag("child")
MyElement getMyChild();
MyElement getChild();
@SubTag(value = "child", index = 1)
MyElement getChild2();
MyElement getChild239();
@SubTagList("child-element")
List<? extends MyElement> getMyChildren();
@CustomChildren
List<MyElement> getCustomChildren();
@SubTagList("child-element")
@Required
List<MyElement> getMyChildren2();
List<MyElement> getChildElements();
MyElement addChildElement();
MyElement addChildElement(int index);
MyAbstractElement getAbstractChild();
List<MyAbstractElement> getAbstractElements();
@SubTagList("abstract-element")
MyBarConcreteElement addBarChild();
@SubTagList("abstract-element")
MyFooConcreteElement addFooChild();
<T extends MyAbstractElement> T addAbstractElement(Class<T> aClass);
<T extends MyAbstractElement> T addAbstractElement(Class<T> aClass, int index);
<T extends MyAbstractElement> T addAbstractElement(int index, Class<T> aClass);
List<GenericDomValue<Integer>> getGenericChildren();
@Required
GenericDomValue<Boolean> isGenericValue();
@SubTagsList({"child-element", "abstract-element"})
List<MyElement> getCompositeList();
@SubTagsList(value = {"child-element", "abstract-element"}, tagName = "abstract-element")
MyBarConcreteElement addBarComposite();
@SubTagsList(value = {"child-element", "abstract-element"}, tagName = "child-element")
MyElement addChildComposite();
@SubTagsList(value = {"child-element", "abstract-element"}, tagName = "abstract-element")
MyFooConcreteElement addFooComposite(int index);
}
public interface MyAbstractElement extends MyElement {
}
public interface MyFooConcreteElement extends MyAbstractElement {
MyFooConcreteElement getFoo();
}
public interface MyBarConcreteElement extends MyAbstractElement {
}
private static class MyTypeChooser extends TypeChooser {
@Override
public Type chooseType(final XmlTag tag) {
return tag != null && tag.getText().contains("Foo") ? MyFooConcreteElement.class : MyBarConcreteElement.class;
}
@Override
public void distinguishTag(final XmlTag tag, final Type aClass) throws IncorrectOperationException {
tag.setAttribute("foo", ((Class)aClass).getName());
}
@Override
public Type[] getChooserTypes() {
return ArrayUtil.EMPTY_CLASS_ARRAY;
}
}
}