From b9b616eb908cba5a2f850a63d5cd22985fabb81f Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Mon, 6 Aug 2012 17:11:59 +0400 Subject: [PATCH] dom-tests moved to community --- .idea/modules.xml | 1 + community-main.iml | 1 + xml/dom-tests/dom-tests.iml | 23 + xml/dom-tests/testData/stubs/foo.xml | 1 + .../util/xml/DelimitedListProcessorTest.java | 29 + .../intellij/util/xml/DomAnnotationsTest.java | 65 ++ .../com/intellij/util/xml/DomBasicsTest.java | 606 +++++++++++++++ .../intellij/util/xml/DomChildrenTest.java | 614 ++++++++++++++++ .../util/xml/DomConcurrencyStressTest.java | 233 ++++++ .../intellij/util/xml/DomExtensionsTest.java | 310 ++++++++ .../util/xml/DomFileDescriptionTest.java | 232 ++++++ .../util/xml/DomHardCoreTestCase.java | 107 +++ .../util/xml/DomHighlightingLiteTest.java | 329 +++++++++ .../intellij/util/xml/DomIncludesTest.java | 229 ++++++ .../util/xml/DomModelMergingTest.java | 81 ++ .../intellij/util/xml/DomNamespacesTest.java | 354 +++++++++ .../intellij/util/xml/DomPerformanceTest.java | 208 ++++++ .../util/xml/DomReferenceInjectorTest.java | 456 ++++++++++++ .../intellij/util/xml/DomReferencesTest.java | 249 +++++++ .../intellij/util/xml/DomSaxParserTest.java | 118 +++ .../util/xml/DomSimpleValuesTest.java | 539 ++++++++++++++ .../com/intellij/util/xml/DomTestCase.java | 135 ++++ .../util/xml/DomVirtualFileEventsTest.java | 109 +++ .../com/intellij/util/xml/MockDomElement.java | 169 +++++ .../intellij/util/xml/MockDomFileElement.java | 219 ++++++ .../util/xml/MockGenericAttributeValue.java | 54 ++ .../intellij/util/xml/NameStrategyTest.java | 44 ++ .../com/intellij/util/xml/ProxyTest.java | 171 +++++ .../SimpleValuesIncrementalUpdateTest.java | 81 ++ .../util/xml/StringBufferConverter.java | 17 + .../xml/impl/IncrementalUpdateEventsTest.java | 133 ++++ .../xml/impl/TreeIncrementalUpdateTest.java | 694 ++++++++++++++++++ .../intellij/util/xml/stubs/DomStubsTest.java | 57 ++ .../com/intellij/util/xml/stubs/Foo.java | 10 + 34 files changed, 6678 insertions(+) create mode 100644 xml/dom-tests/dom-tests.iml create mode 100644 xml/dom-tests/testData/stubs/foo.xml create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/DelimitedListProcessorTest.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/DomAnnotationsTest.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/DomBasicsTest.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/DomChildrenTest.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/DomConcurrencyStressTest.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/DomExtensionsTest.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/DomFileDescriptionTest.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/DomHardCoreTestCase.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/DomHighlightingLiteTest.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/DomIncludesTest.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/DomModelMergingTest.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/DomNamespacesTest.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/DomPerformanceTest.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/DomReferenceInjectorTest.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/DomReferencesTest.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/DomSaxParserTest.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/DomSimpleValuesTest.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/DomTestCase.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/DomVirtualFileEventsTest.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/MockDomElement.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/MockDomFileElement.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/MockGenericAttributeValue.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/NameStrategyTest.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/ProxyTest.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/SimpleValuesIncrementalUpdateTest.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/StringBufferConverter.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/impl/IncrementalUpdateEventsTest.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/impl/TreeIncrementalUpdateTest.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/stubs/DomStubsTest.java create mode 100644 xml/dom-tests/tests/com/intellij/util/xml/stubs/Foo.java diff --git a/.idea/modules.xml b/.idea/modules.xml index 9ed9a88d02a1..7da03cdac9b9 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -36,6 +36,7 @@ + diff --git a/community-main.iml b/community-main.iml index f5341e75ff3d..2993c87f49d5 100644 --- a/community-main.iml +++ b/community-main.iml @@ -91,6 +91,7 @@ + diff --git a/xml/dom-tests/dom-tests.iml b/xml/dom-tests/dom-tests.iml new file mode 100644 index 000000000000..ca2f81f92c66 --- /dev/null +++ b/xml/dom-tests/dom-tests.iml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xml/dom-tests/testData/stubs/foo.xml b/xml/dom-tests/testData/stubs/foo.xml new file mode 100644 index 000000000000..d1bd00e347d2 --- /dev/null +++ b/xml/dom-tests/testData/stubs/foo.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/xml/dom-tests/tests/com/intellij/util/xml/DelimitedListProcessorTest.java b/xml/dom-tests/tests/com/intellij/util/xml/DelimitedListProcessorTest.java new file mode 100644 index 000000000000..a206e43b5804 --- /dev/null +++ b/xml/dom-tests/tests/com/intellij/util/xml/DelimitedListProcessorTest.java @@ -0,0 +1,29 @@ +package com.intellij.util.xml; + +import com.intellij.util.xml.converters.DelimitedListProcessor; +import junit.framework.TestCase; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @author Dmitry Avdeev + */ +public class DelimitedListProcessorTest extends TestCase { + + public void testProcessor() { + doTest("a; ; ", Arrays.asList("a", " ", " ")); + } + + private void doTest(final String text, final List expected) { + final ArrayList tokens = new ArrayList(); + new DelimitedListProcessor(";") { + @Override + protected void processToken(final int start, final int end, final boolean delimitersOnly) { + tokens.add(text.substring(start, end)); + } + }.processText(text); + assertEquals(expected, tokens); + } +} diff --git a/xml/dom-tests/tests/com/intellij/util/xml/DomAnnotationsTest.java b/xml/dom-tests/tests/com/intellij/util/xml/DomAnnotationsTest.java new file mode 100644 index 000000000000..f6d86922174a --- /dev/null +++ b/xml/dom-tests/tests/com/intellij/util/xml/DomAnnotationsTest.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2000-2006 JetBrains s.r.o. All Rights Reserved. + */ +package com.intellij.util.xml; + +import com.intellij.codeInspection.InspectionManager; +import com.intellij.lang.annotation.HighlightSeverity; +import com.intellij.openapi.fileTypes.StdFileTypes; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiFileFactory; +import com.intellij.psi.xml.XmlFile; +import com.intellij.psi.xml.XmlTag; +import com.intellij.util.xml.highlighting.*; + +import java.util.Arrays; + +/** + * @author peter + */ +public class DomAnnotationsTest extends DomTestCase{ + + @Override + protected T createElement(final String xml, final Class aClass) { + final String name = "a.xml"; + final XmlFile file = (XmlFile)PsiFileFactory.getInstance(getProject()).createFileFromText(name, StdFileTypes.XML, xml, 0, + true); + final XmlTag tag = file.getDocument().getRootTag(); + final String rootTagName = tag != null ? tag.getName() : "root"; + final T element = getDomManager().getFileElement(file, aClass, rootTagName).getRootElement(); + assertNotNull(element); + assertSame(tag, element.getXmlTag()); + return element; + } + + public void testResolveProblemsAreReportedOnlyOnce() throws Throwable { + final MyElement myElement = createElement("abc", MyElement.class); + + new MockDomInspection(MyElement.class).checkFile(DomUtil.getFile(myElement), InspectionManager.getInstance(getProject()), true); + final DomElementsProblemsHolder holder = DomElementAnnotationsManager.getInstance(getProject()).getProblemHolder(myElement); + + final DomElement element = myElement.getMyClass(); + assertEquals(0, holder.getProblems(myElement).size()); + assertEquals(0, holder.getProblems(myElement).size()); + assertEquals(1, holder.getProblems(element).size()); + assertEquals(1, holder.getProblems(element).size()); + assertEquals(1, holder.getProblems(myElement, true, true).size()); + assertEquals(1, holder.getProblems(myElement, true, true).size()); + } + + public void testMinSeverity() throws Throwable { + final MyElement element = createElement("", MyElement.class); + final DomElementsProblemsHolderImpl holder = new DomElementsProblemsHolderImpl(DomUtil.getFileElement(element)); + final DomElementProblemDescriptorImpl error = new DomElementProblemDescriptorImpl(element, "abc", HighlightSeverity.ERROR); + final DomElementProblemDescriptorImpl warning = new DomElementProblemDescriptorImpl(element, "abc", HighlightSeverity.WARNING); + holder.addProblem(error, MockDomInspection.class); + holder.addProblem(warning, MockDomInspection.class); + assertEquals(Arrays.asList(error), holder.getProblems(element, true, true, HighlightSeverity.ERROR)); + assertEquals(Arrays.asList(error, warning), holder.getProblems(element, true, true, HighlightSeverity.WARNING)); + } + + public interface MyElement extends DomElement{ + GenericDomValue getMyClass(); + } + +} diff --git a/xml/dom-tests/tests/com/intellij/util/xml/DomBasicsTest.java b/xml/dom-tests/tests/com/intellij/util/xml/DomBasicsTest.java new file mode 100644 index 000000000000..fb6829c7ab00 --- /dev/null +++ b/xml/dom-tests/tests/com/intellij/util/xml/DomBasicsTest.java @@ -0,0 +1,606 @@ +package com.intellij.util.xml; + +import com.intellij.mock.MockModule; +import com.intellij.openapi.application.Result; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.util.Factory; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.xml.XmlFile; +import com.intellij.psi.xml.XmlTag; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.xml.events.DomEvent; +import com.intellij.util.xml.impl.*; +import com.intellij.util.xml.reflect.DomAttributeChildDescription; +import com.intellij.util.xml.reflect.DomCollectionChildDescription; +import com.intellij.util.xml.reflect.DomFixedChildDescription; +import com.intellij.util.xml.reflect.DomGenericInfo; + +import java.lang.reflect.ParameterizedType; +import java.util.*; + +/** + * @author peter + */ +public class DomBasicsTest extends DomTestCase { + @Override + protected void invokeTestRunnable(final Runnable runnable) throws Exception { + new WriteCommandAction.Simple(null) { + @Override + protected void run() throws Throwable { + runnable.run(); + } + }.execute().throwException(); + } + + public void testFileElementCaching() throws Throwable { + final XmlFile file = createXmlFile(""); + final DomManagerImpl manager = getDomManager(); + final DomFileElementImpl fileElement = manager.getFileElement(file, DomElement.class, "a"); + assertEquals(fileElement, manager.getFileElement(file, DomElement.class, "a")); + assertCached(fileElement, file); + + assertEquals(fileElement.getRootElement(), fileElement.getRootElement()); + } + + public void testElementCaching() throws Throwable { + final MyElement element = createElement(""); + assertSame(element.getChild(), element.getChild()); + assertSame(element.getXmlTag().getSubTags()[0], element.getChild().getXmlTag()); + assertCached(element.getChild(), element.getChild().getXmlTag()); + } + + public void testGetParentAndRoot() throws Throwable { + final XmlFile file = createXmlFile(""); + final DomFileElementImpl fileElement = getDomManager().getFileElement(file, MyElement.class, "a"); + assertNull(fileElement.getParent()); + assertSame(fileElement, DomUtil.getFileElement(fileElement)); + + final MyElement rootElement = fileElement.getRootElement(); + assertSame(fileElement, rootElement.getParent()); + assertSame(fileElement, DomUtil.getFileElement(rootElement)); + + assertParent(rootElement.getFoo(), rootElement); + assertParent(rootElement.getChildElements().get(0), rootElement); + assertParent(rootElement.getChildElements().get(1), rootElement); + } + + private static void assertParent(final DomElement element, final DomElement parent) { + assertEquals(parent, element.getParent()); + } + + public void testEnsureTagExists() throws Throwable { + final MyElement element = createElement(""); + myCallRegistry.clear(); + final MyElement child = element.getChild(); + assertNull(child.getXmlTag()); + + child.ensureTagExists(); + final XmlTag[] subTags = element.getXmlTag().getSubTags(); + assertEquals(1, subTags.length); + final XmlTag childTag = subTags[0]; + assertEquals("child", childTag.getName()); + assertCached(child, childTag); + assertSame(child.getXmlTag(), childTag); + + final DomElement element1 = child; + myCallRegistry.putExpected(new DomEvent(element1, true)); + myCallRegistry.assertResultsAndClear(); + + final MyElement childElement = element.addChildElement(); + final XmlTag childElementTag = childElement.getXmlTag(); + assertSame(childElementTag, childElement.ensureTagExists()); + } + + public void testEnsureRootTagExists() throws Throwable { + final MyElement rootElement = createEmptyElement(); + myCallRegistry.clear(); + assertNull(rootElement.getXmlTag()); + rootElement.ensureTagExists(); + final DomElement element = rootElement; + myCallRegistry.putExpected(new DomEvent(element, true)); + + assertCached(rootElement, assertRootTag(rootElement)); + myCallRegistry.assertResultsAndClear(); + } + + private XmlTag assertRootTag(final DomElement rootElement) { + return assertRootTag(DomUtil.getFile(rootElement)); + } + + private static XmlTag assertRootTag(final XmlFile file) { + final XmlTag rootTag = file.getDocument().getRootTag(); + assertNotNull(rootTag); + assertEquals("root", rootTag.getName()); + return rootTag; + } + + protected MyElement createEmptyElement() throws IncorrectOperationException { + final XmlFile file = createXmlFile(""); + return getDomManager().getFileElement(file, MyElement.class, "root").getRootElement(); + } + + public void testFile() throws Throwable { + final XmlFile file = createXmlFile("foo"); + DomFileElementImpl fileElement = getDomManager().getFileElement(file, MyElement.class, "a"); + final MyElement rootElement = fileElement.getRootElement(); + assertNotNull(rootElement); + assertEquals("foo", rootElement.getValue()); + } + + public void testAcceptChidren() throws Throwable { + final MyElement element = createElement(""); + final Set visited = new HashSet(); + element.acceptChildren(new DomElementVisitor() { + @Override + public void visitDomElement(DomElement element) { + visited.add(element); + } + }); + final MyElement foo = element.getFoo(); + final MyElement child = element.getChild(); + final MyElement child1 = element.getChildElements().get(0); + final MyElement child2 = element.getChildElements().get(1); + final GenericDomValue genericValue = element.getGenericValue(); + assertSameElements(visited, foo, child, child1, child2, genericValue, element.getAttr()); + } + + public void testChildrenReflection() throws Throwable { + final MyElement element = + createElement(""); + final DomGenericInfo info = element.getGenericInfo(); + + final DomFixedChildDescription foo = info.getFixedChildDescription("foo"); + assertFixedChildDescription(foo, element.getFoo(), "foo"); + + final DomFixedChildDescription child = info.getFixedChildDescription("child"); + assertFixedChildDescription(child, element.getChild(), "child"); + + final DomFixedChildDescription genericChild = info.getFixedChildDescription("generic-value"); + assertGenericChildDescription(genericChild, element.getGenericValue(), "generic-value"); + + final DomCollectionChildDescription collectionChild = info.getCollectionChildDescription("child-element"); + assertEquals(element.getChildElements(), collectionChild.getValues(element)); + assertEquals("child-element", collectionChild.getXmlElementName()); + assertEquals(MyElement.class, collectionChild.getType()); + assertEquals(MyElement.class.getMethod("getChildElements"), collectionChild.getGetterMethod().getMethod()); + + assertEquals(new HashSet(Arrays.asList(foo, child, collectionChild, genericChild, + info.getAttributeChildrenDescriptions().get(0))), + new HashSet(info.getChildrenDescriptions())); + } + + private void assertFixedChildDescription(final DomFixedChildDescription description, + final DomElement child, + final String tagName) throws NoSuchMethodException { + assertEquals(1, description.getCount()); + assertEquals(Arrays.asList(child), description.getValues(child.getParent())); + assertEquals(tagName, description.getXmlElementName()); + assertEquals(MyElement.class, description.getType()); + assertEquals(JavaMethod.getMethod(MyElement.class, new JavaMethodSignature("get" + StringUtil.capitalize(tagName))), description.getGetterMethod(0)); + } + + private static void assertGenericChildDescription(final DomFixedChildDescription description, + final DomElement child, + final String tagName) throws NoSuchMethodException { + assertEquals(1, description.getCount()); + assertEquals(Arrays.asList(child), description.getValues(child.getParent())); + assertEquals(tagName, description.getXmlElementName()); + assertEquals(GenericDomValue.class, ((ParameterizedType)description.getType()).getRawType()); + assertEquals(JavaMethod.getMethod(MyElement.class, new JavaMethodSignature("getGenericValue")), description.getGetterMethod(0)); + } + + public void testGetDomElementType() throws Throwable { + final MyElement element = createElement(""); + assertEquals(MyElement.class.getMethod("getGenericValue").getGenericReturnType(), element.getGenericValue().getDomElementType()); + } + + public void testAddChildrenByReflection() throws Throwable { + final MyElement element = + createElement(""); + final DomGenericInfo info = element.getGenericInfo(); + final DomCollectionChildDescription collectionChild = info.getCollectionChildDescription("child-element"); + final List values = collectionChild.getValues(element); + + MyElement newChild = (MyElement) collectionChild.addValue(element); + List newChildren = Arrays.asList(values.get(0), newChild); + assertEquals(newChildren, element.getChildElements()); + assertEquals(newChildren, collectionChild.getValues(element)); + + MyElement lastChild = (MyElement) collectionChild.addValue(element, 0); + newChildren = Arrays.asList(lastChild, values.get(0), newChild); + assertEquals(newChildren, element.getChildElements()); + assertEquals(newChildren, collectionChild.getValues(element)); + } + + public void testGetPresentableName() throws Throwable { + assertCollectionPresentableName("Aaas", "aaa", DomNameStrategy.HYPHEN_STRATEGY); + assertCollectionPresentableName("Aaa Bbbs", "aaa-bbb", DomNameStrategy.HYPHEN_STRATEGY); + + assertCollectionPresentableName("Aaas", "aaa", DomNameStrategy.JAVA_STRATEGY); + assertCollectionPresentableName("Aaa Children", "aaaChild", DomNameStrategy.JAVA_STRATEGY); + + assertFixedPresentableName("Aaa Bbbs", "aaa-bbbs", DomNameStrategy.HYPHEN_STRATEGY); + assertFixedPresentableName("Aaa Child", "aaaChild", DomNameStrategy.JAVA_STRATEGY); + } + + private void assertCollectionPresentableName(final String expected, final String tagName, final DomNameStrategy strategy) { + assertEquals(expected, new CollectionChildDescriptionImpl(new XmlName(tagName), DomElement.class, null).getCommonPresentableName(strategy)); + } + + private void assertFixedPresentableName(final String expected, final String tagName, final DomNameStrategy strategy) { + assertEquals(expected, new FixedChildDescriptionImpl(new XmlName(tagName), DomElement.class, 0, new Collection[0]).getCommonPresentableName(strategy)); + } + + public void testNameStrategy() throws Throwable { + assertTrue(createElement("").getNameStrategy() instanceof HyphenNameStrategy); + + final AnotherElement anotherElement = createElement("", AnotherElement.class); + assertTrue(anotherElement.getNameStrategy() instanceof JavaNameStrategy); + assertTrue(anotherElement.getChild().getNameStrategy() instanceof JavaNameStrategy); + } + + public void testModificationCount() throws Throwable { + final MyElement element = createElement(""); + final long count = DomUtil.getFileElement(element).getModificationCount(); + element.addChildElement(); + assertTrue(DomUtil.getFileElement(element).getModificationCount() > count); + } + + public void testIsTagValueElement() throws Throwable { + assertTrue(getDomManager().getGenericInfo(MyElement.class).isTagValueElement()); + assertFalse(getDomManager().getGenericInfo(AnotherElement.class).isTagValueElement()); + } + + public void testAttributeChildrenGenerics() throws Throwable { + final StaticGenericInfo genericInfo = DomApplicationComponent.getInstance().getStaticGenericInfo(MyElement.class); + final List descriptions = genericInfo.getAttributeChildrenDescriptions(); + assertEquals(1, descriptions.size()); + final DomAttributeChildDescription description = descriptions.get(0); + + final MyElement element = createElement(""); + assertEquals(element.getAttr(), description.getValues(element).get(0)); + assertEquals(element.getAttr(), description.getDomAttributeValue(element)); + } + + private MyElement createElement(final String xml) throws IncorrectOperationException { + return createElement(xml, MyElement.class); + } + + public void testSubPropertyAccessing() throws Throwable { + final MyElement element = createElement(""); + final GenericAttributeValue attr = element.getChild().getChild().getAttr(); + assertNotNull(attr); + assertEquals(element.getChildChildAttr(), attr); + + final GenericAttributeValue attr1 = element.getChild().addChildElement().getAttr(); + final GenericAttributeValue attr2 = element.getChild().addChildElement().getAttr(); + assertOrderedEquals(element.getChildChildrenAttr(), attr1, attr2); + } + + public void testInstanceImplementation() throws Throwable { + MyElement element = createElement(""); + final Object o = new Object(); + element.setObject(o); + assertSame(o, element.getObject()); + + element = createElement("", InheritedElement.class); + element.setObject(o); + assertSame(o, element.getObject()); + assertSame(o, element.getObject()); + } + + public void testSeveralInstanceImplementations() throws Throwable { + InheritedElement element = createElement("", InheritedElement.class); + final Object o = new Object(); + element.setObject(o); + assertSame(o, element.getObject()); + assertSame(o, element._getObject()); + element.setString("foo"); + assertEquals("foo", element.getString()); + } + + public void testVisitor() throws Throwable { + final Integer[] visits = new Integer[]{0, 0, 0}; + DomElementVisitor visitor = new MyVisitor() { + @Override + public void visit(InheritedElement element) { + visits[0]++; + } + + @Override + public void visitDomElement(DomElement element) { + visits[1]++; + } + + @Override + public void visitMyElement(MyElement element) { + visits[2]++; + } + }; + + createElement("", MyElement.class).accept(visitor); + final List visitsList = Arrays.asList(visits); + assertEquals(Arrays.asList(0, 0, 1), visitsList); + + createElement("", InheritedElement.class).accept(visitor); + assertEquals(Arrays.asList(1, 0, 1), visitsList); + + createElement("", AnotherElement.class).accept(visitor); + assertEquals(Arrays.asList(1, 1, 1), visitsList); + + createElement("", DomElement.class).accept(visitor); + assertEquals(Arrays.asList(1, 2, 1), visitsList); + + createElement("", InheritedElement.class).accept(new DomElementVisitor() { + @Override + public void visitDomElement(DomElement element) { + visits[1]++; + } + }); + assertEquals(Arrays.asList(1, 3, 1), visitsList); + } + + public void testRegisteringImplementation() throws Throwable { + DomApplicationComponent.getInstance().registerImplementation(AnotherElement.class, EmptyImpl.class, getTestRootDisposable()); + DomApplicationComponent.getInstance().registerImplementation(DomElement.class, BaseImpl.class, getTestRootDisposable()); + final AnotherElement element = createElement("", AnotherElement.class); + assertTrue(element.getClass().getSuperclass().getName(), element instanceof EmptyImpl); + } + + public void testMockElements() throws Throwable { + Module module = new MockModule(getTestRootDisposable()); + final MyElement element = getDomManager().createMockElement(MyElement.class, module, false); + assertSame(module, element.getModule()); + assertTrue(element.isValid()); + assertNull(element.getXmlTag()); + assertEquals(element, DomUtil.getFileElement(element).getRootElement()); + assertFalse(DomUtil.getFile(element).isPhysical()); + + final MyElement element2 = getDomManager().createMockElement(MyElement.class, null, false); + assertNull(element2.getModule()); + assertNull(element2.getXmlTag()); + element2.addChildElement().getGenericValue().setStringValue("xxx"); + assertNotNull(element2.getXmlTag()); + final MyElement oldChild = element2.getChild(); + + element.getAttr().setValue("attr"); + element.getGenericValue().setValue(Boolean.TRUE); + element.getChild().getGenericValue().setStringValue("abc"); + element.addChildElement().getGenericValue().setStringValue("def"); + + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + element2.copyFrom(element); + } + }.execute(); + assertEquals("attr", element2.getAttr().getValue()); + assertEquals("true", element2.getGenericValue().getStringValue()); + + final MyElement newChild = element2.getChild(); + assertEquals("abc", newChild.getGenericValue().getStringValue()); + + final List childElements = element2.getChildElements(); + assertEquals(1, childElements.size()); + assertEquals("def", childElements.get(0).getGenericValue().getStringValue()); + } + + public void testCopyingFromEmptyToEmpty() throws Throwable { + Module module = new MockModule(getTestRootDisposable()); + MyElement element1 = getDomManager().createMockElement(MyElement.class, module, false); + MyElement element2 = getDomManager().createMockElement(MyElement.class, module, false); + element2.copyFrom(element1); + assertNull(element2.getXmlTag()); + } + + public void testCopyingFromNonEmptyToEmpty() throws Throwable { + Module module = new MockModule(getTestRootDisposable()); + final MyElement element1 = getDomManager().createMockElement(MyElement.class, module, false); + final MyElement element2 = getDomManager().createMockElement(MyElement.class, module, false); + element2.ensureTagExists(); + assertNull(element2.getChild().getChild().getGenericValue().getStringValue()); + element1.getChild().getChild().getGenericValue().setStringValue("abc"); + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + element2.copyFrom(element1); + } + }.execute(); + assertEquals("abc", element2.getChild().getChild().getGenericValue().getStringValue()); + } + + public void testStableValues() throws Throwable { + final MyElement[] element = new MyElement[]{null}; + final MyElement stable = getDomManager().createStableValue(new Factory() { + @Override + public MyElement create() { + try { + return element[0] = createElement("").addChildElement(); + } + catch (IncorrectOperationException e) { + throw new RuntimeException(e); + } + } + }); + assertNotNull(element[0]); + assertSame(element[0], ((StableElement)stable).getWrappedElement()); + assertEquals(element[0], stable); + assertEquals(stable, element[0]); + MyElement oldElement = element[0]; + assertFalse(stable.getChild().equals(stable)); + final GenericDomValue oldGenericValue = stable.getGenericValue(); + assertEquals(oldGenericValue, oldElement.getGenericValue()); + assertEquals(stable.getChild(), oldElement.getChild()); + assertSame(element[0], oldElement); + final MyElement child1 = stable.addChildElement(); + final MyElement child2 = stable.addChildElement(); + + MyElement oldChild1 = oldElement.getChildElements().get(0); + assertEquals(oldChild1, child1); + assertEquals(child1, oldChild1); + final MyElement oldElement1 = oldElement; + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + oldElement1.undefine(); + } + }.execute(); + assertFalse(oldChild1.isValid()); + + assertFalse(oldElement.isValid()); + assertFalse(element[0].isValid()); + assertTrue(stable.isValid()); + assertTrue(element[0].isValid()); + assertNotSame(element[0], oldElement); + + assertFalse(child1.isValid()); + assertFalse(child2.isValid()); + assertEquals(DomUtil.getFileElement(element[0]), DomUtil.getFileElement(stable)); + assertEquals(element[0].getParent(), stable.getParent()); + + oldElement = element[0]; + oldChild1 = oldElement.getChild(); + ((StableElement)stable).invalidate(); + assertTrue(oldElement.isValid()); + assertTrue(oldChild1.isValid()); + assertFalse(oldElement.equals(((StableElement)stable).getWrappedElement())); + } + + public void testStable_Revalidate() throws Throwable { + final MyElement[] element = new MyElement[]{createElement("")}; + final MyElement stable = getDomManager().createStableValue(new Factory() { + @Override + public MyElement create() { + return element[0]; + } + }); + MyElement oldElement = element[0]; + ((StableElement) stable).revalidate(); + assertSame(oldElement, ((StableElement) stable).getWrappedElement()); + + element[0] = createElement(""); + assertTrue(oldElement.isValid()); + ((StableElement) stable).revalidate(); + assertTrue(oldElement.isValid()); + assertNotSame(oldElement, ((StableElement) stable).getWrappedElement()); + assertSame(element[0], ((StableElement) stable).getWrappedElement()); + } + + public void testStable_Invalidate() throws Throwable { + final MyElement oldElement = createElement(""); + final MyElement[] element = new MyElement[]{oldElement}; + final MyElement stable = getDomManager().createStableValue(new Factory() { + @Override + public MyElement create() { + return element[0]; + } + }); + element[0] = null; + ((StableElement) stable).invalidate(); + assertTrue(stable.equals(stable)); + assertEquals(oldElement.toString(), stable.toString()); + } + + public void testStableCopies() throws Throwable { + final MyElement element = createElement(""); + final MyElement parent = element.getChildElements().get(1); + final MyElement child = parent.getChild(); + final MyElement copy = (MyElement) child.createStableCopy(); + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + parent.undefine(); + element.addChildElement().getChild().ensureXmlElementExists(); + } + }.execute(); + assertFalse(child.isValid()); + assertTrue(copy.isValid()); + } + + @Implementation(Impl.class) + public interface MyElement extends DomElement { + GenericAttributeValue getAttr(); + + String getValue(); + + MyElement getFoo(); + + MyElement getChild(); + MyElement addChildElement(); + @SuppressWarnings({"UnusedDeclaration"}) + MyElement addChildElement(int index); + + List getChildElements(); + + GenericDomValue getGenericValue(); + + Object getObject(); + + void setObject(Object o); + + @PropertyAccessor({"child", "child", "attr"}) + GenericAttributeValue getChildChildAttr(); + + @PropertyAccessor({"child", "childElements", "attr"}) + List> getChildChildrenAttr(); + + } + + public static abstract class Impl implements MyElement { + private Object myObject; + + public Object _getObject() { + return myObject; + } + + @Override + public Object getObject() { + return myObject; + } + + @Override + public void setObject(final Object object) { + myObject = object; + } + + } + + public static abstract class AnotherImpl extends Impl implements InheritedElement{ + private String myString; + + @Override + public String getString() { + return myString; + } + + @Override + public void setString(final String string) { + myString = string; + } + } + + @NameStrategy(JavaNameStrategy.class) + public interface AnotherElement extends DomElement { + DomElement getChild(); + } + + public static abstract class BaseImpl implements DomElement{} + + public static abstract class EmptyImpl extends BaseImpl implements AnotherElement{} + + @Implementation(AnotherImpl.class) + public interface InheritedElement extends MyElement, DomElement { + String getString(); + + void setString(final String string); + + Object _getObject(); + } + + public interface MyVisitor extends DomElementVisitor{ + void visitMyElement(MyElement element); + void visit(InheritedElement element); + } + +} diff --git a/xml/dom-tests/tests/com/intellij/util/xml/DomChildrenTest.java b/xml/dom-tests/tests/com/intellij/util/xml/DomChildrenTest.java new file mode 100644 index 000000000000..d50b878b9ad1 --- /dev/null +++ b/xml/dom-tests/tests/com/intellij/util/xml/DomChildrenTest.java @@ -0,0 +1,614 @@ +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 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("" + "foo" + ""); + 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(""); + assertNotNull(element.getChild239().getXmlTag()); + } + + public void testGetChildren() throws Throwable { + final MyElement element = createElement("" + "foo" + "bar" + ""); + assertSubBars(element.getMyChildren()); + assertSubBars(element.getChildElements()); + assertSubBars(element.getMyChildren2()); + } + + public void testGetChildrenMultipleTimes() throws Throwable { + final MyElement element = createElement("" + "" + " " + "" + ""); + for (int i = 0; i < 239; i++) { + final List children = element.getChildElements(); + final MyElement child = children.get(0); + final List children1 = child.getChildElements(); + final MyElement child1 = children1.get(0); + assertNotNull(child1.getChild()); + } + } + + private void assertSubBars(final List 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("" + + "Foo" + + "Foo" + + "bar" + ""); + assertFalse(element.getAbstractChild()instanceof MyBarConcreteElement); + final List 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(""); + 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 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("" + "foo" + "bar" + ""); + assertCached(element.getChild2(), element.getXmlTag().getSubTags()[1]); + assertEquals(0, element.getChildElements().size()); + } + + public void testDefiningIndexedChild() throws Throwable { + final MyElement element = createElement(""); + 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(""); + 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(""); + final MyElement child1 = element.getChildElements().get(0); + final MyElement child2 = element.getChildElements().get(1); + final MyElement child3 = element.getChildElements().get(2); + + final List oldChildren = new ArrayList(Arrays.asList(element.getXmlTag().getSubTags())); + + assertTrue(child2.isValid()); + assertEquals(element, child2.getParent()); + + ApplicationManager.getApplication().runWriteAction(new Runnable() { + 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("").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(""); + final GenericAttributeValue 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("" + "1" + "2" + ""); + 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("" + "1" + "2" + ""); + 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("" + "1" + "2" + "2" + ""); + 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(""); + 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("239"); + 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(""); + final MyElement child = element.getChild(); + ApplicationManager.getApplication().runWriteAction(new Runnable() { + 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("").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("", 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("<", MyElement.class); + final XmlTag tag = myElement.getXmlTag(); + assertEquals(0, myElement.getCustomChildren().size()); + } + + public void testCustomNameChildren() throws Throwable { + final MyElement myElement = createElement("", MyElement.class); + final XmlTag tag = myElement.getXmlTag(); + final List customChildren = myElement.getCustomChildren(); + assertOrderedEquals(customChildren, myElement.getGenericInfo().getCustomNameChildrenDescription().getValues(myElement)); + assertOrderedCollection(customChildren, new Consumer() { + @Override + public void consume(final MyElement element) { + assertInstanceOf(element, MyElement.class); + assertEquals(tag.getSubTags()[0], element.getXmlTag()); + } + }, new Consumer() { + @Override + public void consume(final MyElement element) { + assertInstanceOf(element, MyElement.class); + assertEquals(tag.getSubTags()[1], element.getXmlTag()); + } + }, new Consumer() { + @Override + public void consume(final MyElement element) { + assertInstanceOf(element, MyElement.class); + assertEquals(tag.getSubTags()[2], element.getXmlTag()); + } + }); + } + + private List getFixedPath(final DomElement element) { + return ContainerUtil.map2List(DomUtil.getFixedPath(element), new Function() { + @Override + public String fun(final JavaMethod s) { + return s.getName(); + } + }); + } + + public void testElementsWithoutXmlGetItLater() throws Throwable { + final MyElement element = createElement(""); + final MyElement child = element.getChild(); + final MyElement child2 = element.getChild2(); + final PsiElement tag = element.getXmlTag().add(createTag("")); + incModCount(); + + assertTrue(child.isValid()); + assertEquals(tag, child.getXmlTag()); + + assertTrue(child2.isValid()); + assertNull(child2.getXmlTag()); + } + + public void testHigherOrderFixedsAreNotCustom() { + final MyElement element = createElement(""); + XmlTag third = element.getXmlTag().getSubTags()[2]; + assertNull(getDomManager().getDomElement(third)); + assertEmpty(element.getCustomChildren()); + } + + + public interface MyElement extends DomElement { + @Attribute + @Required + GenericAttributeValue getAttr(); + + String getValue(); + + @SubTag("child") + MyElement getMyChild(); + + MyElement getChild(); + + @SubTag(value = "child", index = 1) + MyElement getChild2(); + + MyElement getChild239(); + + @SubTagList("child-element") + List getMyChildren(); + + @CustomChildren + List getCustomChildren(); + + @SubTagList("child-element") + @Required + List getMyChildren2(); + + List getChildElements(); + + MyElement addChildElement(); + + MyElement addChildElement(int index); + + MyAbstractElement getAbstractChild(); + + List getAbstractElements(); + + @SubTagList("abstract-element") + MyBarConcreteElement addBarChild(); + + @SubTagList("abstract-element") + MyFooConcreteElement addFooChild(); + + T addAbstractElement(Class aClass); + + T addAbstractElement(Class aClass, int index); + + T addAbstractElement(int index, Class aClass); + + List> getGenericChildren(); + + @Required + GenericDomValue isGenericValue(); + + @SubTagsList({"child-element", "abstract-element"}) + List 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; + } + } +} diff --git a/xml/dom-tests/tests/com/intellij/util/xml/DomConcurrencyStressTest.java b/xml/dom-tests/tests/com/intellij/util/xml/DomConcurrencyStressTest.java new file mode 100644 index 000000000000..4280836b81fc --- /dev/null +++ b/xml/dom-tests/tests/com/intellij/util/xml/DomConcurrencyStressTest.java @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2000-2007 JetBrains s.r.o. All Rights Reserved. + */ +package com.intellij.util.xml; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ex.PathManagerEx; +import com.intellij.openapi.extensions.Extensions; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiManager; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.XmlFile; +import com.intellij.psi.xml.XmlTag; +import com.intellij.semantic.SemService; +import com.intellij.testFramework.PlatformTestUtil; +import com.intellij.testFramework.Timings; +import com.intellij.util.xml.impl.DomFileElementImpl; +import com.intellij.util.xml.reflect.DomExtender; +import com.intellij.util.xml.reflect.DomExtenderEP; +import com.intellij.util.xml.reflect.DomExtensionsRegistrar; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Random; +import java.util.concurrent.CountDownLatch; + +/** + * @author peter + */ +public class DomConcurrencyStressTest extends DomTestCase { + private static final int ITERATIONS = Timings.adjustAccordingToMySpeed(239, true); + + public void testInternalDomLocksReadConsistency() throws Throwable { + getDomManager().registerFileDescription(new DomFileDescription(MyElement.class, "a"), myTestRootDisposable); + + registerExtender(MyElement.class, MyExtender.class); + + + final XmlFile file = createXmlFile("" + + "" + + "" + + "" + + " " + + " " + + " " + + " " + + "" + + "" + + "" + + " " + + " " + + " " + + " " + + "" + + "" + + " " + + " " + + " " + + "" + + "" + + "" + + "" + + ""); + + System.out.println("ITERATIONS =" + ITERATIONS); + runThreads(42, new Runnable() { + @Override + public void run() { + for (int i = 0; i < ITERATIONS; i++) { + ApplicationManager.getApplication().runReadAction(new Runnable() { + public void run() { + final DomFileElementImpl element = getDomManager().getFileElement(file); + assertNotNull(element); + element.getRootElement().accept(new DomElementVisitor() { + @Override + public void visitDomElement(final DomElement element) { + if (DomUtil.hasXml(element)) { + element.acceptChildren(this); + } + } + }); + } + }); + + SemService.getSemService(getProject()).clearCache(); + } + } + }); + } + + private void registerExtender(final Class elementClass, final Class extenderClass) { + final DomExtenderEP extenderEP = new DomExtenderEP(); + extenderEP.domClassName = elementClass.getName(); + extenderEP.extenderClassName = extenderClass.getName(); + PlatformTestUtil.registerExtension(Extensions.getRootArea(), DomExtenderEP.EP_NAME, extenderEP, myTestRootDisposable); + } + + private static void runThreads(int threadCount, final Runnable runnable) throws Throwable { + final Ref exc = Ref.create(null); + + final CountDownLatch reads = new CountDownLatch(threadCount); + for (int j = 0; j < threadCount; j++) { + new Thread(){ + @Override + public void run() { + try { + runnable.run(); + } + catch (Throwable e) { + exc.set(e); + } finally { + reads.countDown(); + } + } + }.start(); + } + reads.await(); + if (!exc.isNull()) { + throw exc.get(); + } + } + + public interface MyElement extends DomElement { + MyElement getFooChild(); + MyElement getBarChild(); + + MyElement getSomeChild(); + + List getFooElements(); + MyElement addFooElement(); + + List getChildren(); + MyElement addChild(); + + GenericAttributeValue getAttr(); + GenericAttributeValue getAttr2(); + + @SubTag(indicator = true) + GenericDomValue getBool(); + + } + + public static class MyExtender extends DomExtender { + private final Random myRandom = new Random(); + + @Override + public void registerExtensions(@NotNull MyElement myElement, @NotNull DomExtensionsRegistrar registrar) { + for (MyElement element : myElement.getFooElements()) { + element.getFooElements(); + } + if (myRandom.nextInt(20) < 2) { + try { + Thread.sleep(1); + } + catch (InterruptedException ignored) { + } + } + registrar.registerFixedNumberChildExtension(new XmlName("custom-foo", null), MyElement.class); + myElement.getSomeChild().getFooElements(); + myElement.getFooChild().getFooChild().getAttr(); + myElement.getAttr(); + registrar.registerCollectionChildrenExtension(new XmlName("custom-bar", null), MyElement.class); + } + } + + public void testBigCustomFile() throws Throwable { + getDomManager().registerFileDescription(new DomFileDescription(MyAllCustomElement.class, "component"), myTestRootDisposable); + + registerExtender(MyAllCustomElement.class, MyAllCustomExtender.class); + + VirtualFile bigXml = LocalFileSystem.getInstance().findFileByPath(PathManagerEx.getTestDataPath() + "/psi/formatter/xml/performance.xml"); + assert bigXml != null; + final XmlFile file = (XmlFile)PsiManager.getInstance(ourProject).findFile(bigXml); + + runThreads(42, new Runnable() { + + @Override + public void run() { + final Random random = new Random(); + for (int i = 0; i < ITERATIONS; i++) { + ApplicationManager.getApplication().runReadAction(new Runnable() { + public void run() { + int offset = random.nextInt(file.getTextLength() - 10); + XmlTag tag = PsiTreeUtil.findElementOfClassAtOffset(file, offset, XmlTag.class, false); + assert tag != null : offset; + DomElement element = DomUtil.getDomElement(tag); + assert element instanceof MyAllCustomElement : element; + } + }); + + if (random.nextInt(50) == 0) { + SemService.getSemService(getProject()).clearCache(); + } + } + } + }); + + } + + public interface MyAllCustomElement extends DomElement {} + + public static class MyAllCustomExtender extends DomExtender { + @Override + public void registerExtensions(@NotNull MyAllCustomElement element, @NotNull DomExtensionsRegistrar registrar) { + try { + DomElement parent = element; + while (parent != null) { + for (DomElement child : DomUtil.getDefinedChildren(parent, true, true)) { + DomElement childParent = child.getParent(); + if (!parent.equals(childParent)) { + String parentText = parent.getXmlTag().getText(); + String childText = child.getXmlElement().getText(); + child.getParent(); + parent.equals(childParent); + throw new AssertionError(parent.getXmlElement() + "; " + childParent + "; " + child.getXmlElement().getText()); + } + } + + parent = parent.getParent(); + } + } + catch (StackOverflowError e) { + System.out.println(Thread.currentThread() + ": " + element.getXmlTag().getName() + element.getXmlTag().hashCode()); + throw e; + } + registrar.registerCustomChildrenExtension(MyAllCustomElement.class); + } + } + + +} diff --git a/xml/dom-tests/tests/com/intellij/util/xml/DomExtensionsTest.java b/xml/dom-tests/tests/com/intellij/util/xml/DomExtensionsTest.java new file mode 100644 index 000000000000..0b62789e0163 --- /dev/null +++ b/xml/dom-tests/tests/com/intellij/util/xml/DomExtensionsTest.java @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2000-2007 JetBrains s.r.o. All Rights Reserved. + */ +package com.intellij.util.xml; + +import com.intellij.openapi.extensions.Extensions; +import com.intellij.openapi.util.Key; +import com.intellij.testFramework.IdeaTestUtil; +import com.intellij.util.Consumer; +import com.intellij.util.ParameterizedTypeImpl; +import com.intellij.util.ReflectionUtil; +import com.intellij.util.xml.reflect.*; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +/** + * @author peter + */ +public class DomExtensionsTest extends DomTestCase { + private static final Key BOOL_KEY = Key.create("aaa"); + + public void testExtendAttributes() throws Throwable { + registerDomExtender(AttrDomExtender.class); + assertEmpty(getCustomChildren(createElement("", MyElement.class))); + + MyElement element = createElement("", MyElement.class); + final GenericAttributeValue child = assertInstanceOf(assertOneElement(getCustomChildren(element)), GenericAttributeValue.class); + assertEquals("true", child.getStringValue()); + assertEquals(Boolean.TRUE, child.getValue()); + assertEquals(Boolean.class, DomUtil.getGenericValueParameter(child.getDomElementType())); + assertSame(element.getXmlTag().getAttribute("foo"), child.getXmlElement()); + + child.setStringValue("xxx"); + assertEquals("xxx", child.getStringValue()); + assertEquals("xxx", element.getXmlTag().getAttributeValue("foo")); + + element = createElement("", MyElement.class); + final GenericAttributeValue value = getDomManager().getDomElement(element.getXmlTag().getAttribute("foo")); + assertNotNull(value); + assertEquals(value, assertOneElement(getCustomChildren(element))); + assertNotNull(element.getGenericInfo().getAttributeChildDescription("foo")); + } + + public void testCustomAttributeChildClass() throws Throwable { + registerDomExtender(AttrDomExtender3.class); + final MyElement element = createElement("", MyElement.class); + assertEquals("xxx", assertInstanceOf(assertOneElement(getCustomChildren(element)), MyAttribute.class).getXmlElementName()); + } + + public void testUserData() throws Throwable { + registerDomExtender(AttrDomExtender3.class); + final MyElement element = createElement("", MyElement.class); + final DomAttributeChildDescription description = element.getGenericInfo().getAttributeChildDescription("xxx"); + assertNotNull(description); + assertSame(Boolean.TRUE, description.getUserData(BOOL_KEY)); + } + + public void testUseCustomConverter() throws Throwable { + registerDomExtender(AttrDomExtender2.class); + final MyElement myElement = createElement("", MyElement.class); + assertUnorderedCollection(getCustomChildren(myElement), new Consumer() { + @Override + public void consume(final DomElement element) { + final StringBuffer stringBuffer = ((GenericAttributeValue)element).getValue(); + assertEquals("zzz", stringBuffer.toString()); + assertInstanceOf(((GenericAttributeValue)element).getConverter(), StringBufferConverter.class); + assertNotNull(myElement.getGenericInfo().getAttributeChildDescription("xxx")); + + Convert convert = element.getAnnotation(Convert.class); + assertNotNull(convert); + assertEquals(StringBufferConverter.class, convert.value()); + assertTrue(convert.soft()); + } + }, new Consumer() { + @Override + public void consume(final DomElement element) { + final StringBuffer stringBuffer = ((GenericAttributeValue)element).getValue(); + assertEquals("zzz", stringBuffer.toString()); + assertInstanceOf(((GenericAttributeValue)element).getConverter(), StringBufferConverter.class); + assertNotNull(myElement.getGenericInfo().getAttributeChildDescription("yyy")); + + Convert convert = element.getAnnotation(Convert.class); + assertNotNull(convert); + assertEquals(StringBufferConverter.class, convert.value()); + assertFalse(convert.soft()); + } + }); + } + + public void testFixedChildren() throws Throwable { + registerDomExtender(FixedDomExtender.class); + final MyElement myElement = createElement("zzz", MyElement.class); + assertUnorderedCollection(getCustomChildren(myElement), new Consumer() { + @Override + public void consume(final DomElement element) { + assertEquals(GenericDomValue.class, ReflectionUtil.getRawType(element.getDomElementType())); + final StringBuffer stringBuffer = ((GenericDomValue)element).getValue(); + assertEquals("zzz", stringBuffer.toString()); + assertInstanceOf(((GenericDomValue)element).getConverter(), MyStringBufferConverter.class); + assertNotNull(myElement.getGenericInfo().getFixedChildDescription("xxx")); + + Convert convert = element.getAnnotation(Convert.class); + assertNotNull(convert); + assertEquals(MyStringBufferConverter.class, convert.value()); + assertTrue(convert.soft()); + + assertNotNull(element.getGenericInfo().getAttributeChildDescription("aaa")); + } + }, new Consumer() { + @Override + public void consume(final DomElement element) { + assertEquals("foo", ((MyElement) element).getAttr().getValue()); + assertNull(element.getAnnotation(Convert.class)); + } + }, new Consumer() { + @Override + public void consume(final DomElement element) { + assertEquals("bar", ((MyElement) element).getAttr().getValue()); + assertNull(element.getAnnotation(Convert.class)); + } + }); + final DomFixedChildDescription description = myElement.getGenericInfo().getFixedChildDescription("yyy"); + assertNotNull(description); + assertEquals(2, description.getCount()); + } + + public void testCollectionChildren() throws Throwable { + registerDomExtender(CollectionDomExtender.class); + final MyElement myElement = createElement("zzz", MyElement.class); + assertUnorderedCollection(getCustomChildren(myElement), new Consumer() { + @Override + public void consume(final DomElement element) { + assertEquals("foo", ((MyElement) element).getAttr().getValue()); + assertNull(element.getAnnotation(Convert.class)); + } + }, new Consumer() { + @Override + public void consume(final DomElement element) { + assertNull(((MyElement) element).getAttr().getValue()); + assertNull(element.getAnnotation(Convert.class)); + } + }); + assertNotNull(myElement.getGenericInfo().getCollectionChildDescription("xxx")); + } + + public void testCollectionAdders() throws Throwable { + registerDomExtender(CollectionDomExtender.class); + final MyElement myElement = createElement("", MyElement.class); + final DomCollectionChildDescription description = myElement.getGenericInfo().getCollectionChildDescription("xxx"); + final DomElement element2 = description.addValue(myElement); + final DomElement element0 = description.addValue(myElement, 0); + final DomElement element3 = description.addValue(myElement, MyConcreteElement.class); + final DomElement element1 = description.addValue(myElement, MyConcreteElement.class, 1); + assertSameElements(getCustomChildren(myElement), element0, element1, element2, element3); + } + + public void testCustomChildrenAccessFromExtender() throws Throwable { + registerDomExtender(MyCustomChildrenElement.class, CustomDomExtender.class); + final MyCustomChildrenElement myElement = createElement("", MyCustomChildrenElement.class); + final DomCollectionChildDescription description = myElement.getGenericInfo().getCollectionChildDescription("xx"); + assertNotNull(description); + assertInstanceOf(assertOneElement(description.getValues(myElement)), MyDynamicElement.class); + assertInstanceOf(assertOneElement(myElement.getCustomChidren()), MyCustomElement.class); + assertInstanceOf(assertOneElement(myElement.getConcreteChildren()), MyConcreteElement.class); + assertNotNull(assertInstanceOf(myElement.getSomeConcreteChild(), MyConcreteElement.class).getXmlTag()); + } + + public void testFirstChildRedefinitionOnExtending() throws Exception { + registerDomExtender(MyCustomChildrenElement.class, ModestDomExtender.class); + + final MyCustomChildrenElement myElement = createElement("", MyCustomChildrenElement.class); + final List list = myElement.getConcreteChildren(); + final List list2 = myElement.getConcreteChildren(); + assertSame(list.get(0), list2.get(0)); + assertSame(list.get(1), list2.get(1)); + } + + public static List getCustomChildren(final DomElement element) { + final List children = new ArrayList(); + element.acceptChildren(new DomElementVisitor() { + @Override + public void visitDomElement(final DomElement element) { + if (!"attr".equals(element.getXmlElementName())) { + children.add(element); + } + } + }); + return children; + } + + public void registerDomExtender(Class> extender) { + registerDomExtender(MyElement.class, extender); + } + + public void registerDomExtender(final Class domClass, final Class> extenderClass) { + final DomExtenderEP extenderEP = new DomExtenderEP(); + extenderEP.domClassName = domClass.getName(); + extenderEP.extenderClassName = extenderClass.getName(); + IdeaTestUtil.registerExtension(Extensions.getRootArea(), DomExtenderEP.EP_NAME, extenderEP, myTestRootDisposable); + } + + + public interface MyElement extends DomElement { + GenericAttributeValue getAttr(); + } + + public interface MyCustomChildrenElement extends DomElement { + @CustomChildren List getCustomChidren(); + + List getConcreteChildren(); + + MyConcreteElement getSomeConcreteChild(); + } + + public interface MyConcreteElement extends MyElement { + } + public interface MyCustomElement extends MyElement { + } + public interface MyDynamicElement extends MyElement { + } + + public static class AttrDomExtender extends DomExtender { + @Override + public void registerExtensions(@NotNull final MyElement element, @NotNull final DomExtensionsRegistrar registrar) { + final String value = element.getAttr().getValue(); + if (value != null) { + registrar.registerGenericAttributeValueChildExtension(new XmlName(value), Boolean.class); + } + } + } + + public static class AttrDomExtender2 extends DomExtender { + @Override + public void registerExtensions(@NotNull final MyElement element, @NotNull final DomExtensionsRegistrar registrar) { + final String value = element.getAttr().getValue(); + if (value != null) { + registrar.registerGenericAttributeValueChildExtension(new XmlName(value), StringBuffer.class).setConverter(new StringBufferConverter(), true); + registrar.registerGenericAttributeValueChildExtension(new XmlName("yyy"), StringBuffer.class).setConverter(new StringBufferConverter(), false); + } + } + } + + + + public static class AttrDomExtender3 extends DomExtender { + + @Override + public void registerExtensions(@NotNull final MyElement element, @NotNull final DomExtensionsRegistrar registrar) { + final String value = element.getAttr().getValue(); + if (value != null) { + registrar.registerAttributeChildExtension(new XmlName(value), MyAttribute.class).putUserData(BOOL_KEY, Boolean.TRUE); + } + } + } + + public static class FixedDomExtender extends DomExtender { + @Override + public void registerExtensions(@NotNull final MyElement element, @NotNull final DomExtensionsRegistrar registrar) { + final String value = element.getAttr().getValue(); + if (value != null) { + final ParameterizedType type = new ParameterizedTypeImpl(GenericDomValue.class, StringBuffer.class); + final DomExtension extension = + registrar.registerFixedNumberChildExtension(new XmlName(value), type).setConverter(new MyStringBufferConverter(true), true); + extension.addExtender(new DomExtender>(){ + + @Override + public void registerExtensions(@NotNull final GenericDomValue stringBufferGenericDomValue, @NotNull final DomExtensionsRegistrar registrar) { + registrar.registerGenericAttributeValueChildExtension(new XmlName("aaa"), String.class); + } + }); + ((DomExtensionsRegistrarImpl)registrar).registerFixedNumberChildrenExtension(new XmlName("yyy"), MyElement.class, 2); + } + } + } + public static class CollectionDomExtender extends DomExtender { + @Override + public void registerExtensions(@NotNull final MyElement element, @NotNull final DomExtensionsRegistrar registrar) { + final String value = element.getAttr().getValue(); + if (value != null) { + registrar.registerCollectionChildrenExtension(new XmlName(value), MyElement.class).setConverter(new MyStringBufferConverter(true), true); + } + } + } + + public static class CustomDomExtender extends DomExtender { + @Override + public void registerExtensions(@NotNull final MyCustomChildrenElement element, @NotNull final DomExtensionsRegistrar registrar) { + assertEmpty(element.getCustomChidren()); + registrar.registerCollectionChildrenExtension(new XmlName("xx"), MyDynamicElement.class); + } + } + + public static class ModestDomExtender extends DomExtender { + @Override + public void registerExtensions(@NotNull final MyCustomChildrenElement element, @NotNull final DomExtensionsRegistrar registrar) { + registrar.registerCollectionChildrenExtension(new XmlName("xx"), MyDynamicElement.class); + } + } + + public static class MyStringBufferConverter extends StringBufferConverter { + public MyStringBufferConverter(boolean b) { + } + } + public static interface MyAttribute extends GenericAttributeValue {} +} + diff --git a/xml/dom-tests/tests/com/intellij/util/xml/DomFileDescriptionTest.java b/xml/dom-tests/tests/com/intellij/util/xml/DomFileDescriptionTest.java new file mode 100644 index 000000000000..7d76e0e2c9c2 --- /dev/null +++ b/xml/dom-tests/tests/com/intellij/util/xml/DomFileDescriptionTest.java @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2000-2006 JetBrains s.r.o. All Rights Reserved. + */ +/* + * Copyright (c) 2000-2006 JetBrains s.r.o. All Rights Reserved. + */ +package com.intellij.util.xml; + +import com.intellij.openapi.application.Result; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.module.Module; +import com.intellij.psi.PsiFile; +import com.intellij.psi.xml.XmlFile; +import com.intellij.util.xml.impl.DomFileElementImpl; +import com.intellij.util.xml.impl.MockDomFileDescription; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Set; + +/** + * @author peter + */ +public class DomFileDescriptionTest extends DomHardCoreTestCase { + private XmlFile myFooElementFile; + private XmlFile myBarElementFile; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + myFooElementFile = new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + result.setResult((XmlFile)createFile("a.xml", "")); + } + }.execute().getResultObject(); + + getDomManager().registerFileDescription(new MockDomFileDescription(FooElement.class, "a", myFooElementFile), getTestRootDisposable()); + + myBarElementFile = new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + result.setResult((XmlFile)createFile("b.xml", "")); + } + }.execute().getResultObject(); + + getDomManager().registerFileDescription(new DomFileDescription(BarElement.class, "b") { + + @Override + public boolean isMyFile(@NotNull final XmlFile file, final Module module) { + return myFooElementFile.getText().contains("239"); + } + + @Override + public boolean isAutomaticHighlightingEnabled() { + return false; + } + }, getTestRootDisposable()); + + assertResultsAndClear(); + } + + public void testNoInitialDomnessInB() throws Throwable { + assertFalse(getDomManager().isDomFile(myBarElementFile)); + assertNull(getDomManager().getFileElement(myBarElementFile)); + } + + public void testIsDomValue() throws Throwable { + final XmlFile file = (XmlFile)createFile("a.xml", "42"); + getDomManager().registerFileDescription(new DomFileDescription(MyElement.class, "b") { + + @Override + public boolean isMyFile(@NotNull final XmlFile file, final Module module) { + return /*super.isMyFile(file, module) && */file.getText().contains("239"); + } + }, getTestRootDisposable()); + + + assertFalse(getDomManager().isDomFile(file)); + assertNull(getDomManager().getFileElement(file)); + + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + file.getDocument().getRootTag().getValue().setText("239"); + } + }.execute(); + assertTrue(getDomManager().isDomFile(file)); + final DomFileElementImpl root = getDomManager().getFileElement(file); + assertNotNull(root); + final MyElement child = root.getRootElement().getChild(); + assertTrue(root.isValid()); + assertTrue(child.isValid()); + + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + file.getDocument().getRootTag().getValue().setText("57121"); + } + }.execute(); + assertFalse(getDomManager().isDomFile(file)); + assertNull(getDomManager().getFileElement(file)); + assertFalse(root.isValid()); + assertFalse(child.isValid()); + } + + public void testCopyFileDescriptionFromOriginalFile() throws Throwable { + final XmlFile file = (XmlFile)createFile("a.xml", "42"); + + getDomManager().registerFileDescription(new MockDomFileDescription(MyElement.class, "b", file), getTestRootDisposable()); + file.setName("b.xml"); + assertTrue(getDomManager().isDomFile(file)); + final XmlFile copy = (XmlFile)file.copy(); + assertTrue(getDomManager().isDomFile(copy)); + assertFalse(getDomManager().getFileElement(file).equals(getDomManager().getFileElement(copy))); + } + + public void testDependantFileDescriptionCauseStackOverflow() throws Throwable { + final XmlFile interestingFile = (XmlFile)createFile("a.xml", "42"); + + getDomManager().registerFileDescription(new MockDomFileDescription(MyElement.class, "b", (XmlFile)null), getTestRootDisposable()); + for (int i = 0; i < 239; i++) { + getDomManager().registerFileDescription(new MockDomFileDescription(AbstractElement.class, "b", (XmlFile)null) { + @Override + @NotNull + public Set getDependencyItems(final XmlFile file) { + getDomManager().isDomFile(interestingFile); + return super.getDependencyItems(file); + } + }, getTestRootDisposable()); + } + + getDomManager().isDomFile(interestingFile); + } + + public void testCheckNamespace() throws Throwable { + getDomManager().registerFileDescription(new DomFileDescription(NamespacedElement.class, "xxx", "bar"){ + + @Override + protected void initializeFileDescription() { + registerNamespacePolicy("foo", "bar"); + } + }, getTestRootDisposable()); + + final PsiFile file = createFile("xxx.xml", ""); + assertFalse(getDomManager().isDomFile(file)); + + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + ((XmlFile)file).getDocument().getRootTag().setAttribute("xmlns", "bar"); + } + }.execute(); + + assertTrue(getDomManager().isDomFile(file)); + + } + + public void testCheckDtdPublicId() throws Throwable { + getDomManager().registerFileDescription(new DomFileDescription(NamespacedElement.class, "xxx", "bar"){ + + @Override + protected void initializeFileDescription() { + registerNamespacePolicy("foo", "bar"); + } + }, getTestRootDisposable()); + + final PsiFile file = createFile("xxx.xml", ""); + assertFalse(getDomManager().isDomFile(file)); + + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + final Document document = getDocument(file); + document.insertString(0, "\n"); + commitDocument(document); + } + }.execute(); + + assertTrue(getDomManager().isDomFile(file)); + } + + public void testChangeCustomDomness() throws Throwable { + getDomManager().registerFileDescription(new DomFileDescription(MyElement.class, "xxx"){ + @Override + public boolean isMyFile(@NotNull final XmlFile file, @Nullable final Module module) { + return file.getText().contains("foo"); + } + }, getTestRootDisposable()); + final XmlFile file = (XmlFile)createFile("xxx.xml", ""); + final MyElement boy = getDomManager().getFileElement(file, MyElement.class).getRootElement().getBoys().get(0); + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + file.getDocument().getRootTag().setAttribute("zzz", "bar"); + } + }.execute(); + assertFalse(getDomManager().isDomFile(file)); + assertFalse(boy.isValid()); + } + + public interface AbstractElement extends GenericDomValue { + GenericAttributeValue getAttr(); + } + + public interface FooElement extends AbstractElement { + } + + public interface BarElement extends AbstractElement { + } + + public interface ZipElement extends AbstractElement { + } + + public interface MyElement extends DomElement { + + MyElement getChild(); + + List getBoys(); + + } + + @Namespace("foo") + public interface NamespacedElement extends DomElement { + + } + + +} diff --git a/xml/dom-tests/tests/com/intellij/util/xml/DomHardCoreTestCase.java b/xml/dom-tests/tests/com/intellij/util/xml/DomHardCoreTestCase.java new file mode 100644 index 000000000000..3a6938a3e951 --- /dev/null +++ b/xml/dom-tests/tests/com/intellij/util/xml/DomHardCoreTestCase.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2000-2006 JetBrains s.r.o. All Rights Reserved. + */ +package com.intellij.util.xml; + +import com.intellij.codeInsight.CodeInsightTestCase; +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.codeInsight.lookup.PresentableLookupValue; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; +import com.intellij.psi.xml.XmlAttributeValue; +import com.intellij.psi.xml.XmlTag; +import com.intellij.psi.xml.XmlTagValue; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.xml.events.DomEvent; +import com.intellij.util.xml.impl.DomManagerImpl; + +/** + * @author peter + */ +public abstract class DomHardCoreTestCase extends CodeInsightTestCase { + private CallRegistry myCallRegistry; + + @Override + protected void setUp() throws Exception { + super.setUp(); + myCallRegistry = new CallRegistry(); + getDomManager().addDomEventListener(new DomEventListener() { + @Override + public void eventOccured(DomEvent event) { + myCallRegistry.putActual(event); + } + }, myProject); + } + + protected DomManagerImpl getDomManager() { + return DomManagerImpl.getDomManager(getProject()); + } + + protected T createElement(final String xml, final Class aClass) throws IncorrectOperationException { + return DomTestCase.createElement(getDomManager(), xml, aClass); + } + + protected void assertEventCount(final int size) { + assertEquals(myCallRegistry.toString(), size, myCallRegistry.getSize()); + } + + protected void putExpected(final DomEvent event) { + myCallRegistry.putExpected(event); + } + + protected void assertResultsAndClear() { + myCallRegistry.assertResultsAndClear(); + } + + protected PsiReference assertReference(final GenericDomValue value) { + return assertReference(value, value.getXmlTag()); + } + + protected PsiReference assertReference(final GenericDomValue value, PsiElement resolveTo) { + final XmlTagValue tagValue = value.getXmlTag().getValue(); + final TextRange textRange = tagValue.getTextRange(); + final String s = value.getStringValue(); + final int i = tagValue.getText().indexOf(s); + return assertReference(value, resolveTo, textRange.getStartOffset() + i + s.length()); + } + + protected PsiReference assertReference(final GenericDomValue value, PsiElement resolveTo, int offset) { + final XmlTag tag = value.getXmlTag(); + final PsiReference reference = tag.getContainingFile().findReferenceAt(offset); + assertNotNull(reference); + assertEquals(resolveTo, reference.resolve()); + return reference; + } + + protected PsiReference getReference(final GenericAttributeValue value) { + final XmlAttributeValue attributeValue = value.getXmlAttributeValue(); + final PsiReference reference = attributeValue.getContainingFile().findReferenceAt(attributeValue.getTextRange().getStartOffset() + 1); + assertNotNull(reference); + assertEquals(attributeValue, reference.resolve()); + return reference; + } + + protected void assertVariants(PsiReference reference, String... variants) { + Object[] refVariants = reference.getVariants(); + assertNotNull(refVariants); + assertEquals(refVariants.length, variants.length); + int i = 0; + for (String variant : variants) { + Object refVariant = refVariants[i++]; + if (refVariant instanceof LookupElement) { + assertEquals(variant, ((LookupElement)refVariant).getLookupString()); + } + else if (refVariant instanceof PresentableLookupValue) { + assertEquals(variant, ((PresentableLookupValue)refVariant).getPresentation()); + } + else { + assertEquals(variant, refVariant.toString()); + } + } + } + + protected DomReferencesTest.MyElement createElement(final String xml) throws IncorrectOperationException { + return createElement(xml, DomReferencesTest.MyElement.class); + } +} diff --git a/xml/dom-tests/tests/com/intellij/util/xml/DomHighlightingLiteTest.java b/xml/dom-tests/tests/com/intellij/util/xml/DomHighlightingLiteTest.java new file mode 100644 index 000000000000..8470c15bb5e0 --- /dev/null +++ b/xml/dom-tests/tests/com/intellij/util/xml/DomHighlightingLiteTest.java @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2000-2006 JetBrains s.r.o. All Rights Reserved. + */ +package com.intellij.util.xml; + +import com.intellij.codeInsight.daemon.HighlightDisplayKey; +import com.intellij.codeInspection.InspectionManager; +import com.intellij.codeInspection.InspectionProfile; +import com.intellij.codeInspection.InspectionToolProvider; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.codeInspection.ex.InspectionToolRegistrar; +import com.intellij.ide.ui.search.SearchableOptionsRegistrar; +import com.intellij.lang.annotation.Annotation; +import com.intellij.mock.MockInspectionProfile; +import com.intellij.profile.codeInspection.InspectionProfileManager; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiType; +import com.intellij.psi.xml.XmlElement; +import com.intellij.psi.xml.XmlFile; +import com.intellij.psi.xml.XmlTag; +import com.intellij.testFramework.MockSchemesManagerFactory; +import com.intellij.util.xml.highlighting.*; +import com.intellij.util.xml.impl.DefaultDomAnnotator; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Type; +import java.util.ArrayList; + +/** + * @author peter + */ +public class DomHighlightingLiteTest extends DomTestCase { + private DomElementAnnotationsManagerImpl myAnnotationsManager; + private MockDomFileElement myElement; + private MockInspectionProfile myInspectionProfile; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + final InspectionToolRegistrar registrar = new InspectionToolRegistrar(SearchableOptionsRegistrar.getInstance()); + registrar.registerTools(new InspectionToolProvider[0]); + final InspectionProfileManager inspectionProfileManager = new InspectionProfileManager(registrar, new MockSchemesManagerFactory()); + myInspectionProfile = new MockInspectionProfile(); + myAnnotationsManager = new DomElementAnnotationsManagerImpl(getProject()) { + + @Override + protected InspectionProfile getInspectionProfile(final DomFileElement fileElement) { + + return myInspectionProfile; + } + }; + + final XmlFile file = createXmlFile(""); + final MockDomElement rootElement = new MockDomElement() { + @Override + @Nullable + public XmlElement getXmlElement() { + return getXmlTag(); + } + + @Override + public XmlTag getXmlTag() { + return file.getDocument().getRootTag(); + } + + @Override + public Type getDomElementType() { + return DomElement.class; + } + }; + + myElement = new MockDomFileElement() { + @Override + @Nullable + public XmlElement getXmlElement() { + return file; + } + + @Override + @NotNull + public XmlFile getFile() { + return file; + } + + @Override + public DomElement getParent() { + return null; + } + + @Override + @NotNull + public DomElement getRootElement() { + return rootElement; + } + + @Override + public Class getRootElementClass() { + return DomElement.class; + } + + @Override + public boolean isValid() { + return true; + } + }; + } + + public void testEmptyProblemDescriptorInTheBeginning() throws Throwable { + assertEmptyHolder(myAnnotationsManager.getProblemHolder(myElement)); + } + + private static void assertEmptyHolder(final DomElementsProblemsHolder holder) { + assertFalse(holder instanceof DomElementsProblemsHolderImpl); + assertEmpty(holder.getAllProblems()); + } + + public void testProblemDescriptorIsCreated() throws Throwable { + myAnnotationsManager.appendProblems(myElement, new DomElementAnnotationHolderImpl(), MyDomElementsInspection.class); + final DomElementsProblemsHolderImpl holder = assertNotEmptyHolder(myAnnotationsManager.getProblemHolder(myElement)); + assertEmpty(holder.getAllProblems()); + assertEmpty(holder.getAllProblems(new MyDomElementsInspection())); + } + + private static DomElementsProblemsHolderImpl assertNotEmptyHolder(final DomElementsProblemsHolder holder1) { + return assertInstanceOf(holder1, DomElementsProblemsHolderImpl.class); + } + + public void testInspectionMarkedAsPassedAfterAppend() throws Throwable { + myAnnotationsManager.appendProblems(myElement, new DomElementAnnotationHolderImpl(), MyDomElementsInspection.class); + final DomElementsProblemsHolderImpl holder = (DomElementsProblemsHolderImpl)myAnnotationsManager.getProblemHolder(myElement); + assertTrue(holder.isInspectionCompleted(MyDomElementsInspection.class)); + assertFalse(holder.isInspectionCompleted(DomElementsInspection.class)); + } + + public void testHolderRecreationAfterChange() throws Throwable { + myAnnotationsManager.appendProblems(myElement, new DomElementAnnotationHolderImpl(), MyDomElementsInspection.class); + assertTrue(DomElementAnnotationsManagerImpl.isHolderUpToDate(myElement)); + final DomElementsProblemsHolder holder = myAnnotationsManager.getProblemHolder(myElement); + + myElement.incModificationCount(); + assertFalse(DomElementAnnotationsManagerImpl.isHolderUpToDate(myElement)); + assertSame(holder, myAnnotationsManager.getProblemHolder(myElement)); + + myAnnotationsManager.appendProblems(myElement, new DomElementAnnotationHolderImpl(), MyDomElementsInspection.class); + assertNotSame(holder, assertNotEmptyHolder(myAnnotationsManager.getProblemHolder(myElement))); + } + + public void testMockDomInspection() throws Throwable { + myElement.setFileDescription(new MyNonHighlightingDomFileDescription()); + assertInstanceOf(myAnnotationsManager.getMockInspection(myElement), MockDomInspection.class); + } + + public void testMockAnnotatingDomInspection() throws Throwable { + myElement.setFileDescription(new DomFileDescription(DomElement.class, "a")); + assertInstanceOf(myAnnotationsManager.getMockInspection(myElement), MockAnnotatingDomInspection.class); + } + + public void testNoMockInspection() throws Throwable { + myElement.setFileDescription(new MyNonHighlightingDomFileDescription()); + myInspectionProfile.setInspectionTools(new MyDomElementsInspection()); + assertNull(myAnnotationsManager.getMockInspection(myElement)); + } + + public void testDefaultAnnotator() throws Throwable { + final DefaultDomAnnotator annotator = new DefaultDomAnnotator() { + @Override + protected DomElementAnnotationsManagerImpl getAnnotationsManager(final DomElement element) { + return myAnnotationsManager; + } + }; + final StringBuilder s = new StringBuilder(); + final ArrayList toFill = new ArrayList(); + final MyDomElementsInspection inspection = new MyDomElementsInspection() { + + @Override + public void checkFileElement(final DomFileElement fileElement, final DomElementAnnotationHolder holder) { + s.append("visited"); + } + }; + annotator.runInspection(inspection, myElement, toFill); + assertEquals("visited", s.toString()); + final DomElementsProblemsHolderImpl holder = assertNotEmptyHolder(myAnnotationsManager.getProblemHolder(myElement)); + assertEmpty(toFill); + + annotator.runInspection(inspection, myElement, toFill); + assertEquals("visited", s.toString()); + assertSame(holder, assertNotEmptyHolder(myAnnotationsManager.getProblemHolder(myElement))); + assertEmpty(toFill); + } + + public void testHighlightStatus_MockDomInspection() throws Throwable { + myElement.setFileDescription(new MyNonHighlightingDomFileDescription()); + assertEquals(DomHighlightStatus.NONE, myAnnotationsManager.getHighlightStatus(myElement)); + + myAnnotationsManager.appendProblems(myElement, new DomElementAnnotationHolderImpl(), MockDomInspection.class); + assertEquals(DomHighlightStatus.INSPECTIONS_FINISHED, myAnnotationsManager.getHighlightStatus(myElement)); + } + public void testHighlightStatus_MockAnnotatingDomInspection() throws Throwable { + myElement.setFileDescription(new DomFileDescription(DomElement.class, "a")); + + myAnnotationsManager.appendProblems(myElement, new DomElementAnnotationHolderImpl(), MockAnnotatingDomInspection.class); + assertEquals(DomHighlightStatus.INSPECTIONS_FINISHED, myAnnotationsManager.getHighlightStatus(myElement)); + } + + public void testHighlightStatus_OtherInspections() throws Throwable { + myElement.setFileDescription(new DomFileDescription(DomElement.class, "a")); + final MyDomElementsInspection inspection = new MyDomElementsInspection() { + + @Override + public ProblemDescriptor[] checkFile(@NotNull final PsiFile file, @NotNull final InspectionManager manager, + final boolean isOnTheFly) { + myAnnotationsManager.appendProblems(myElement, new DomElementAnnotationHolderImpl(), this.getClass()); + return new ProblemDescriptor[0]; + } + + @Override + public void checkFileElement(final DomFileElement fileElement, final DomElementAnnotationHolder holder) { + } + }; + HighlightDisplayKey.register(inspection.getShortName()); + myInspectionProfile.setInspectionTools(inspection); + + myAnnotationsManager.appendProblems(myElement, new DomElementAnnotationHolderImpl(), MockAnnotatingDomInspection.class); + assertEquals(DomHighlightStatus.ANNOTATORS_FINISHED, myAnnotationsManager.getHighlightStatus(myElement)); + + myAnnotationsManager.appendProblems(myElement, new DomElementAnnotationHolderImpl(), inspection.getClass()); + assertEquals(DomHighlightStatus.INSPECTIONS_FINISHED, myAnnotationsManager.getHighlightStatus(myElement)); + } + + public void testHighlightStatus_OtherInspections2() throws Throwable { + myElement.setFileDescription(new DomFileDescription(DomElement.class, "a")); + final MyDomElementsInspection inspection = new MyDomElementsInspection() { + + @Override + public ProblemDescriptor[] checkFile(@NotNull final PsiFile file, @NotNull final InspectionManager manager, + final boolean isOnTheFly) { + myAnnotationsManager.appendProblems(myElement, new DomElementAnnotationHolderImpl(), this.getClass()); + return new ProblemDescriptor[0]; + } + + @Override + public void checkFileElement(final DomFileElement fileElement, final DomElementAnnotationHolder holder) { + } + }; + HighlightDisplayKey.register(inspection.getShortName()); + myInspectionProfile.setInspectionTools(inspection); + myInspectionProfile.setEnabled(inspection, false); + + myAnnotationsManager.appendProblems(myElement, new DomElementAnnotationHolderImpl(), MockAnnotatingDomInspection.class); + assertEquals(DomHighlightStatus.INSPECTIONS_FINISHED, myAnnotationsManager.getHighlightStatus(myElement)); + } + + public void testRequiredAttributeWithoutAttributeValue() throws Throwable { + final MyElement element = createElement("", MyElement.class); + new MyBasicDomElementsInspection().checkDomElement(element.getId(), new DomElementAnnotationHolderImpl(), DomHighlightingHelperImpl.INSTANCE); + } + + private class MyDomElementsInspection extends DomElementsInspection { + public MyDomElementsInspection() { + super(DomElement.class); + } + + @Override + @NotNull + public String getGroupDisplayName() { + throw new UnsupportedOperationException("Method getGroupDisplayName is not yet implemented in " + getClass().getName()); + } + + @Override + @NotNull + public String getDisplayName() { + throw new UnsupportedOperationException("Method getDisplayName is not yet implemented in " + getClass().getName()); + } + + @Override + @NotNull + public String getShortName() { + return "xxx"; + } + } + + private class MyBasicDomElementsInspection extends BasicDomElementsInspection { + public MyBasicDomElementsInspection() { + super(DomElement.class); + } + + @Override + @NotNull + public String getGroupDisplayName() { + throw new UnsupportedOperationException("Method getGroupDisplayName is not yet implemented in " + getClass().getName()); + } + + @Override + @NotNull + public String getDisplayName() { + throw new UnsupportedOperationException("Method getDisplayName is not yet implemented in " + getClass().getName()); + } + + @Override + protected void checkDomElement(final DomElement element, final DomElementAnnotationHolder holder, final DomHighlightingHelper helper) { + super.checkDomElement(element, holder, helper); + } + + @Override + @NotNull + public String getShortName() { + return "xxx"; + } + } + + + private static class MyNonHighlightingDomFileDescription extends DomFileDescription { + public MyNonHighlightingDomFileDescription() { + super(DomElement.class, "a"); + } + + @Override + public boolean isAutomaticHighlightingEnabled() { + return false; + } + } + + public interface MyElement extends DomElement { + @Convert(soft=true, value=JvmPsiTypeConverter.class) + @Required GenericAttributeValue getId(); + } +} diff --git a/xml/dom-tests/tests/com/intellij/util/xml/DomIncludesTest.java b/xml/dom-tests/tests/com/intellij/util/xml/DomIncludesTest.java new file mode 100644 index 000000000000..20e19b88193f --- /dev/null +++ b/xml/dom-tests/tests/com/intellij/util/xml/DomIncludesTest.java @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2000-2005 by JetBrains s.r.o. All Rights Reserved. + * Use is subject to license terms. + */ +package com.intellij.util.xml; + +import com.intellij.codeInsight.navigation.actions.GotoDeclarationAction; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.pom.PomTargetPsiElement; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.xml.XmlFile; +import com.intellij.testFramework.Timings; +import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase; +import com.intellij.util.Consumer; +import com.intellij.util.xml.impl.DomFileElementImpl; +import com.intellij.util.xml.impl.DomManagerImpl; + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; + +/** + * @author peter + */ +public class DomIncludesTest extends JavaCodeInsightFixtureTestCase { + + public void testGetChildrenHonorsIncludes() throws Throwable { + final MyElement rootElement = createDomFile("a.xml", "" + + "" + + "" + + ""); + createFile("b.xml", ""); + final List children = rootElement.getChildren(); + Consumer consumer1 = new Consumer() { + @Override + public void consume(final MyElement element) { + assertFalse(element.getXmlElement().isPhysical()); + assertEquals(rootElement, element.getParent()); + assertEquals(DomUtil.getFileElement(rootElement), DomUtil.getFileElement(element)); + assertEquals("b", element.getXxx().getValue()); + } + }; + Consumer consumer2 = new Consumer() { + @Override + public void consume(final MyElement element) { + assertTrue(element.getXmlElement().isPhysical()); + assertEquals(rootElement, element.getParent()); + assertEquals(DomUtil.getFileElement(rootElement), DomUtil.getFileElement(element)); + assertEquals("a", element.getXxx().getValue()); + } + }; + assertOrderedCollection(children, new Consumer[] { consumer1, consumer2 }); + } + + public void testNamespaces() throws Throwable { + final MyElement rootElement = createDomFile("a.xml", "" + + "" + + "" + + ""); + DomUtil.getFileElement(rootElement).getFileDescription().registerNamespacePolicy("foo", "foo"); + + createFile("b.xml", ""); + final List children = rootElement.getBoys(); + assertOrderedCollection(children, new Consumer() { + @Override + public void consume(final Boy element) { + assertFalse(element.getXmlElement().isPhysical()); + assertEquals(rootElement, element.getParent()); + assertEquals(DomUtil.getFileElement(rootElement), DomUtil.getFileElement(element)); + assertEquals("b", element.getXxx().getValue()); + } + }, new Consumer() { + @Override + public void consume(final Boy element) { + assertTrue(element.getXmlElement().isPhysical()); + assertEquals(rootElement, element.getParent()); + assertEquals(DomUtil.getFileElement(rootElement), DomUtil.getFileElement(element)); + assertEquals("a", element.getXxx().getValue()); + } + }); + } + + public void testEqualsWithMultiThreadedIncludes() throws Throwable { + final MyElement rootElement = createDomFile("a.xml", "" + + "" + + ""); + + final PsiFile fileB = createFile("b.xml", + ""); + final PsiFile fileC = createFile("c.xml", + ""); + final PsiFile fileD = createFile("d.xml", + ""); + final PsiFile fileE = createFile("e.xml", + ""); + final int threadCount = 100; + final int iterationCount = Timings.adjustAccordingToMySpeed(800, true); + System.out.println("iterationCount = " + iterationCount); + + final CountDownLatch finished = new CountDownLatch(threadCount); + final AtomicReference ex = new AtomicReference(); + + Thread[] threads = new Thread[threadCount]; + for (int j = 0; j < threadCount; j++) { + Thread thread = new Thread() { + @Override + public void run() { + for (int k = 0; k < iterationCount; k++) { + ApplicationManager.getApplication().runReadAction(new Runnable() { + public void run() { + try { + final List boys = rootElement.getBoys(); + Thread.yield(); + final List children = rootElement.getChildren(); + Thread.yield(); + assertEquals(boys, rootElement.getBoys()); + assertEquals(children, rootElement.getChildren()); + } + catch (Exception e) { + ex.set(e); + } + finally { + finished.countDown(); + } + } + }); + Thread.yield(); + } + } + }; + threads[j] = thread; + thread.start(); + } + + for (int i = 0; i < iterationCount; i++) { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + public void run() { + fileB.getViewProvider().getDocument().insertString(0, " "); + fileD.getViewProvider().getDocument().insertString(0, " "); + PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); //clear xinclude caches + } + }); + Thread.sleep(10); + ApplicationManager.getApplication().runWriteAction(new Runnable() { + public void run() { + fileC.getViewProvider().getDocument().insertString(0, " "); + fileE.getViewProvider().getDocument().insertString(0, " "); + PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); //clear xinclude caches + } + }); + Thread.sleep(10); + } + + finished.await(); + for (Thread thread : threads) { + thread.join(); + } + final Exception exception = ex.get(); + if (exception != null) { + throw exception; + } + } + + public void testNavigationToIncluded() throws Throwable { + final MyElement rootElement = createDomFile("a.xml", "" + + "" + + "" + + ""); + final XmlFile includedFile = (XmlFile) createFile("b.xml", ""); + final List children = rootElement.getChildren(); + final MyElement domTarget = children.get(0); + final GenericAttributeValue ref = children.get(1).getRef(); + final MyElement value = ref.getValue(); + assertEquals(domTarget, value); + + myFixture.configureFromTempProjectFile("a.xml"); + + final int offset = ref.getXmlAttributeValue().getTextRange().getStartOffset() + 1; + myFixture.getEditor().getCaretModel().moveToOffset(offset); + final PsiElement target = GotoDeclarationAction.findTargetElement(getProject(), myFixture.getEditor(), offset); + PsiElement element = ((DomTarget)((PomTargetPsiElement)target).getTarget()).getNavigationElement(); +// assertSame(PomService.convertToPsi(DomTarget.getTarget(domTarget)), target); + assertSame(includedFile.getDocument().getRootTag().getSubTags()[0].getAttributes()[0].getValueElement(), element); + } + + private MyElement createDomFile(final String fileName, final String fileText) throws IOException { + final XmlFile xmlFile = (XmlFile) createFile(fileName, fileText); + final DomFileElementImpl element = getDomManager().getFileElement(xmlFile, MyElement.class, xmlFile.getDocument().getRootTag().getName()); + return element.getRootElement(); + } + + private PsiFile createFile(final String fileName, final String fileText) throws IOException { + final VirtualFile file = myFixture.getTempDirFixture().createFile(fileName); + VfsUtil.saveText(file, fileText); + return getPsiManager().findFile(file); + } + + private DomManagerImpl getDomManager() { + return DomManagerImpl.getDomManager(getProject()); + } + + public interface MyElement extends DomElement { + + List getChildren(); + + List getBoys(); + + @NameValue + GenericAttributeValue getXxx(); + + GenericAttributeValue getRef(); + + } + + @Namespace("foo") + public interface Boy extends MyElement { + + } + + public interface Child extends MyElement { + + } + +} diff --git a/xml/dom-tests/tests/com/intellij/util/xml/DomModelMergingTest.java b/xml/dom-tests/tests/com/intellij/util/xml/DomModelMergingTest.java new file mode 100644 index 000000000000..6dc47ed8a06a --- /dev/null +++ b/xml/dom-tests/tests/com/intellij/util/xml/DomModelMergingTest.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2000-2006 JetBrains s.r.o. All Rights Reserved. + */ +package com.intellij.util.xml; + +import com.intellij.openapi.command.WriteCommandAction; + +import java.util.*; + +/** + * @author peter + */ +public class DomModelMergingTest extends DomTestCase{ + private ModelMerger myMerger; + + @Override + protected void setUp() throws Exception { + super.setUp(); + myMerger = new ModelMergerImpl(); + } + + public void testVisitor() throws Throwable { + final MyElement element1 = createElement("", MyElement.class); + final MyElement foo1 = element1.getFoo(); + final MyElement bar1 = element1.addBar(); + final MyElement bar2 = element1.addBar(); + + final MyElement element2 = createElement("", MyElement.class); + final MyElement foo2 = element2.getFoo(); + final MyElement bar3 = element2.addBar(); + + final MyElement element = myMerger.mergeModels(MyElement.class, element1, element2); + final MyElement foo = element.getFoo(); + assertEquals(foo, myMerger.mergeModels(MyElement.class, foo1, foo2)); + + final int[] count = new int[]{0}; + element.accept(new DomElementVisitor() { + @Override + public void visitDomElement(DomElement _element) { + count[0]++; + assertEquals(_element, element); + } + }); + assertEquals(1, count[0]); + + count[0] = 0; + final Set result = new HashSet(); + element.acceptChildren(new DomElementVisitor() { + @Override + public void visitDomElement(DomElement element) { + count[0]++; + result.add(element); + } + }); + assertEquals(new HashSet(Arrays.asList(foo, bar1, bar2, bar3)).toString().replace(",", "\n"), result.toString().replace(",", "\n")); + assertEquals(new HashSet(Arrays.asList(foo, bar1, bar2, bar3)), result); + assertEquals(4, count[0]); + } + + public void testValidity() throws Throwable { + new WriteCommandAction.Simple(getProject()) { + @Override + protected void run() throws Throwable { + final MyElement element = createElement("", MyElement.class); + final MyElement bar1 = element.addBar(); + final MyElement bar2 = element.addBar(); + final MyElement merged = myMerger.mergeModels(MyElement.class, bar1, bar2); + assertTrue(merged.isValid()); + bar2.undefine(); + assertFalse(merged.isValid()); + } + }.execute().throwException(); + } + + public interface MyElement extends DomElement { + MyElement getFoo(); + List getBars(); + MyElement addBar(); + } + +} diff --git a/xml/dom-tests/tests/com/intellij/util/xml/DomNamespacesTest.java b/xml/dom-tests/tests/com/intellij/util/xml/DomNamespacesTest.java new file mode 100644 index 000000000000..9c24fa57557b --- /dev/null +++ b/xml/dom-tests/tests/com/intellij/util/xml/DomNamespacesTest.java @@ -0,0 +1,354 @@ +/* + * Copyright (c) 2000-2005 by JetBrains s.r.o. All Rights Reserved. + * Use is subject to license terms. + */ +package com.intellij.util.xml; + +import com.intellij.openapi.application.Result; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.psi.xml.XmlFile; +import com.intellij.psi.xml.XmlTag; +import com.intellij.util.xml.impl.DomFileElementImpl; +import com.intellij.util.xml.reflect.DomGenericInfo; + +import java.util.List; + +/** + * @author peter + */ +public class DomNamespacesTest extends DomTestCase { + + public void testUseExistingNamespace() throws Throwable { + final MyElement element = createElement("", MyElement.class); + registerNamespacePolicies(element); + + final XmlTag fooChildTag = element.getFooChild().ensureTagExists(); + assertEquals("foo-child", fooChildTag.getName()); + assertEquals("foo", fooChildTag.getNamespace()); + assertEquals("", fooChildTag.getNamespacePrefix()); + + final XmlTag barChildTag = element.getBarChild().ensureTagExists(); + assertEquals("bar:bar-child", barChildTag.getName()); + assertEquals("bar", barChildTag.getNamespace()); + assertEquals("bar", barChildTag.getNamespacePrefix()); + } + + public void testDefineNewNamespace() throws Throwable { + final MyElement element = createElement("", MyElement.class); + registerNamespacePolicies(element); + + final XmlTag fooChildTag = element.getFooChild().ensureTagExists(); + assertEquals("foo-child", fooChildTag.getName()); + assertEquals("foo", fooChildTag.getNamespace()); + assertEquals("", fooChildTag.getNamespacePrefix()); + assertEquals("foo", fooChildTag.getAttributeValue("xmlns")); + + final XmlTag barChildTag = element.getBarChild().ensureTagExists(); + assertEquals("bar-child", barChildTag.getName()); + assertEquals("bar", barChildTag.getNamespace()); + assertEquals("", barChildTag.getNamespacePrefix()); + assertEquals("bar", barChildTag.getAttributeValue("xmlns")); + } + + public void testCollectionChildNamespace() throws Throwable { + final MyElement element = createElement("", MyElement.class); + registerNamespacePolicies(element); + + final XmlTag fooChildTag = element.addFooElement().getXmlTag(); + assertEquals("foo:foo-element", fooChildTag.getName()); + assertEquals("foo", fooChildTag.getNamespace()); + assertEquals("foo", fooChildTag.getNamespacePrefix()); + assertNull(fooChildTag.getAttributeValue("xmlns")); + } + + public void testNoNamespaceForFixedChild() throws Throwable { + final MyElement element = createElement("", MyElement.class); + registerNamespacePolicies(element); + + final XmlTag childTag = element.getSomeChild().ensureTagExists(); + assertEquals("some-child", childTag.getName()); + assertEquals("", childTag.getNamespace()); + assertEquals("", childTag.getNamespacePrefix()); + assertNull(childTag.getAttributeValue("xmlns")); + } + + public void testNoNamespaceForCollectionChild() throws Throwable { + final MyElement element = createElement("", MyElement.class); + registerNamespacePolicies(element); + + final XmlTag childTag = element.addChild().getXmlTag(); + assertEquals("child", childTag.getName()); + assertEquals("", childTag.getNamespace()); + assertEquals("", childTag.getNamespacePrefix()); + assertNull(childTag.getAttributeValue("xmlns")); + } + + public void testNamespaceEqualToParent() throws Throwable { + final MyElement element = createElement("", MyElement.class); + registerNamespacePolicies(element); + + final XmlTag childTag = element.addChild().getXmlTag(); + assertEquals("child", childTag.getName()); + assertEquals("foo", childTag.getNamespace()); + assertEquals("", childTag.getNamespacePrefix()); + assertNull(childTag.getAttributeValue("xmlns")); + } + + public void testNamespaceEqualToParent2() throws Throwable { + final MyElement root = createElement("", MyElement.class); + registerNamespacePolicies(root); + final MyFooElement element = root.addFooElement(); + + final MyElement child = element.addChild(); + final XmlTag childTag = child.getXmlTag(); + assertEquals("child", childTag.getName()); + assertEquals("foo", childTag.getNamespace()); + assertEquals("", childTag.getNamespacePrefix()); + assertNull(childTag.getAttributeValue("xmlns")); + + assertEquals("foo", element.getXmlElementNamespaceKey()); + assertNull(child.getXmlElementNamespaceKey()); + } + + public void testHardcodedNamespacePrefix() throws Throwable { + final XmlFile xmlFile = createXmlFile(""); + final MyElement element = getDomManager().getFileElement(xmlFile, MyElement.class, "a").getRootElement(); + final MyElement hardcodedElement = element.getHardcodedElement(); + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + hardcodedElement.ensureTagExists(); + } + }.execute(); + assertTrue(element.isValid()); + assertTrue(hardcodedElement.isValid()); + assertNotNull(hardcodedElement.getXmlElement()); + assertEquals("", hardcodedElement.getXmlElement().getText()); + assertEquals("sys:aaa", hardcodedElement.getXmlTag().getName()); + + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + hardcodedElement.getHardcodedElement().getHardcodedElement().ensureTagExists(); + } + }.execute(); + + assertTrue(element.isValid()); + assertTrue(hardcodedElement.isValid()); + assertNotNull(hardcodedElement.getXmlElement()); + assertEquals("sys:aaa", hardcodedElement.getHardcodedElement().getHardcodedElement().getXmlTag().getName()); + + assertEquals(1, element.getXmlTag().getSubTags().length); + } + + public void testAutoChooseNamespaceIfPresent() throws Throwable { + final MyElement root = createElement("", MyElement.class); + getDomManager().getDomFileDescription(root.getXmlElement()).registerNamespacePolicy("foo", "bar", "foo"); + + final XmlTag fooChildTag = root.getFooChild().ensureTagExists(); + assertEquals("foo-child", fooChildTag.getName()); + assertEquals("foo", fooChildTag.getNamespace()); + assertEquals("", fooChildTag.getNamespacePrefix()); + assertEquals(0, fooChildTag.getAttributes().length); + } + + public void testNonemptyRootTagPrefix() throws Throwable { + getDomManager().registerFileDescription(new DomFileDescription(MyFooElement.class, "a", "foons") { + + @Override + protected void initializeFileDescription() { + super.initializeFileDescription(); + registerNamespacePolicy("foo", "foons"); + } + }, getTestRootDisposable()); + + final XmlFile psiFile = createXmlFile(""); + final DomFileElementImpl element = getDomManager().getFileElement(psiFile, MyFooElement.class); + assertNotNull(element); + + final MyFooElement root = element.getRootElement(); + assertNotNull(root); + assertSame(psiFile.getDocument().getRootTag(), root.getXmlElement()); + } + + public void testSpringAopLike() throws Throwable { + getDomManager().registerFileDescription(new DomFileDescription(MyBeans.class, "beans", "beans", "aop") { + + @Override + protected void initializeFileDescription() { + super.initializeFileDescription(); + registerNamespacePolicy("beans", "beans"); + registerNamespacePolicy("aop", "aop"); + } + }, getTestRootDisposable()); + + final XmlFile psiFile = createXmlFile("" + + "" + + "" + + "" + + ""); + final MyBeans beans = getDomManager().getFileElement(psiFile, MyBeans.class).getRootElement(); + final DomElement pointcut = + getDomManager().getDomElement(beans.getXmlTag().findFirstSubTag("aop:config").findFirstSubTag("aop:pointcut")); + assertNotNull(pointcut); + final MyAopConfig aopConfig = beans.getConfig(); + assertEquals(assertOneElement(aopConfig.getPointcuts()), pointcut); + } + + public void testSpringUtilLike() throws Throwable { + getDomManager().registerFileDescription(new DomFileDescription(MyBeans.class, "beans", "beans", "util") { + + @Override + protected void initializeFileDescription() { + super.initializeFileDescription(); + registerNamespacePolicy("beans", "beans"); + registerNamespacePolicy("util", "util"); + } + }, getTestRootDisposable()); + + final XmlFile psiFile = createXmlFile("" + + "" + + "aaa" + + "bbb" + + ""); + + final MyList listOrSet = assertInstanceOf(getDomManager().getFileElement(psiFile, MyBeans.class).getRootElement().getList(), MyList.class); + assertNotNull(listOrSet.getXmlTag()); + + final XmlTag listTag = psiFile.getDocument().getRootTag().findFirstSubTag("util:list"); + assertNotNull(getDomManager().getDomElement(listTag.findFirstSubTag("ref"))); + assertNotNull(getDomManager().getDomElement(listTag.findFirstSubTag("util:child"))); + + assertEquals("aaa", listOrSet.getRef().getValue()); + assertEquals("bbb", listOrSet.getChild().getValue()); + } + + private void registerNamespacePolicies(final MyElement element) { + registerNamespacePolicies(element, "foo", "bar"); + } + + private void registerNamespacePolicies(final MyElement element, final String foo, final String bar) { + final DomFileDescription description = getDomManager().getDomFileDescription(element.getXmlElement()); + description.registerNamespacePolicy("foo", foo); + description.registerNamespacePolicy("bar", bar); + } + + public void testFindChildDescriptionWithoutNamespace() throws Throwable { + final DomGenericInfo info = getDomManager().getGenericInfo(MyListOrSet.class); + assertNotNull(info.getAttributeChildDescription("attr")); + assertNotNull(info.getAttributeChildDescription("attr").getType()); + assertNotNull(info.getCollectionChildDescription("child")); + assertNotNull(info.getCollectionChildDescription("child").getType()); + assertNotNull(info.getFixedChildDescription("ref")); + assertNotNull(info.getFixedChildDescription("ref").getType()); + } + + public void testCopyFromHonorsNamespaces() throws Throwable { + final MyElement element = createElement("", MyElement.class); + registerNamespacePolicies(element); + + final MyElement element2 = createElement("" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "", MyElement.class); + registerNamespacePolicies(element2, "foo1", "bar1"); + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + element.copyFrom(element2); + } + }.execute(); + + assertEquals("" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "", + element.getXmlTag().getText()); + } + + public void testAttributeWithAnotherNamespace() throws Throwable { + final MyElement element = createElement("", MyElement.class); + registerNamespacePolicies(element); + final MyFooElement fooElement = element.getFooChild(); + final MyAttribute myAttribute = fooElement.getMyAttribute(); + assertNotNull(myAttribute.getXmlAttribute()); + assertEquals("xxx", myAttribute.getStringValue()); + } + + public interface MyElement extends DomElement { + MyFooElement getFooChild(); + MyBarElement getBarChild(); + + MyElement getSomeChild(); + + List getFooElements(); + MyFooElement addFooElement(); + + List getChildren(); + MyElement addChild(); + + GenericAttributeValue getAttr(); + GenericAttributeValue getAttr2(); + + @SubTag(indicator = true) + GenericDomValue getBool(); + + @SubTag("sys:aaa") + MyElement getHardcodedElement(); + } + + @Namespace("foo") + public interface MyFooElement extends MyElement { + MyAttribute getMyAttribute(); + } + + @Namespace("bar") + public interface MyBarElement extends MyElement { + + } + + @Namespace("bar") + public interface MyAttribute extends GenericAttributeValue { + + } + + @Namespace("beans") + public interface MyBeans extends DomElement { + MyAopConfig getConfig(); + MyList getList(); + } + + @Namespace("aop") + public interface MyAopConfig extends DomElement { + List getPointcuts(); + } + + @Namespace("aop") + public interface MySpringPointcut extends DomElement { + + } + + @Namespace("beans") + public interface MyListOrSet extends DomElement { + GenericDomValue getRef(); + List> getChildren(); + GenericAttributeValue getAttr(); + } + + @Namespace("util") + public interface MyList extends MyListOrSet { + GenericDomValue getChild(); + } + + +} diff --git a/xml/dom-tests/tests/com/intellij/util/xml/DomPerformanceTest.java b/xml/dom-tests/tests/com/intellij/util/xml/DomPerformanceTest.java new file mode 100644 index 000000000000..ef9ee9843e2a --- /dev/null +++ b/xml/dom-tests/tests/com/intellij/util/xml/DomPerformanceTest.java @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2000-2006 JetBrains s.r.o. All Rights Reserved. + */ +package com.intellij.util.xml; + +import com.intellij.openapi.application.Result; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.impl.PsiManagerImpl; +import com.intellij.psi.impl.meta.MetaRegistry; +import com.intellij.psi.xml.XmlFile; +import com.intellij.testFramework.IdeaTestUtil; +import com.intellij.testFramework.PlatformTestUtil; +import com.intellij.util.ThrowableRunnable; +import com.intellij.xml.impl.dtd.XmlNSDescriptorImpl; +import org.jetbrains.annotations.NotNull; + +import java.io.BufferedWriter; +import java.io.OutputStreamWriter; +import java.util.List; + +/** + * @author peter + */ +public class DomPerformanceTest extends DomHardCoreTestCase{ + + public void testVisitorPerformance() throws Throwable { + final MyElement element = createElement("", MyElement.class); + + MetaRegistry.bindDataToElement(DomUtil.getFile(element).getDocument(), new XmlNSDescriptorImpl()); + + final MyElement child = element.addChildElement(); + child.getAttr().setValue("239"); + child.getChild239().getAttr().setValue("42"); + child.getChild().getAttr().setValue("42xx"); + child.getChild2().getAttr().setValue("42yy"); + child.addChildElement().getChild().addFooChild().getAttr().setValue("xxx"); + child.addChildElement().addFooChild().getAttr().setValue("yyy"); + child.addChildElement().addFooChild().addBarChild().addBarChild().addChildElement().getChild().getAttr().setValue("xxx"); + child.addChildElement().addBarComposite().setValue("ssss"); + child.addBarChild().getChild2().getAttr().setValue("234178956023"); + + long start = System.currentTimeMillis(); + for (int i = 0; i < 239; i++) { + element.addChildElement().copyFrom(child); + } + IdeaTestUtil.assertTiming("", 30000, System.currentTimeMillis() - start); + + final MyElement newElement = createElement(DomUtil.getFile(element).getText(), MyElement.class); + + //ProfilingUtil.startCPUProfiling(); + PlatformTestUtil.startPerformanceTest(getTestName(false), 200, new ThrowableRunnable() { + @Override + public void run() throws Exception { + newElement.acceptChildren(new DomElementVisitor() { + @Override + public void visitDomElement(DomElement element) { + element.acceptChildren(this); + } + }); + /* + try { + ProfilingUtil.captureCPUSnapshot("aaa"); + } + catch (Exception e) { + e.printStackTrace(); + } + */ + + } + }).cpuBound().assertTiming(); + } + + public void testShouldntParseNonDomFiles() throws Throwable { + for (int i = 0; i < 420; i++) { + getDomManager().registerFileDescription(new DomFileDescription(MyChildElement.class, "foo") { + + @Override + public boolean isMyFile(@NotNull final XmlFile file, final Module module) { + fail(); + return super.isMyFile(file, module); + } + }, getTestRootDisposable()); + getDomManager().registerFileDescription(new DomFileDescription(MyChildElement.class, "bar") { + + @Override + public boolean isMyFile(@NotNull final XmlFile file, final Module module) { + fail(); + return super.isMyFile(file, module); + } + }, getTestRootDisposable()); + } + + getDomManager().createMockElement(MyChildElement.class, null, true); + + @NotNull final VirtualFile virtualFile = createFile("a.xml", "").getVirtualFile(); + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + + final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(virtualFile.getOutputStream(this))); + writer.write("\n"); + for (int i = 0; i < 23942; i++) { + writer.write("\n"); + } + writer.write(""); + writer.close(); + virtualFile.refresh(false, false); + } + }.execute(); + + ((PsiManagerImpl)getPsiManager()).getFileManager().cleanupForNextTest(); + final XmlFile file = (XmlFile)getPsiManager().findFile(virtualFile); + assertFalse(file.getNode().isParsed()); + assertTrue(StringUtil.isNotEmpty(file.getText())); + PlatformTestUtil.startPerformanceTest("", 100, new ThrowableRunnable() { + @Override + public void run() throws Exception { + assertNull(getDomManager().getFileElement(file)); + } + }).cpuBound().assertTiming(); + assertFalse(file.getNode().isParsed()); + } + + public void testDontParseNamespacedDomFiles() throws Exception { + getDomManager().registerFileDescription(new DomFileDescription(MyNamespacedElement.class, "foo") { + @Override + protected void initializeFileDescription() { + registerNamespacePolicy("project", "project"); + } + }, getTestRootDisposable()); + + XmlFile file = (XmlFile)createFile("a.xml", ""); + assertFalse(file.getNode().isParsed()); + assertNotNull(DomManager.getDomManager(getProject()).getFileElement(file, MyNamespacedElement.class)); + assertFalse(file.getNode().isParsed()); + + file = (XmlFile)createFile("a.xml", ""); + assertFalse(file.getNode().isParsed()); + assertNull(DomManager.getDomManager(getProject()).getFileElement(file, MyNamespacedElement.class)); + assertFalse(file.getNode().isParsed()); + } + + @Namespace("project") + public interface MyNamespacedElement extends DomElement { + + } + public interface MyChildElement extends DomElement { + @Attribute + @Required + GenericAttributeValue getAttr(); + + List getFooChildren(); + + MyFooConcreteElement addFooChild(); + } + + public interface MyElement extends DomElement { + @Attribute + @Required + GenericAttributeValue getAttr(); + + String getValue(); + + void setValue(String s); + + MyChildElement getChild(); + + @SubTag(value = "child", index = 1) + MyChildElement getChild2(); + + MyChildElement getChild239(); + + List getChildElements(); + + MyElement addChildElement(); + + List getAbstractElements(); + + @SubTagList("abstract-element") + MyBarConcreteElement addBarChild(); + + @SubTagList("abstract-element") + MyFooConcreteElement addFooChild(); + + @SubTagsList({"child-element", "abstract-element"}) + List getCompositeList(); + + @SubTagsList(value = {"child-element", "abstract-element"}, tagName = "abstract-element") + MyBarConcreteElement addBarComposite(); + + } + + public interface MyAbstractElement extends MyElement { + } + + public interface MyFooConcreteElement extends MyAbstractElement { + } + + public interface MyBarConcreteElement extends MyAbstractElement { + } + + +} diff --git a/xml/dom-tests/tests/com/intellij/util/xml/DomReferenceInjectorTest.java b/xml/dom-tests/tests/com/intellij/util/xml/DomReferenceInjectorTest.java new file mode 100644 index 000000000000..cc0cb5b51eec --- /dev/null +++ b/xml/dom-tests/tests/com/intellij/util/xml/DomReferenceInjectorTest.java @@ -0,0 +1,456 @@ +package com.intellij.util.xml; + +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.SearchScope; +import com.intellij.psi.scope.PsiScopeProcessor; +import com.intellij.util.IncorrectOperationException; +import com.intellij.lang.Language; +import com.intellij.lang.ASTNode; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.NonNls; + +import javax.swing.*; + +public class DomReferenceInjectorTest extends DomHardCoreTestCase { + public void testBasic() throws Exception { + MyElement element = createElement("abc${prop}def", MyElement.class); + assertEquals("abc${prop}def", element.getValue().getStringValue()); + assertEquals("abc${prop}def", element.getValue().getValue()); + } + + public void testWithInjector() throws Exception { + MyElement element = createElement("abc${prop}def", MyElement.class); + + registerInjectorFor(element, null); + + assertEquals("abcFOOdef", element.getValue().getStringValue()); + assertEquals("abcFOOdef", element.getValue().getValue()); + } + + public void testCorrectlyCalculateOffsetWithInjector() throws Exception { + MyElement element = createElement(" abc${prop}def ", MyElement.class); + + registerInjectorFor(element, null); + + assertEquals("abcFOOdef", element.getValue().getStringValue()); + assertEquals("abcFOOdef", element.getValue().getValue()); + } + + public void testWithInjectorAndConverter() throws Exception { + MyElement element = createElement("abc${prop}def", MyElement.class); + + registerInjectorFor(element, null); + + assertEquals("abcFOOdef", element.getConvertedValue().getStringValue()); + assertEquals("abcBARdef", element.getConvertedValue().getValue()); + } + + public void testReference() throws Exception { + String text = "abc${prop}def"; + MyElement element = createElement(text, MyElement.class); + + MyPsiElement targetElement = new MyPsiElement(); + registerInjectorFor(element, targetElement); + + assertEquals("abcFOOdef", element.getValue().getStringValue()); + assertEquals("abcFOOdef", element.getValue().getValue()); + + assertReference(element.getValue(), targetElement, text.indexOf("${prop}") + 1); + } + + public void testAttribute() throws Exception { + String text = ""; + MyElement element = createElement(text, MyElement.class); + + MyPsiElement targetElement = new MyPsiElement(); + registerInjectorFor(element, targetElement); + + assertEquals("abcFOOdef", element.getAttr().getStringValue()); + assertEquals("abcFOOdef", element.getAttr().getValue()); + + assertReference(element.getAttr(), targetElement, text.indexOf("${prop}") + 1); + } + + private void registerInjectorFor(DomElement element, PsiElement targetElement) { + DomUtil.getFileElement(element).getFileDescription().registerReferenceInjector(new MyInjector(targetElement)); + } + + public interface MyElement extends DomElement { + GenericDomValue getValue(); + + @Convert(MyConverter.class) + GenericDomValue getConvertedValue(); + + GenericAttributeValue getAttr(); + } + + public static class MyConverter extends Converter { + @Override + public String fromString(@Nullable @NonNls String s, ConvertContext context) { + return s == null ? null : s.replaceAll("FOO", "BAR"); + } + + @Override + public String toString(@Nullable String s, ConvertContext context) { + return s; + } + } + + public static class MyPsiElement implements PsiElement { + @Override + @NotNull + public Project getProject() throws PsiInvalidElementAccessException { + throw new UnsupportedOperationException(); + } + + @Override + @NotNull + public Language getLanguage() { + throw new UnsupportedOperationException(); + } + + @Override + public PsiManager getManager() { + throw new UnsupportedOperationException(); + } + + @Override + @NotNull + public PsiElement[] getChildren() { + throw new UnsupportedOperationException(); + } + + @Override + public PsiElement getParent() { + throw new UnsupportedOperationException(); + } + + @Override + public PsiElement getFirstChild() { + throw new UnsupportedOperationException(); + } + + @Override + public PsiElement getLastChild() { + throw new UnsupportedOperationException(); + } + + @Override + public PsiElement getNextSibling() { + throw new UnsupportedOperationException(); + } + + @Override + public PsiElement getPrevSibling() { + throw new UnsupportedOperationException(); + } + + @Override + public PsiFile getContainingFile() throws PsiInvalidElementAccessException { + throw new UnsupportedOperationException(); + } + + @Override + public TextRange getTextRange() { + throw new UnsupportedOperationException(); + } + + @Override + public int getStartOffsetInParent() { + throw new UnsupportedOperationException(); + } + + @Override + public int getTextLength() { + throw new UnsupportedOperationException(); + } + + @Override + public PsiElement findElementAt(int offset) { + throw new UnsupportedOperationException(); + } + + @Override + public PsiReference findReferenceAt(int offset) { + throw new UnsupportedOperationException(); + } + + @Override + public int getTextOffset() { + throw new UnsupportedOperationException(); + } + + @Override + public String getText() { + throw new UnsupportedOperationException(); + } + + @Override + @NotNull + public char[] textToCharArray() { + throw new UnsupportedOperationException(); + } + + @Override + public PsiElement getNavigationElement() { + throw new UnsupportedOperationException(); + } + + @Override + public PsiElement getOriginalElement() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean textMatches(@NotNull @NonNls CharSequence text) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean textMatches(@NotNull PsiElement element) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean textContains(char c) { + throw new UnsupportedOperationException(); + } + + @Override + public void accept(@NotNull PsiElementVisitor visitor) { + throw new UnsupportedOperationException(); + } + + @Override + public void acceptChildren(@NotNull PsiElementVisitor visitor) { + throw new UnsupportedOperationException(); + } + + @Override + public PsiElement copy() { + throw new UnsupportedOperationException(); + } + + @Override + public PsiElement add(@NotNull PsiElement element) throws IncorrectOperationException { + throw new UnsupportedOperationException(); + } + + @Override + public PsiElement addBefore(@NotNull PsiElement element, PsiElement anchor) throws IncorrectOperationException { + throw new UnsupportedOperationException(); + } + + @Override + public PsiElement addAfter(@NotNull PsiElement element, PsiElement anchor) throws IncorrectOperationException { + throw new UnsupportedOperationException(); + } + + @Override + public void checkAdd(@NotNull PsiElement element) throws IncorrectOperationException { + throw new UnsupportedOperationException(); + } + + @Override + public PsiElement addRange(PsiElement first, PsiElement last) throws IncorrectOperationException { + throw new UnsupportedOperationException(); + } + + @Override + public PsiElement addRangeBefore(@NotNull PsiElement first, @NotNull PsiElement last, PsiElement anchor) + throws IncorrectOperationException { + throw new UnsupportedOperationException(); + } + + @Override + public PsiElement addRangeAfter(PsiElement first, PsiElement last, PsiElement anchor) throws IncorrectOperationException { + throw new UnsupportedOperationException(); + } + + @Override + public void delete() throws IncorrectOperationException { + throw new UnsupportedOperationException(); + } + + @Override + public void checkDelete() throws IncorrectOperationException { + throw new UnsupportedOperationException(); + } + + @Override + public void deleteChildRange(PsiElement first, PsiElement last) throws IncorrectOperationException { + throw new UnsupportedOperationException(); + } + + @Override + public PsiElement replace(@NotNull PsiElement newElement) throws IncorrectOperationException { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isValid() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isWritable() { + throw new UnsupportedOperationException(); + } + + @Override + public PsiReference getReference() { + throw new UnsupportedOperationException(); + } + + @Override + @NotNull + public PsiReference[] getReferences() { + throw new UnsupportedOperationException(); + } + + @Override + public T getCopyableUserData(Key key) { + throw new UnsupportedOperationException(); + } + + @Override + public void putCopyableUserData(Key key, T value) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean processDeclarations(@NotNull PsiScopeProcessor processor, + @NotNull ResolveState state, + @Nullable PsiElement lastParent, + @NotNull PsiElement place) { + throw new UnsupportedOperationException(); + } + + @Override + public PsiElement getContext() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isPhysical() { + throw new UnsupportedOperationException(); + } + + @Override + @NotNull + public GlobalSearchScope getResolveScope() { + throw new UnsupportedOperationException(); + } + + @Override + @NotNull + public SearchScope getUseScope() { + throw new UnsupportedOperationException(); + } + + @Override + public ASTNode getNode() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isEquivalentTo(PsiElement another) { + throw new UnsupportedOperationException(); + } + + @Override + public T getUserData(@NotNull Key key) { + throw new UnsupportedOperationException(); + } + + @Override + public void putUserData(@NotNull Key key, @Nullable T value) { + throw new UnsupportedOperationException(); + } + + @Override + public Icon getIcon(int flags) { + throw new UnsupportedOperationException(); + } + } + + private static class MyInjector implements DomReferenceInjector { + private final PsiElement myMyTargetElement; + + public MyInjector(PsiElement myTargetElement) { + myMyTargetElement = myTargetElement; + } + + @Override + public String resolveString(@Nullable String unresolvedText, @NotNull ConvertContext context) { + return unresolvedText == null ? null : unresolvedText.replaceAll("\\$\\{prop\\}", "FOO"); + } + + @Override + @NotNull + public PsiReference[] inject(@Nullable String unresolvedText, @NotNull final PsiElement element, @NotNull ConvertContext context) { + final String prop = "${prop}"; + int index = unresolvedText == null ? -1 : unresolvedText.indexOf(prop); + if (index == -1) return PsiReference.EMPTY_ARRAY; + + TextRange textRange = ElementManipulators.getValueTextRange(element); + final TextRange refRange = TextRange.from(textRange.getStartOffset() + index, prop.length()); + + return new PsiReference[] { + new PsiReference() { + @Override + public PsiElement getElement() { + return element; + } + + @Override + public TextRange getRangeInElement() { + return refRange; + } + + @Override + public PsiElement resolve() { + return myMyTargetElement; + } + + @Override + @NotNull + public String getCanonicalText() { + return prop; + } + + @Override + public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException { + return null; + } + + @Override + public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException { + return null; + } + + @Override + public boolean isReferenceTo(PsiElement element) { + return false; + } + + @Override + @NotNull + public Object[] getVariants() { + return EMPTY_ARRAY; + } + + @Override + public boolean isSoft() { + return true; + } + } + }; + } + } +} diff --git a/xml/dom-tests/tests/com/intellij/util/xml/DomReferencesTest.java b/xml/dom-tests/tests/com/intellij/util/xml/DomReferencesTest.java new file mode 100644 index 000000000000..1dae0c78cd5b --- /dev/null +++ b/xml/dom-tests/tests/com/intellij/util/xml/DomReferencesTest.java @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2000-2006 JetBrains s.r.o. All Rights Reserved. + */ +package com.intellij.util.xml; + +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.xml.XmlAttributeValue; +import com.intellij.psi.xml.XmlTag; +import com.intellij.psi.xml.XmlTagValue; +import com.intellij.util.xml.impl.GenericDomValueReference; +import org.jetbrains.annotations.NotNull; + +import java.util.*; + +/** + * @author peter + */ +public class DomReferencesTest extends DomHardCoreTestCase { + + public void testMetaData() throws Throwable { + final MyElement element = createElement(""); + element.getName().setValue("A"); + final XmlTag tag = element.getXmlTag(); + final DomMetaData metaData = assertInstanceOf(tag.getMetaData(), DomMetaData.class); + assertEquals(tag, metaData.getDeclaration()); + assertOrderedEquals(metaData.getDependences(), DomUtil.getFileElement(element), tag); + assertEquals("A", metaData.getName()); + assertEquals("A", metaData.getName(null)); + + metaData.setName("B"); + assertEquals("B", element.getName().getValue()); + } + + public void testNameReference() throws Throwable { + final MyElement element = createElement("abc"); + final DomTarget target = DomTarget.getTarget(element); + assertNotNull(target); + final XmlTag tag = element.getName().getXmlTag(); + assertNull(tag.getContainingFile().findReferenceAt(tag.getValue().getTextRange().getStartOffset())); + } + + public void testProcessingInstruction() throws Throwable { + createElement("").getXmlTag().accept(new PsiRecursiveElementVisitor() { + @Override public void visitElement(PsiElement element) { + super.visitElement(element); + for (final PsiReference reference : element.getReferences()) { + assertFalse(reference instanceof GenericDomValueReference); + } + } + }); + } + + public void testBooleanReference() throws Throwable { + final MyElement element = createElement("true"); + assertVariants(assertReference(element.getBoolean()), "false", "true"); + } + + public void testBooleanAttributeReference() throws Throwable { + final MyElement element = createElement(""); + final PsiReference reference = getReference(element.getBooleanAttribute()); + assertVariants(reference, "false", "true"); + + final XmlAttributeValue xmlAttributeValue = element.getBooleanAttribute().getXmlAttributeValue(); + final PsiElement psiElement = reference.getElement(); + assertEquals(xmlAttributeValue, psiElement); + + assertEquals(new TextRange(0, "true".length()).shiftRight(1), reference.getRangeInElement()); + } + + public void testEnumReference() throws Throwable { + assertVariants(assertReference(createElement("239").getEnum(), null), "A", "B", "C"); + assertVariants(assertReference(createElement("A").getEnum()), "A", "B", "C"); + } + + public void testPsiClass() throws Throwable { + final MyElement element = createElement("java.lang.String"); + assertReference(element.getPsiClass(), PsiType.getJavaLangString(getPsiManager(), GlobalSearchScope.allScope(getProject())).resolve(), + element.getPsiClass().getXmlTag().getValue().getTextRange().getEndOffset() - 1); + } + + public void testPsiType() throws Throwable { + final MyElement element = createElement("java.lang.String"); + assertReference(element.getPsiType(), PsiType.getJavaLangString(getPsiManager(), GlobalSearchScope.allScope(getProject())).resolve()); + } + + public void testIndentedPsiType() throws Throwable { + final MyElement element = createElement(" java.lang.Strin "); + final PsiReference psiReference = assertReference(element.getPsiType(), null); + assertEquals(new TextRange(22, 22 + "Strin".length()), psiReference.getRangeInElement()); + } + + public void testPsiPrimitiveType() throws Throwable { + final MyElement element = createElement("int"); + assertReference(element.getPsiType()); + } + + public void testPsiPrimitiveTypeArray() throws Throwable { + final MyElement element = createElement("int[]"); + final GenericDomValue value = element.getPsiType(); + final XmlTagValue tagValue = value.getXmlTag().getValue(); + final int i = tagValue.getText().indexOf(value.getStringValue()); + assertReference(value, value.getXmlTag(), tagValue.getTextRange().getStartOffset() + i + "int".length()); + } + + public void testPsiUnknownType() throws Throwable { + final MyElement element = createElement("#$^%*$"); + assertReference(element.getPsiType(), null); + } + + public void testPsiArrayType() throws Throwable { + final MyElement element = createElement("java.lang.String[]"); + final XmlTag tag = element.getPsiType().getXmlTag(); + final TextRange valueRange = tag.getValue().getTextRange(); + final PsiReference reference = tag.getContainingFile().findReferenceAt(valueRange.getStartOffset() + "java.lang.".length()); + assertNotNull(reference); + assertEquals(PsiType.getJavaLangString(getPsiManager(), GlobalSearchScope.allScope(getProject())).resolve(), reference.resolve()); + assertEquals("java.lang.".length(), reference.getRangeInElement().getStartOffset()); + assertEquals("String".length(), reference.getRangeInElement().getLength()); + } + + public void testJvmArrayType() throws Throwable { + final MyElement element = createElement("[Ljava.lang.String;"); + final XmlTag tag = element.getJvmPsiType().getXmlTag(); + final TextRange valueRange = tag.getValue().getTextRange(); + final PsiReference reference = tag.getContainingFile().findReferenceAt(valueRange.getEndOffset() - 1); + assertNotNull(reference); + assertEquals(PsiType.getJavaLangString(getPsiManager(), GlobalSearchScope.allScope(getProject())).resolve(), reference.resolve()); + assertEquals("[Ljava.lang.".length(), reference.getRangeInElement().getStartOffset()); + assertEquals("String".length(), reference.getRangeInElement().getLength()); + } + + public void testCustomResolving() throws Throwable { + final MyElement element = createElement("239"); + assertVariants(assertReference(element.getStringBuffer()), "239", "42", "foo", "zzz"); + } + + public void testAdditionalValues() throws Throwable { + final MyElement element = createElement("zzz"); + final XmlTag tag = element.getStringBuffer().getXmlTag(); + assertTrue(tag.getContainingFile().findReferenceAt(tag.getValue().getTextRange().getStartOffset()).isSoft()); + } + + public interface MyElement extends DomElement { + GenericDomValue getBoolean(); + + GenericAttributeValue getBooleanAttribute(); + + @Convert(MyStringConverter.class) + GenericDomValue getConvertedString(); + + GenericDomValue getEnum(); + + @NameValue GenericDomValue getName(); + + GenericDomValue getPsiClass(); + + GenericDomValue getPsiType(); + + @Convert(JvmPsiTypeConverter.class) + GenericDomValue getJvmPsiType(); + + List> getEnumChildren(); + + @Convert(MyStringBufferConverter.class) + GenericDomValue getStringBuffer(); + + MyAbstractElement getChild(); + + MyElement getRecursiveChild(); + + List getMyGenericValues(); + + MyGenericValue getMyAnotherGenericValue(); + } + + @Convert(MyStringConverter.class) + public interface MyGenericValue extends GenericDomValue { + + } + + public interface MySomeInterface { + GenericValue getFoo(); + } + + public interface MyAbstractElement extends DomElement { + GenericAttributeValue getFubar239(); + GenericAttributeValue getFubar(); + } + + public interface MyFooElement extends MyAbstractElement, MySomeInterface { + @Override + GenericDomValue getFoo(); + + GenericAttributeValue getFubar2(); + } + + public interface MyBarElement extends MyAbstractElement { + GenericDomValue getBar(); + } + + + public enum MyEnum { + A,B,C + } + + public static class MyStringConverter extends ResolvingConverter { + + @Override + @NotNull + public Collection getVariants(final ConvertContext context) { + return Collections.emptyList(); + } + + @Override + public String fromString(final String s, final ConvertContext context) { + return s; + } + + @Override + public String toString(final String s, final ConvertContext context) { + return s; + } + } + + public static class MyStringBufferConverter extends ResolvingConverter { + + @Override + public StringBuffer fromString(final String s, final ConvertContext context) { + return s == null ? null : new StringBuffer(s); + } + + @Override + public String toString(final StringBuffer t, final ConvertContext context) { + return t == null ? null : t.toString(); + } + + @Override + public Collection getVariants(final ConvertContext context) { + return Arrays.asList(new StringBuffer("239"), new StringBuffer("42"), new StringBuffer("foo")); + } + + @Override + public Set getAdditionalVariants() { + return Collections.singleton("zzz"); + } + } +} diff --git a/xml/dom-tests/tests/com/intellij/util/xml/DomSaxParserTest.java b/xml/dom-tests/tests/com/intellij/util/xml/DomSaxParserTest.java new file mode 100644 index 000000000000..93c5e11271ce --- /dev/null +++ b/xml/dom-tests/tests/com/intellij/util/xml/DomSaxParserTest.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2000-2005 by JetBrains s.r.o. All Rights Reserved. + * Use is subject to license terms. + */ +package com.intellij.util.xml; + +import com.intellij.lang.xml.XMLLanguage; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiFileFactory; +import com.intellij.psi.xml.XmlFile; +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; +import org.jetbrains.annotations.Nullable; +import org.xml.sax.SAXException; + +import java.io.IOException; + +/** + * @author peter + */ +public class DomSaxParserTest extends LightCodeInsightFixtureTestCase { + + public void testGetRootTagNameWithoutNamespace() throws Throwable { + assertData("", "root", null, null, null); + } + + public void testGetRootTagNameWithNamespaceWithEmptyPrefix() throws Throwable { + assertData("", "root", "foo", null, null); + } + + public void testGetRootTagNameWithUnfinishedAttribute() throws Throwable { + XmlFile file = createXmlFile(""); + ensureParsed(file); + final XmlFileHeader header = DomService.getInstance().getXmlFileHeader(file); + assertEquals(new XmlFileHeader("root", "foo", null, null), header); + } + + public void testGetRootTagNameWithNamespaceWithNonEmptyPrefix() throws Throwable { + assertData("", "root", "b", null, null); + } + + public void testGetRootTagNameWithDtdNamespace() throws Throwable { + assertData("", "root", null, "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN", "http://java.sun.com/dtd/ejb-jar_2_0.dtd"); + } + + public void testGetRootTagNameWithDtdNamespace2() throws Throwable { + assertData("\n" + + "", "root", null, "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN", "http://java.sun.com/dtd/ejb-jar_2_0.dtd"); + } + + public void testNoTag() throws Throwable { + assertData("aaaaaaaaaaaaaaaaaaaaa", null, null, null, null); + } + + public void testEmptyFile() throws Throwable { + assertData("", null, null, null, null); + } + + public void testInvalidContent() throws Throwable { + assertData("", "root", null, null, null); + } + + public void testSpring() throws Throwable { + assertData("\n" + "\n" + "\n" + + "\n" + + "", "beans", "http://www.springframework.org/schema/beans", null, null); + } + + public void testInternalDtd() throws Throwable { + assertData("\n" + + "\n" + "%globals;\n" + "]>", "a", null, null, "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"); + } + + private void assertData(final String start, @Nullable final String localName, @Nullable String namespace, @Nullable String publicId, @Nullable String systemId) throws IOException, SAXException { + XmlFileHeader expected = new XmlFileHeader(localName, namespace, publicId, systemId); + + XmlFile file = createXmlFile(start); + assert !file.getNode().isParsed(); + assertEquals(expected, DomService.getInstance().getXmlFileHeader(file)); + + ensureParsed(file); + assert file.getNode().isParsed(); + assertEquals(expected, DomService.getInstance().getXmlFileHeader(file)); + } + + private XmlFile createXmlFile(String text) { + return (XmlFile)PsiFileFactory.getInstance(getProject()).createFileFromText("a.xml", XMLLanguage.INSTANCE, text, false, false, false); + } +} diff --git a/xml/dom-tests/tests/com/intellij/util/xml/DomSimpleValuesTest.java b/xml/dom-tests/tests/com/intellij/util/xml/DomSimpleValuesTest.java new file mode 100644 index 000000000000..4fcdcf0d91d1 --- /dev/null +++ b/xml/dom-tests/tests/com/intellij/util/xml/DomSimpleValuesTest.java @@ -0,0 +1,539 @@ +package com.intellij.util.xml; + +import com.intellij.openapi.application.Result; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.xml.XmlTag; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.xml.events.DomEvent; +import com.intellij.util.xml.ui.DomUIFactory; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +/** + * @author peter + */ +public class DomSimpleValuesTest extends DomTestCase { + + private MyElement createElement(final String xml) throws IncorrectOperationException { + return createElement(xml, MyElement.class); + } + + public void testGetValue() throws Throwable { + final String text = "foo"; + assertEquals("foo", createElement(text).getTagValue()); + assertEquals("foo", createElement(text).getValue()); + } + + public void testSetValue() throws Throwable { + final MyElement element = createElement(""); + assertEquals("", element.getValue()); + element.setValue(239); + assertEquals("239", element.getValue()); + assertEquals("239", element.getXmlTag().getValue().getText()); + myCallRegistry.putExpected(new DomEvent(element, false)); + + myCallRegistry.assertResultsAndClear(); + } + + public void testDefineAndSet() throws Throwable { + final MyElement element = getDomManager().getFileElement(createXmlFile(""), MyElement.class, "root").getRootElement(); + myCallRegistry.clear(); + assertNull(element.getXmlTag()); + element.setValue(42); + assertNotNull(element.getXmlTag()); + assertEquals("42", element.getXmlTag().getValue().getText()); + final DomElement element1 = element; + myCallRegistry.putExpected(new DomEvent(element1, true)); + myCallRegistry.putExpected(new DomEvent(element, false)); + + element.setValue((Integer)null); + assertNull(element.getXmlTag()); + assertEquals(null, element.getValue()); + + myCallRegistry.putExpected(new DomEvent(element, false)); + myCallRegistry.assertResultsAndClear(); + } + + + public void testSimpleConverters() throws Throwable { + assertEquals(239, createElement("239").getInt()); + assertEquals(true, createElement("true").getBoolean()); + assertEquals("true", createElement("true").getBuffer().toString()); + + try { + createElement("true").getInt(); + fail(); + } + catch (NullPointerException e) { + } + try { + createElement("42").getBoolean(); + fail(); + } + catch (NullPointerException e) { + } + } + + public void testComment() throws Throwable { + assertEquals(239, createElement("" + + " " + + " 239" + + " " + + "").getInt()); + } + + public void testPsiClassConverter() throws Throwable { + final String className = Object.class.getName(); + final PsiClass objectClass = getJavaFacade().findClass(className, GlobalSearchScope.allScope(getProject())); + assertEquals(objectClass, createElement("" + className + "").getPsiClass()); + + assertNull(createElement("abcdef").getPsiClass()); + } + + public void testEnums() throws Throwable { + final MyElement element = createElement("", MyElement.class); + assertNull(element.getEnum()); + + element.setEnum(DomTestCase.MyEnum.BAR); + assertEquals(DomTestCase.MyEnum.BAR, element.getEnum()); + assertEquals(DomTestCase.MyEnum.BAR.getValue(), element.getXmlTag().getValue().getText()); + + element.setEnum(null); + assertNull(element.getEnum()); + assertNull(element.getXmlTag()); + + element.setValue(239); + assertNull(element.getEnum()); + } + + public void testAttributeValues() throws Throwable { + final MyElement element = createElement(""); + final GenericAttributeValue attributeValue = element.getAttributeValue(); + assertEquals("attra", attributeValue.getXmlElementName()); + assertEquals("foo", attributeValue.getValue()); + + final GenericAttributeValue attr = element.getAttr(); + attr.setValue(239); + assertEquals(239, (int)attr.getValue()); + assertEquals("239", element.getXmlTag().getAttributeValue("attr")); + final DomElement element1 = attr; + myCallRegistry.putExpected(new DomEvent(element1, true)); + myCallRegistry.assertResultsAndClear(); + + attr.setValue(42); + myCallRegistry.putExpected(new DomEvent(attr, false)); + myCallRegistry.assertResultsAndClear(); + + attr.setValue(null); + assertNull(attr.getValue()); + assertNull(element.getXmlTag().getAttributeValue("attr")); + assertNull(element.getXmlTag().getAttribute("attr", "")); + myCallRegistry.putExpected(new DomEvent(attr, false)); + myCallRegistry.assertResultsAndClear(); + + assertEquals("some-attribute", element.getSomeAttribute().getXmlElementName()); + + assertNull(createElement("").getAttributeValue().getStringValue()); + assertNull(createElement("").getAttributeValue().getStringValue()); + assertNull(createElement("").getAttributeValue().getStringValue()); + } + + public void testGenericValue() throws Throwable { + final MyElement element = createElement("239"); + final GenericDomValue integerChild = element.getGenericChild(); + assertEquals(239, (int)integerChild.getValue()); + assertEquals("239", integerChild.getStringValue()); + integerChild.setValue(42); + assertEquals(42, (int)integerChild.getValue()); + assertEquals("42", integerChild.getStringValue()); + } + + public void testAnnotatedGenericValue() throws Throwable { + final MyElement element = createElement("239"); + element.getGenericChild().getValue(); + final GenericDomValue genericChild2 = element.getGenericChild2(); + assertEquals("239", genericChild2.getValue().toString()); + } + + public void testSpecialCharacters() throws Throwable { + final MyElement element = createElement(""); + element.setValue("<"); + assertEquals("<", element.getValue()); + assertEquals("", element.getXmlTag().getValue().getText()); + + element.getAttributeValue().setValue("<"); + assertEquals("<", element.getAttributeValue().getValue()); + assertEquals("\"<\"", element.getXmlTag().getAttribute("attra", null).getValueElement().getText()); + } + + public void testIndicators() throws Throwable { + final MyElement element = createElement(""); + final GenericDomValue indicator = element.getIndicator(); + assertTrue(indicator.getValue()); + + indicator.setValue(false); + assertFalse(indicator.getValue()); + assertNull(indicator.getStringValue()); + assertNull(indicator.getXmlTag()); + assertEquals(0, element.getXmlTag().getSubTags().length); + putExpected(new DomEvent(indicator, false)); + assertResultsAndClear(); + + indicator.setValue(true); + assertTrue(indicator.getValue()); + assertEquals("", indicator.getStringValue()); + assertSame(indicator.getXmlTag(), element.getXmlTag().getSubTags()[0]); + final DomElement element1 = indicator; + putExpected(new DomEvent(element1, true)); + assertResultsAndClear(); + + final XmlTag tag = element.getXmlTag(); + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + tag.add(createTag("")); + tag.add(createTag("")); + } + }.execute(); + + assertTrue(element.isValid()); + assertTrue(element.getIndicator().getValue()); + element.getIndicator().setValue(false); + assertFalse(element.getIndicator().getValue()); + assertEquals(0, element.getXmlTag().findSubTags("indicator").length); + } + + public void testConcreteGenericValue() throws Throwable { + final ConcreteGeneric generic = createElement("", ConcreteGeneric.class); + generic.setValue("abc"); + assertEquals("abc", generic.getValue()); + + DomUIFactory.SET_VALUE_METHOD.invoke(generic, "def"); + assertEquals("def", DomUIFactory.GET_VALUE_METHOD.invoke(generic)); + } + + public void testConcreteGenericValueWithMethods() throws Throwable { + final ConcreteGenericWithMethods generic = createElement("", ConcreteGenericWithMethods.class); + generic.setValue("abc"); + assertEquals("abc", generic.getValue()); + + DomUIFactory.SET_VALUE_METHOD.invoke(generic, "def"); + assertEquals("def", DomUIFactory.GET_VALUE_METHOD.invoke(generic)); + } + + public void testNameValueInPresentation() throws Throwable { + final MyElement element = createElement(""); + element.getAttr().setValue(23942); + assertEquals("23942", element.getPresentation().getElementName()); + } + + public void testResolveToDomElement() throws Throwable { + final RootInterface element = createElement("", RootInterface.class); + final MyElement child1 = element.addChild(); + child1.getAttr().setValue(555); + final MyElement child2 = element.addChild(); + child2.getAttr().setValue(777); + + final GenericDomValue resolve = child2.getResolve(); + resolve.setStringValue("777"); + assertEquals(child2, resolve.getValue()); + + resolve.setValue(child1); + assertEquals("555", resolve.getStringValue()); + assertEquals(child1, resolve.getValue()); + + resolve.setStringValue("239"); + assertNull(resolve.getValue()); + + final GenericDomValue resolve2 = child2.getResolve2(); + resolve2.setStringValue("777"); + assertEquals(child2, resolve2.getValue()); + } + + public void testPlainPsiTypeConverter() throws Throwable { + assertNull(createElement("").getPsiType()); + assertSame(PsiType.INT, createElement("int").getPsiType()); + final PsiType psiType = createElement("java.lang.String").getPsiType(); + assertEquals(CommonClassNames.JAVA_LANG_STRING, assertInstanceOf(psiType, PsiClassType.class).getCanonicalText()); + + final PsiType arrayType = createElement("int[]").getPsiType(); + assertTrue(arrayType instanceof PsiArrayType); + assertSame(PsiType.INT, ((PsiArrayType) arrayType).getComponentType()); + } + + public void testJvmPsiTypeConverter() throws Throwable { + assertNull(createElement("").getJvmPsiType()); + assertNotNull(createElement("int").getJvmPsiType()); + final PsiClassType string = PsiType.getJavaLangString(getPsiManager(), GlobalSearchScope.allScope(getProject())); + final PsiType psiType = createElement("java.lang.String").getJvmPsiType(); + assertEquals(CommonClassNames.JAVA_LANG_STRING, assertInstanceOf(psiType, PsiClassType.class).getCanonicalText()); + + final PsiArrayType intArray = assertInstanceOf(createElement("[I").getJvmPsiType(), PsiArrayType.class); + final PsiArrayType stringArray = assertInstanceOf(createElement("[Ljava.lang.String;").getJvmPsiType(), PsiArrayType.class); + assertSame(PsiType.INT, intArray.getComponentType()); + assertEquals(CommonClassNames.JAVA_LANG_STRING, assertInstanceOf(stringArray.getComponentType(), PsiClassType.class).getCanonicalText()); + + assertJvmPsiTypeToString(intArray, "[I"); + assertJvmPsiTypeToString(stringArray, "[Ljava.lang.String;"); + assertJvmPsiTypeToString(string, "java.lang.String"); + } + + public void testValueCaching() throws Throwable { + final GenericDomValue element = createElement("", MyElement.class).getCachedValue(); + assertEquals(0, ((MyConverter) element.getConverter()).fromStringCalls); + assertEquals("", element.getValue()); + assertEquals(1, ((MyConverter) element.getConverter()).fromStringCalls); + assertEquals("", element.getValue()); + assertEquals(1, ((MyConverter) element.getConverter()).fromStringCalls); + element.setValue("1"); + assertEquals(1, ((MyConverter) element.getConverter()).fromStringCalls); + assertEquals("1", element.getValue()); + assertEquals(2, ((MyConverter) element.getConverter()).fromStringCalls); + } + + + private void assertJvmPsiTypeToString(final PsiType type, final String expected) throws IncorrectOperationException { + final MyElement element = createElement(""); + element.setJvmPsiType(type); + assertEquals(expected, element.getValue()); + } + + public void testJavaStyledElement() throws IncorrectOperationException { + JavaStyledElement element = createElement("", JavaStyledElement.class); + assertEquals(element.getJavaStyledAttribute().getXmlElementName(), "javaStyledAttribute"); + } + + public void testGenericValueListConverter() throws Throwable { + final MyElement element = createElement("abc"); + assertEquals("abc", element.getStringBuffers().get(0).getValue().toString()); + } + + public void testConvertAnnotationOnType() throws Throwable { + final MyElement element = + createElement("" + "abc" + "abc" + ""); + assertEquals("bar", element.getMyGenericValue().getValue()); + assertEquals("foo", element.getMyFooGenericValue().getValue()); + } + + public void testEntities() throws Throwable { + final MyElement element = createElement("]>\n" + + "&xxx;+&idgenerator;+&"); + assertEquals("a getChildren(); + MyElement addChild(); + } + + + public interface MyElement extends DomElement { + @Attribute("attra") + GenericAttributeValue getAttributeValue(); + + @NameValue + GenericAttributeValue getAttr(); + + GenericAttributeValue getSomeAttribute(); + + String getValue(); + + void setValue(Integer value); + + @TagValue() + int getInt(); + + void setValue(String value); + + @TagValue() + boolean getBoolean(); + + @TagValue() + PsiType getPsiType(); + + @Convert(JvmPsiTypeConverter.class) + @TagValue() + PsiType getJvmPsiType(); + + @TagValue() + @Convert(JvmPsiTypeConverter.class) + void setJvmPsiType(PsiType psiType); + + @TagValue() + DomTestCase.MyEnum getEnum(); + + @TagValue + void setEnum(DomTestCase.MyEnum value); + + @TagValue() + @Convert(StringBufferConverter.class) + StringBuffer getBuffer(); + + @TagValue() + PsiClass getPsiClass(); + + @TagValue() + String getTagValue(); + + GenericDomValue getGenericChild(); + + @SubTag("buffer") + @Convert(StringBufferConverter.class) + GenericDomValue getGenericChild2(); + + @SubTag(indicator = true) + GenericDomValue getIndicator(); + + @Resolve(MyElement.class) + GenericDomValue getResolve(); + + GenericDomValue getResolve2(); + + @Convert(StringBufferConverter.class) + List> getStringBuffers(); + + @Convert(MyConverter.class) GenericDomValue getCachedValue(); + + MyGenericValue getMyGenericValue(); + + @Convert(FooConverter.class) + MyGenericValue getMyFooGenericValue(); + + } + + @Convert(BarConverter.class) + public interface MyGenericValue extends GenericDomValue { + } + + @NameStrategyForAttributes(JavaNameStrategy.class) + public interface JavaStyledElement extends DomElement { + GenericAttributeValue getJavaStyledAttribute(); + } + + public interface ConcreteGeneric extends GenericDomValue { + } + + public interface ConcreteGenericWithMethods extends GenericDomValue { + @Override + String getValue(); + + @Override + void setValue(String s); + } + + public void testFuhrer() throws Throwable { + final FieldGroup group = createElement("\n" + + "myGroup\n" + + "myField1\n" + + "myField2\n" + + "", + FieldGroup.class); + assertEquals(2, group.getFieldNames().size()); + assertEquals("myField1", group.getFieldNames().get(0).getValue().getName().getValue()); + assertEquals(null, group.getFieldNames().get(1).getValue()); + } + + public interface JavaeeModelElement { + } + + public interface JavaeeDomModelElement extends JavaeeModelElement, DomElement { +} + + public interface FieldGroup extends JavaeeDomModelElement { + + GenericDomValue getGroupName(); + + List getFieldNames(); + + GroupField addFieldName(); + } + + public interface GroupField extends JavaeeDomModelElement { + + @Convert(value = CmpFieldConverter.class) + CmpField getValue(); + + @Convert(value = CmpFieldConverter.class) + void setValue(CmpField value); + } + + public static class CmpFieldConverter extends ResolvingConverter { + + @Override + public CmpField fromString(String s, ConvertContext context) { + return ElementPresentationManager.findByName(getVariants(context), s); + } + + @Override + public String toString(CmpField t, ConvertContext context) { + return t == null ? null : t.getName().getValue(); + } + + @Override + @NotNull + public Collection getVariants(ConvertContext context) { + final DomElement element = context.getInvocationElement(); + return Arrays.asList(createCmpField(null, element), createCmpField("myField1", element), createCmpField("def", element)); + } + + private CmpField createCmpField(String name, DomElement element) { + final CmpField mockElement = element.getManager().createMockElement(CmpField.class, element.getModule(), false); + mockElement.getName().setValue(name); + return mockElement; + } + } + + public interface CmpField extends JavaeeDomModelElement { + @NameValue + public GenericDomValue getName(); + } + + public static class MyConverter extends Converter { + public int fromStringCalls = 0; + + @Override + public String fromString(@Nullable @NonNls String s, final ConvertContext context) { + fromStringCalls++; + return s; + } + + @Override + public String toString(@Nullable String s, final ConvertContext context) { + return s; + } + } + + public static class FooConverter extends Converter { + @Override + public String fromString(@Nullable @NonNls final String s, final ConvertContext context) { + return s == null ? null : "foo"; + } + + @Override + public String toString(@Nullable final String s, final ConvertContext context) { + return s; + } + } + public static class BarConverter extends Converter { + @Override + public String fromString(@Nullable @NonNls final String s, final ConvertContext context) { + return s == null ? null : "bar"; + } + + @Override + public String toString(@Nullable final String s, final ConvertContext context) { + return s; + } + } + +} diff --git a/xml/dom-tests/tests/com/intellij/util/xml/DomTestCase.java b/xml/dom-tests/tests/com/intellij/util/xml/DomTestCase.java new file mode 100644 index 000000000000..5de96bd73550 --- /dev/null +++ b/xml/dom-tests/tests/com/intellij/util/xml/DomTestCase.java @@ -0,0 +1,135 @@ +package com.intellij.util.xml; + +import com.intellij.openapi.application.Result; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.psi.PsiFileFactory; +import com.intellij.psi.XmlElementFactory; +import com.intellij.psi.impl.PsiModificationTrackerImpl; +import com.intellij.psi.xml.XmlElement; +import com.intellij.psi.xml.XmlFile; +import com.intellij.psi.xml.XmlTag; +import com.intellij.testFramework.LightIdeaTestCase; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.xml.events.DomEvent; +import com.intellij.util.xml.impl.DomApplicationComponent; +import com.intellij.util.xml.impl.DomFileElementImpl; +import com.intellij.util.xml.impl.DomInvocationHandler; +import com.intellij.util.xml.impl.DomManagerImpl; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * @author peter + */ +public abstract class DomTestCase extends LightIdeaTestCase { + protected CallRegistry myCallRegistry; + private final DomEventListener myListener = new DomEventListener() { + @Override + public void eventOccured(DomEvent event) { + myCallRegistry.putActual(event); + } + }; + + @Override + protected void setUp() throws Exception { + super.setUp(); + myCallRegistry = new CallRegistry(); + getDomManager().addDomEventListener(myListener, myTestRootDisposable); + } + + @Override + protected void tearDown() throws Exception { + DomApplicationComponent.getInstance().clearImplementations(); + super.tearDown(); + } + + protected void assertCached(final DomElement element, final XmlElement xmlElement) { + assertNotNull(xmlElement); + assertSame(element.getXmlTag(), xmlElement); + final DomInvocationHandler cachedElement = getCachedHandler(xmlElement); + assertNotNull(cachedElement); + assertEquals(element, cachedElement.getProxy()); + } + + protected void assertCached(final DomFileElementImpl element, final XmlFile file) { + assertNotNull(file); + assertEquals(element, getDomManager().getFileElement(file)); + } + + protected XmlTag createTag(final String text) throws IncorrectOperationException { + return XmlElementFactory.getInstance(getProject()).createTagFromText(text); + } + + protected DomManagerImpl getDomManager() { + return (DomManagerImpl)DomManager.getDomManager(getProject()); + } + + protected static XmlFile createXmlFile(@NonNls final String text) throws IncorrectOperationException { + return (XmlFile)createLightFile("a.xml", text); + } + + + protected T createElement(final String xml, final Class aClass) throws IncorrectOperationException { + final DomManagerImpl domManager = getDomManager(); + T element = createElement(domManager, xml, aClass); + myCallRegistry.clear(); + return element; + } + + protected static T createElement(final DomManager domManager, final String xml, final Class aClass) + throws IncorrectOperationException { + final String name = "a.xml"; + final XmlFile file = (XmlFile)PsiFileFactory.getInstance(domManager.getProject()).createFileFromText(name, xml); + final XmlTag tag = file.getDocument().getRootTag(); + final String rootTagName = tag != null ? tag.getName() : "root"; + final T element = domManager.getFileElement(file, aClass, rootTagName).getRootElement(); + assertNotNull(element); + assertSame(tag, element.getXmlTag()); + return element; + } + + protected void putExpected(final DomEvent event) { + myCallRegistry.putExpected(event); + } + + protected void assertResultsAndClear() { + myCallRegistry.assertResultsAndClear(); + } + + protected TypeChooserManager getTypeChooserManager() { + return getDomManager().getTypeChooserManager(); + } + + protected static void incModCount() { + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + ((PsiModificationTrackerImpl)getPsiManager().getModificationTracker()).incCounter(); + } + }.execute(); + } + + @Nullable + public DomInvocationHandler getCachedHandler(XmlElement element) { + final List option = getDomManager().getSemService().getCachedSemElements(DomManagerImpl.DOM_HANDLER_KEY, element); + return option == null || option.isEmpty() ? null : option.get(0); + } + + public enum MyEnum implements NamedEnum { + FOO("foo"), + BAR("bar"); + + private final String myName; + + MyEnum(final String name) { + myName = name; + } + + @Override + public String getValue() { + return myName; + } + } +} diff --git a/xml/dom-tests/tests/com/intellij/util/xml/DomVirtualFileEventsTest.java b/xml/dom-tests/tests/com/intellij/util/xml/DomVirtualFileEventsTest.java new file mode 100644 index 000000000000..5ceb27dc7277 --- /dev/null +++ b/xml/dom-tests/tests/com/intellij/util/xml/DomVirtualFileEventsTest.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2000-2006 JetBrains s.r.o. All Rights Reserved. + */ +package com.intellij.util.xml; + +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.xml.XmlFile; +import com.intellij.testFramework.PsiTestUtil; +import com.intellij.util.xml.events.DomEvent; +import com.intellij.util.xml.impl.DomFileElementImpl; +import org.jetbrains.annotations.NotNull; + +/** + * @author peter + */ +public class DomVirtualFileEventsTest extends DomHardCoreTestCase{ + + @Override + protected void setUp() throws Exception { + super.setUp(); + getDomManager().registerFileDescription(new DomFileDescription(MyElement.class, "a") { + + @Override + public boolean isMyFile(@NotNull final XmlFile file, final Module module) { + return super.isMyFile(file, module) && file.getName().contains("a"); + } + }, getTestRootDisposable()); + } + + public void testCreateFile() throws Throwable { + new WriteCommandAction.Simple(myProject) { + @Override + protected void run() throws Throwable { + final VirtualFile dir = getVirtualFile(createTempDirectory()); + PsiTestUtil.addSourceContentToRoots(getModule(), dir); + final VirtualFile childData = dir.createChildData(this, "abc.xml"); + System.gc(); + System.gc(); + System.gc(); + System.gc(); + assertResultsAndClear(); + VfsUtil.saveText(childData, ""); + assertEventCount(0); + assertResultsAndClear(); + } + }.execute().throwException(); + } + + public void testDeleteFile() throws Throwable { + new WriteCommandAction.Simple(getProject()) { + @Override + protected void run() throws Throwable { + final VirtualFile dir = getVirtualFile(createTempDirectory()); + PsiTestUtil.addSourceContentToRoots(getModule(), dir); + final VirtualFile childData = dir.createChildData(this, "abc.xml"); + assertResultsAndClear(); + VfsUtil.saveText(childData, ""); + final DomFileElementImpl fileElement = getFileElement(childData); + assertResultsAndClear(); + + childData.delete(this); + assertEventCount(1); + putExpected(new DomEvent(fileElement, false)); + assertResultsAndClear(); + assertFalse(fileElement.isValid()); + } + }.execute().throwException(); + } + + public void testRenameFile() throws Throwable { + new WriteCommandAction.Simple(getProject()) { + @Override + protected void run() throws Throwable { + final VirtualFile dir = getVirtualFile(createTempDirectory()); + PsiTestUtil.addSourceContentToRoots(getModule(), dir); + final VirtualFile data = dir.createChildData(this, "abc.xml"); + VfsUtil.saveText(data, ""); + final DomFileElementImpl fileElement = getFileElement(data); + assertEventCount(0); + assertResultsAndClear(); + + data.rename(this, "deaf.xml"); + assertEventCount(1); + putExpected(new DomEvent(fileElement, false)); + assertResultsAndClear(); + assertEquals(fileElement, getFileElement(data)); + assertTrue(fileElement.isValid()); + + data.rename(this, "fff.xml"); + assertEventCount(1); + putExpected(new DomEvent(fileElement, false)); + assertResultsAndClear(); + assertNull(getFileElement(data)); + assertFalse(fileElement.isValid()); + } + }.execute().throwException(); + } + + private DomFileElementImpl getFileElement(final VirtualFile file) { + return getDomManager().getFileElement((XmlFile)getPsiManager().findFile(file)); + } + + public interface MyElement extends DomElement { + } + +} diff --git a/xml/dom-tests/tests/com/intellij/util/xml/MockDomElement.java b/xml/dom-tests/tests/com/intellij/util/xml/MockDomElement.java new file mode 100644 index 000000000000..1c51ea0e4ed7 --- /dev/null +++ b/xml/dom-tests/tests/com/intellij/util/xml/MockDomElement.java @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2000-2006 JetBrains s.r.o. All Rights Reserved. + */ +package com.intellij.util.xml; + +import com.intellij.openapi.module.Module; +import com.intellij.openapi.util.UserDataHolderBase; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.xml.XmlElement; +import com.intellij.psi.xml.XmlTag; +import com.intellij.util.xml.reflect.DomGenericInfo; +import com.intellij.util.xml.reflect.AbstractDomChildrenDescription; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +/** + * @author peter + */ +public class MockDomElement extends UserDataHolderBase implements DomElement{ + @Override + @Nullable + public XmlTag getXmlTag() { + throw new UnsupportedOperationException("Method getXmlTag is not yet implemented in " + getClass().getName()); + } + + @NotNull + public DomFileElement getRoot() { + throw new UnsupportedOperationException("Method getRoot is not yet implemented in " + getClass().getName()); + } + + @Override + @Nullable + public DomElement getParent() { + throw new UnsupportedOperationException("Method getParent is not yet implemented in " + getClass().getName()); + } + + @Override + public XmlTag ensureTagExists() { + throw new UnsupportedOperationException("Method ensureTagExists is not yet implemented in " + getClass().getName()); + } + + @Override + @Nullable + public XmlElement getXmlElement() { + throw new UnsupportedOperationException("Method getXmlElement is not yet implemented in " + getClass().getName()); + } + + @Override + public XmlElement ensureXmlElementExists() { + throw new UnsupportedOperationException("Method ensureXmlElementExists is not yet implemented in " + getClass().getName()); + } + + @Override + public void undefine() { + throw new UnsupportedOperationException("Method undefine is not yet implemented in " + getClass().getName()); + } + + @Override + public boolean isValid() { + throw new UnsupportedOperationException("Method isValid is not yet implemented in " + getClass().getName()); + } + + @Override + @NotNull + public DomGenericInfo getGenericInfo() { + throw new UnsupportedOperationException("Method getGenericInfo is not yet implemented in " + getClass().getName()); + } + + @Override + @NotNull + @NonNls + public String getXmlElementName() { + throw new UnsupportedOperationException("Method getXmlElementName is not yet implemented in " + getClass().getName()); + } + + @Override + @NotNull + @NonNls + public String getXmlElementNamespace() { + throw new UnsupportedOperationException("Method getXmlElementNamespace is not yet implemented in " + getClass().getName()); + } + + @Override + @Nullable + @NonNls + public String getXmlElementNamespaceKey() { + throw new UnsupportedOperationException("Method getXmlElementNamespaceKey is not yet implemented in " + getClass().getName()); + } + + @Override + public void accept(final DomElementVisitor visitor) { + throw new UnsupportedOperationException("Method accept is not yet implemented in " + getClass().getName()); + } + + @Override + public void acceptChildren(DomElementVisitor visitor) { + throw new UnsupportedOperationException("Method acceptChildren is not yet implemented in " + getClass().getName()); + } + + @Override + @NotNull + public DomManager getManager() { + throw new UnsupportedOperationException("Method getManager is not yet implemented in " + getClass().getName()); + } + + @Override + public Type getDomElementType() { + throw new UnsupportedOperationException("Method getDomElementType is not yet implemented in " + getClass().getName()); + } + + @Override + @NotNull + public AbstractDomChildrenDescription getChildDescription() { + throw new UnsupportedOperationException("Method getChildDescription is not yet implemented in " + getClass().getName()); + } + + @Override + public DomNameStrategy getNameStrategy() { + throw new UnsupportedOperationException("Method getNameStrategy is not yet implemented in " + getClass().getName()); + } + + @Override + @NotNull + public ElementPresentation getPresentation() { + throw new UnsupportedOperationException("Method getPresentation is not yet implemented in " + getClass().getName()); + } + + @Override + public GlobalSearchScope getResolveScope() { + throw new UnsupportedOperationException("Method getResolveScope is not yet implemented in " + getClass().getName()); + } + + @Override + @Nullable + public T getParentOfType(Class requiredClass, boolean strict) { + throw new UnsupportedOperationException("Method getParentOfType is not yet implemented in " + getClass().getName()); + } + + @Override + @Nullable + public Module getModule() { + throw new UnsupportedOperationException("Method getModule is not yet implemented in " + getClass().getName()); + } + + @Override + public void copyFrom(DomElement other) { + throw new UnsupportedOperationException("Method copyFrom is not yet implemented in " + getClass().getName()); + } + + @Override + public T createMockCopy(final boolean physical) { + throw new UnsupportedOperationException("Method createMockCopy is not yet implemented in " + getClass().getName()); + } + + @Override + public T createStableCopy() { + return (T)this; + } + + @Override + @Nullable + public T getAnnotation(Class annotationClass) { + throw new UnsupportedOperationException("Method getAnnotation is not yet implemented in " + getClass().getName()); + } +} diff --git a/xml/dom-tests/tests/com/intellij/util/xml/MockDomFileElement.java b/xml/dom-tests/tests/com/intellij/util/xml/MockDomFileElement.java new file mode 100644 index 000000000000..c1319727a8e1 --- /dev/null +++ b/xml/dom-tests/tests/com/intellij/util/xml/MockDomFileElement.java @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2000-2006 JetBrains s.r.o. All Rights Reserved. + */ +package com.intellij.util.xml; + +import com.intellij.openapi.module.Module; +import com.intellij.openapi.util.UserDataHolderBase; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.xml.XmlElement; +import com.intellij.psi.xml.XmlFile; +import com.intellij.psi.xml.XmlTag; +import com.intellij.util.xml.reflect.DomGenericInfo; +import com.intellij.util.xml.reflect.AbstractDomChildrenDescription; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +/** + * @author peter + */ +public class MockDomFileElement extends UserDataHolderBase implements DomFileElement{ + private long myModCount = 0; + private DomFileDescription myFileDescription; + + public void setFileDescription(final DomFileDescription fileDescription) { + myFileDescription = fileDescription; + } + + @Override + @NotNull + public XmlFile getFile() { + throw new UnsupportedOperationException("Method getFile is not yet implemented in " + getClass().getName()); + } + + @Override + @NotNull + public XmlFile getOriginalFile() { + throw new UnsupportedOperationException("Method getOriginalFile is not yet implemented in " + getClass().getName()); + } + + @Override + public XmlTag getRootTag() { + throw new UnsupportedOperationException("Method getRootTag is not yet implemented in " + getClass().getName()); + } + + @Override + @NotNull + public DomElement getRootElement() { + throw new UnsupportedOperationException("Method getRootElement is not yet implemented in " + getClass().getName()); + } + + @Override + public Class getRootElementClass() { + throw new UnsupportedOperationException("Method getRootElementType is not yet implemented in " + getClass().getName()); + } + + @Override + @NotNull + public DomFileDescription getFileDescription() { + return myFileDescription; + } + + @Override + @Nullable + public XmlTag getXmlTag() { + throw new UnsupportedOperationException("Method getXmlTag is not yet implemented in " + getClass().getName()); + } + + @NotNull + public DomFileElement getRoot() { + return (DomFileElement)this; + } + + @Override + @Nullable + public DomElement getParent() { + throw new UnsupportedOperationException("Method getParent is not yet implemented in " + getClass().getName()); + } + + @Override + public XmlTag ensureTagExists() { + throw new UnsupportedOperationException("Method ensureTagExists is not yet implemented in " + getClass().getName()); + } + + @Override + @Nullable + public XmlElement getXmlElement() { + throw new UnsupportedOperationException("Method getXmlElement is not yet implemented in " + getClass().getName()); + } + + @Override + public XmlElement ensureXmlElementExists() { + throw new UnsupportedOperationException("Method ensureXmlElementExists is not yet implemented in " + getClass().getName()); + } + + @Override + public void undefine() { + throw new UnsupportedOperationException("Method undefine is not yet implemented in " + getClass().getName()); + } + + @Override + public boolean isValid() { + throw new UnsupportedOperationException("Method isValid is not yet implemented in " + getClass().getName()); + } + + @Override + @NotNull + public DomGenericInfo getGenericInfo() { + throw new UnsupportedOperationException("Method getGenericInfo is not yet implemented in " + getClass().getName()); + } + + @Override + @NotNull + @NonNls + public String getXmlElementName() { + throw new UnsupportedOperationException("Method getXmlElementName is not yet implemented in " + getClass().getName()); + } + + @Override + @NotNull + @NonNls + public String getXmlElementNamespace() { + throw new UnsupportedOperationException("Method getXmlElementNamespace is not yet implemented in " + getClass().getName()); + } + + @Override + @Nullable + @NonNls + public String getXmlElementNamespaceKey() { + throw new UnsupportedOperationException("Method getXmlElementNamespaceKey is not yet implemented in " + getClass().getName()); + } + + @Override + public void accept(final DomElementVisitor visitor) { + throw new UnsupportedOperationException("Method accept is not yet implemented in " + getClass().getName()); + } + + @Override + public void acceptChildren(DomElementVisitor visitor) { + } + + @Override + @NotNull + public DomManager getManager() { + throw new UnsupportedOperationException("Method getManager is not yet implemented in " + getClass().getName()); + } + + @Override + public Type getDomElementType() { + throw new UnsupportedOperationException("Method getDomElementType is not yet implemented in " + getClass().getName()); + } + + @Override + @NotNull + public AbstractDomChildrenDescription getChildDescription() { + throw new UnsupportedOperationException("Method getChildDescription is not yet implemented in " + getClass().getName()); + } + + @Override + public DomNameStrategy getNameStrategy() { + throw new UnsupportedOperationException("Method getNameStrategy is not yet implemented in " + getClass().getName()); + } + + @Override + @NotNull + public ElementPresentation getPresentation() { + throw new UnsupportedOperationException("Method getPresentation is not yet implemented in " + getClass().getName()); + } + + @Override + public GlobalSearchScope getResolveScope() { + throw new UnsupportedOperationException("Method getResolveScope is not yet implemented in " + getClass().getName()); + } + + @Override + @Nullable + public T getParentOfType(Class requiredClass, boolean strict) { + throw new UnsupportedOperationException("Method getParentOfType is not yet implemented in " + getClass().getName()); + } + + @Override + @Nullable + public Module getModule() { + throw new UnsupportedOperationException("Method getModule is not yet implemented in " + getClass().getName()); + } + + @Override + public void copyFrom(DomElement other) { + throw new UnsupportedOperationException("Method copyFrom is not yet implemented in " + getClass().getName()); + } + + @Override + public T createMockCopy(final boolean physical) { + throw new UnsupportedOperationException("Method createMockCopy is not yet implemented in " + getClass().getName()); + } + + @Override + public T createStableCopy() { + throw new UnsupportedOperationException("Method createStableCopy is not yet implemented in " + getClass().getName()); + } + + @Override + @Nullable + public T getAnnotation(Class annotationClass) { + throw new UnsupportedOperationException("Method getAnnotation is not yet implemented in " + getClass().getName()); + } + + @Override + public long getModificationCount() { + return myModCount; + } + + public void incModificationCount() { + myModCount++; + } +} diff --git a/xml/dom-tests/tests/com/intellij/util/xml/MockGenericAttributeValue.java b/xml/dom-tests/tests/com/intellij/util/xml/MockGenericAttributeValue.java new file mode 100644 index 000000000000..15b7a0468d30 --- /dev/null +++ b/xml/dom-tests/tests/com/intellij/util/xml/MockGenericAttributeValue.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2000-2007 JetBrains s.r.o. All Rights Reserved. + */ +package com.intellij.util.xml; + +import com.intellij.psi.xml.XmlAttribute; +import com.intellij.psi.xml.XmlAttributeValue; +import org.jetbrains.annotations.NotNull; + +/** + * @author peter + */ +public class MockGenericAttributeValue extends MockDomElement implements GenericAttributeValue { + @Override + public XmlAttribute getXmlAttribute() { + throw new UnsupportedOperationException("Method getXmlAttribute is not yet implemented in " + getClass().getName()); + } + + @Override + public XmlAttributeValue getXmlAttributeValue() { + throw new UnsupportedOperationException("Method getXmlAttributeValue is not yet implemented in " + getClass().getName()); + } + + @Override + @NotNull + public Converter getConverter() { + throw new UnsupportedOperationException("Method getConverter is not yet implemented in " + getClass().getName()); + } + + @Override + public void setStringValue(String value) { + throw new UnsupportedOperationException("Method setStringValue is not yet implemented in " + getClass().getName()); + } + + @Override + public void setValue(Object value) { + throw new UnsupportedOperationException("Method setValue is not yet implemented in " + getClass().getName()); + } + + @Override + public String getStringValue() { + throw new UnsupportedOperationException("Method getStringValue is not yet implemented in " + getClass().getName()); + } + + @Override + public String getRawText() { + throw new UnsupportedOperationException("Method getRawText is not yet implemented in " + getClass().getName()); + } + + @Override + public Object getValue() { + throw new UnsupportedOperationException("Method getValue is not yet implemented in " + getClass().getName()); + } +} diff --git a/xml/dom-tests/tests/com/intellij/util/xml/NameStrategyTest.java b/xml/dom-tests/tests/com/intellij/util/xml/NameStrategyTest.java new file mode 100644 index 000000000000..fba2b08e2216 --- /dev/null +++ b/xml/dom-tests/tests/com/intellij/util/xml/NameStrategyTest.java @@ -0,0 +1,44 @@ +package com.intellij.util.xml; + +import junit.framework.TestCase; + +/** + * @author peter + */ +public class NameStrategyTest extends TestCase { + + public void testHyphenStrategy() throws Throwable { + assertEquals("aaa-bbb-ccc", DomNameStrategy.HYPHEN_STRATEGY.convertName("aaaBbbCcc")); + assertEquals("aaa-bbb-ccc", DomNameStrategy.HYPHEN_STRATEGY.convertName("AaaBbbCcc")); + assertEquals("aaa", DomNameStrategy.HYPHEN_STRATEGY.convertName("Aaa")); + assertEquals("aaa", DomNameStrategy.HYPHEN_STRATEGY.convertName("aaa")); + assertEquals("AAA-bbb", DomNameStrategy.HYPHEN_STRATEGY.convertName("AAABbb")); + assertEquals("aaa-BBB", DomNameStrategy.HYPHEN_STRATEGY.convertName("AaaBBB")); + } + + public void testHyphenStrategySplit() throws Throwable { + assertEquals("aaa bbb ccc", DomNameStrategy.HYPHEN_STRATEGY.splitIntoWords("aaa-bbb-ccc")); + assertEquals("Aaa", DomNameStrategy.HYPHEN_STRATEGY.splitIntoWords("Aaa")); + assertEquals("aaa", DomNameStrategy.HYPHEN_STRATEGY.splitIntoWords("aaa")); + assertEquals("AAA bbb", DomNameStrategy.HYPHEN_STRATEGY.splitIntoWords("AAA-bbb")); + assertEquals("aaa BBB", DomNameStrategy.HYPHEN_STRATEGY.splitIntoWords("aaa-BBB")); + } + + public void testJavaStrategy() throws Throwable { + assertEquals("aaaBbbCcc", DomNameStrategy.JAVA_STRATEGY.convertName("aaaBbbCcc")); + assertEquals("aaaBbbCcc", DomNameStrategy.JAVA_STRATEGY.convertName("AaaBbbCcc")); + assertEquals("aaa", DomNameStrategy.JAVA_STRATEGY.convertName("Aaa")); + assertEquals("aaa", DomNameStrategy.JAVA_STRATEGY.convertName("aaa")); + assertEquals("AAABbb", DomNameStrategy.JAVA_STRATEGY.convertName("AAABbb")); + assertEquals("aaaBBB", DomNameStrategy.JAVA_STRATEGY.convertName("AaaBBB")); + } + + public void testJavaStrategySplit() throws Throwable { + assertEquals("aaa bbb ccc", DomNameStrategy.JAVA_STRATEGY.splitIntoWords("aaaBbbCcc")); + assertEquals("aaa", DomNameStrategy.JAVA_STRATEGY.splitIntoWords("Aaa")); + assertEquals("aaa", DomNameStrategy.JAVA_STRATEGY.splitIntoWords("aaa")); + assertEquals("AAA bbb", DomNameStrategy.JAVA_STRATEGY.splitIntoWords("AAABbb")); + assertEquals("aaa BBB", DomNameStrategy.JAVA_STRATEGY.splitIntoWords("aaaBBB")); + } + +} diff --git a/xml/dom-tests/tests/com/intellij/util/xml/ProxyTest.java b/xml/dom-tests/tests/com/intellij/util/xml/ProxyTest.java new file mode 100644 index 000000000000..bf3b3d43ce13 --- /dev/null +++ b/xml/dom-tests/tests/com/intellij/util/xml/ProxyTest.java @@ -0,0 +1,171 @@ +package com.intellij.util.xml; + +import com.intellij.util.ArrayUtil; +import junit.framework.TestCase; +import net.sf.cglib.proxy.AdvancedProxy; +import net.sf.cglib.proxy.InvocationHandler; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @author peter + */ +public class ProxyTest extends TestCase { + + public void testExtendClass() throws Throwable { + final List invocations = new ArrayList(); + Implementation implementation = AdvancedProxy.createProxy(Implementation.class, new Class[]{Interface3.class}, new InvocationHandler(){ + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + invocations.add(method.getName()); + if (Object.class.equals(method.getDeclaringClass())) { + return method.invoke(this, args); + } + return Implementation.class.getMethod("getField").invoke(proxy); + } + }, "239"); + implementation.hashCode(); + implementation.method(); + assertEquals("239", implementation.getFoo()); + implementation.setField("42"); + assertEquals("42", implementation.getBar()); + assertEquals("42", implementation.toString()); + assertEquals(Arrays.asList("hashCode", "getFoo", "getFoo", "getBar"), invocations); + + assertEquals("42", Interface1.class.getMethod("getFoo").invoke(implementation)); + + assertEquals("42", Interface3.class.getMethod("bar").invoke(implementation)); + + assertEquals("42", Interface1.class.getMethod("foo").invoke(implementation)); + assertEquals("42", Interface2.class.getMethod("foo").invoke(implementation)); + assertEquals("42", Interface2.class.getMethod("foo").invoke(implementation)); + assertEquals("42", Implementation.class.getMethod("foo").invoke(implementation)); + } + + public interface Interface1 { + Object getFoo(); + Object foo(); + } + + public interface Interface2 extends Interface1 { + @Override + CharSequence getFoo(); + @Override + CharSequence foo(); + } + + public interface Interface3 extends Interface1 { + @Override + String foo(); + String bar(); + } + + private abstract static class Implementation implements Interface2 { + private String myField; + + protected Implementation(final String field) { + myField = field; + } + + public String getField() { + return myField; + } + + public void setField(final String field) { + myField = field; + } + + public void method() { + getFoo(); + } + + public String toString() { + return myField; + } + + @Override + public abstract String getFoo(); + + public abstract String getBar(); + + } + + public void testAddInterfaces() throws Throwable { + final BaseImpl proxy = AdvancedProxy.createProxy(BaseImpl.class, BaseIEx.class); + assertEquals(proxy.sayA(), "a"); + assertEquals(((BaseI)proxy).sayA(), "a"); + assertEquals(((BaseIEx)proxy).sayA(), "a"); + } + + public interface BaseI { + Object sayA(); + } + + public interface BaseIEx extends BaseI { + @Override + CharSequence sayA(); + } + + public static abstract class BaseImpl implements BaseI { + @Override + public String sayA() { + return "a"; + } + } + + public static abstract class AbstractBase implements BaseI { + @Override + public abstract String sayA(); + + public abstract static class AbstractBaseImpl extends AbstractBase {} + } + + public void testCovariantFromInterface() throws Throwable { + final AbstractBase.AbstractBaseImpl proxy = AdvancedProxy.createProxy(AbstractBase.AbstractBaseImpl.class, ArrayUtil.EMPTY_CLASS_ARRAY, + new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + return "a"; + } + }, false, new Object[0]); + assertEquals(proxy.sayA(), "a"); + assertEquals(((AbstractBase)proxy).sayA(), "a"); + assertEquals(((BaseI)proxy).sayA(), "a"); + } + + public static class CovariantFromBaseClassTest { + public static interface Intf { + String sayA(); + } + + public static class Base { + public Object sayA() { + return "beeee"; + } + } + + public abstract static class Impl extends Base implements Intf { + @Override + public abstract String sayA(); + } + } + + public void testCovariantFromBaseClass() throws Throwable { + final CovariantFromBaseClassTest.Impl proxy = AdvancedProxy.createProxy(CovariantFromBaseClassTest.Impl.class, + ArrayUtil.EMPTY_CLASS_ARRAY, new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + return "a"; + } + }, false, new Object[0]); + assertEquals(proxy.sayA(), "a"); + assertEquals(((CovariantFromBaseClassTest.Base)proxy).sayA(), "a"); + assertEquals(((CovariantFromBaseClassTest.Intf)proxy).sayA(), "a"); + } + + +} diff --git a/xml/dom-tests/tests/com/intellij/util/xml/SimpleValuesIncrementalUpdateTest.java b/xml/dom-tests/tests/com/intellij/util/xml/SimpleValuesIncrementalUpdateTest.java new file mode 100644 index 000000000000..fe4038235825 --- /dev/null +++ b/xml/dom-tests/tests/com/intellij/util/xml/SimpleValuesIncrementalUpdateTest.java @@ -0,0 +1,81 @@ +package com.intellij.util.xml; + +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.xml.events.DomEvent; + +/** + * @author peter + */ +public class SimpleValuesIncrementalUpdateTest extends DomTestCase{ + + public void testAttributeChange() throws Throwable { + final MyElement element = createElement(""); + element.getXmlTag().setAttribute("attr", "foo"); + putExpected(new DomEvent(element, false)); + assertResultsAndClear(); + assertTrue(element.getAttr().isValid()); + + element.getXmlTag().setAttribute("bttr", "foo"); + element.getXmlTag().setAttribute("attr", "bar"); + putExpected(new DomEvent(element, false)); + putExpected(new DomEvent(element, false)); + assertResultsAndClear(); + assertTrue(element.getAttr().isValid()); + + element.getXmlTag().setAttribute("attr", null); + putExpected(new DomEvent(element, false)); + assertResultsAndClear(); + assertTrue(element.getAttr().isValid()); + } + + public void testAttributeValueChangeAsXmlElementChange() throws Throwable { + final MyElement element = createElement(""); + final GenericAttributeValue attr = element.getAttr(); + attr.getXmlAttributeValue().getFirstChild().replace(createTag("").getAttribute("attr", null).getValueElement().getFirstChild()); + putExpected(new DomEvent(element, false)); + assertResultsAndClear(); + assertTrue(attr.isValid()); + } + + public void testTagValueChange() throws Throwable { + final MyElement element = createElement(" ").getChild(); + element.getXmlTag().getValue().setText("abc"); + putExpected(new DomEvent(element, false)); + assertResultsAndClear(); + + element.getXmlTag().getValue().setText(null); + putExpected(new DomEvent(element, false)); + assertResultsAndClear(); + } + + public void testAttrXmlEmptyUri() throws Throwable { + final MyElement element = createElement("" , MyElement.class); + getDomManager().getDomFileDescription(element.getXmlElement()).registerNamespacePolicy("foo", "foo"); + + final GenericAttributeValue attr = element.getNsChild().getAttr(); + attr.getXmlTag().setAttribute("attr", "42"); + putExpected(new DomEvent(element.getNsChild(), false)); + assertResultsAndClear(); + } + + private MyElement createElement(final String xml) throws IncorrectOperationException { + return createElement(xml, MyElement.class); + } + + public interface MyElement extends DomElement{ + GenericAttributeValue getAttr(); + + Integer getValue(); + + MyElement getChild(); + + MyNsElement getNsChild(); + } + + @Namespace("foo") + public interface MyNsElement extends DomElement{ + GenericAttributeValue getAttr(); + + } + +} diff --git a/xml/dom-tests/tests/com/intellij/util/xml/StringBufferConverter.java b/xml/dom-tests/tests/com/intellij/util/xml/StringBufferConverter.java new file mode 100644 index 000000000000..2a8df5c910b2 --- /dev/null +++ b/xml/dom-tests/tests/com/intellij/util/xml/StringBufferConverter.java @@ -0,0 +1,17 @@ +package com.intellij.util.xml; + +/** + * @author peter + */ +public class StringBufferConverter extends Converter { + @Override + public StringBuffer fromString(final String s, final ConvertContext context) { + return s==null?null:new StringBuffer(s); + } + + @Override + public String toString(final StringBuffer t, final ConvertContext context) { + return t==null?null:t.toString(); + } + +} diff --git a/xml/dom-tests/tests/com/intellij/util/xml/impl/IncrementalUpdateEventsTest.java b/xml/dom-tests/tests/com/intellij/util/xml/impl/IncrementalUpdateEventsTest.java new file mode 100644 index 000000000000..9df3cceb1849 --- /dev/null +++ b/xml/dom-tests/tests/com/intellij/util/xml/impl/IncrementalUpdateEventsTest.java @@ -0,0 +1,133 @@ +package com.intellij.util.xml.impl; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.psi.xml.XmlTag; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.xml.DomElement; +import com.intellij.util.xml.DomTestCase; +import com.intellij.util.xml.SubTag; +import com.intellij.util.xml.events.DomEvent; + +import java.util.List; + +/** + * @author peter + */ +public class IncrementalUpdateEventsTest extends DomTestCase { + private MyElement myElement; + + @Override + protected void setUp() throws Exception { + super.setUp(); + myElement = createElement(""); + } + + public void testRemove0() throws IncorrectOperationException { + deleteTag(0); + putExpected(new DomEvent(myElement, false)); + assertResultsAndClear(); + + deleteTag(0); + putExpected(new DomEvent(myElement, false)); + assertResultsAndClear(); + + deleteTag(0); + putExpected(new DomEvent(myElement, false)); + assertResultsAndClear(); + + deleteTag(0); + putExpected(new DomEvent(myElement, false)); + assertResultsAndClear(); + } + + public void testRemove1() throws IncorrectOperationException { + deleteTag(1); + putExpected(new DomEvent(myElement, false)); + assertResultsAndClear(); + } + + public void testRemove2() throws IncorrectOperationException { + deleteTag(2); + putExpected(new DomEvent(myElement, false)); + assertResultsAndClear(); + + deleteTag(2); + putExpected(new DomEvent(myElement, false)); + assertResultsAndClear(); + } + + public void testRemove3() throws IncorrectOperationException { + deleteTag(3); + putExpected(new DomEvent(myElement, false)); + assertResultsAndClear(); + } + + public void testAdd0() throws Throwable { + addChildTag(0); + putExpected(new DomEvent(myElement, false)); + assertResultsAndClear(); + } + + public void testAdd1() throws Throwable { + addChildTag(1); + putExpected(new DomEvent(myElement, false)); + assertResultsAndClear(); + } + + public void testAdd2() throws Throwable { + addChildElementTag(2); + putExpected(new DomEvent(myElement, false)); + assertResultsAndClear(); + } + + public void testAdd3() throws Throwable { + addChildElementTag(3); + putExpected(new DomEvent(myElement, false)); + assertResultsAndClear(); + } + + public void testAdd4() throws Throwable { + final XmlTag tag = myElement.getXmlTag(); + tag.addAfter(createTag(""), tag.getSubTags()[3]); + putExpected(new DomEvent(myElement, false)); + assertResultsAndClear(); + } + + private MyElement getChild(final int index) { + return myElement.getChildElements().get(index); + } + + private void addChildTag(int index) throws IncorrectOperationException { + final XmlTag tag = myElement.getXmlTag(); + tag.addBefore(createTag(""), tag.getSubTags()[index]); + } + + private void addChildElementTag(int index) throws IncorrectOperationException { + final XmlTag tag = myElement.getXmlTag(); + tag.addBefore(createTag(""), tag.getSubTags()[index]); + } + + + private void deleteTag(final int index) throws IncorrectOperationException { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + public void run() { + myElement.getXmlTag().getSubTags()[index].delete(); + } + }); + } + + private MyElement createElement(final String xml) throws IncorrectOperationException { + return createElement(xml, MyElement.class); + } + + public interface MyElement extends DomElement { + MyElement getChild(); + + @SubTag(value = "child", index = 1) + MyElement getChild2(); + + List getChildElements(); + } + + +} diff --git a/xml/dom-tests/tests/com/intellij/util/xml/impl/TreeIncrementalUpdateTest.java b/xml/dom-tests/tests/com/intellij/util/xml/impl/TreeIncrementalUpdateTest.java new file mode 100644 index 000000000000..7d69ccfa93a9 --- /dev/null +++ b/xml/dom-tests/tests/com/intellij/util/xml/impl/TreeIncrementalUpdateTest.java @@ -0,0 +1,694 @@ +package com.intellij.util.xml.impl; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.Result; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.XmlElementFactory; +import com.intellij.psi.xml.XmlFile; +import com.intellij.psi.xml.XmlTag; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.xml.*; +import com.intellij.util.xml.events.DomEvent; + +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.List; + +/** + * @author peter + */ +public class TreeIncrementalUpdateTest extends DomTestCase { + + public void testRenameCollectionTag() throws Throwable { + final MyElement rootElement = createPhysicalElement( + "\n" + "\n" + " \n" + " \n" + " \n" + ""); + myCallRegistry.clear(); + assertEquals(1, rootElement.getBoys().size()); + assertEquals(1, rootElement.getGirls().size()); + final MyElement oldBoy = rootElement.getBoys().get(0); + final XmlTag tag = oldBoy.getXmlTag(); + assertNotNull(tag); + final int offset = tag.getTextOffset(); + final int endoffset = offset+tag.getTextLength(); + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + rootElement.getGirls().get(0).undefine(); + final Document document = getDocument(DomUtil.getFile(rootElement)); + PsiDocumentManager.getInstance(getProject()).doPostponedOperationsAndUnblockDocument(document); + document.replaceString(offset+1, offset+1+"boy".length(), "girl"); + commitDocument(document); + } + }.execute(); + assertFalse(oldBoy.isValid()); + assertEquals(0, rootElement.getBoys().size()); + assertEquals(1, rootElement.getGirls().size()); + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + final Document document = getDocument(DomUtil.getFile(rootElement)); + document.replaceString(endoffset - "boy".length(), endoffset, "girl"); + commitDocument(document); + } + }.execute(); + assertEquals(0, rootElement.getBoys().size()); + assertEquals(1, rootElement.getGirls().size()); + } + + private MyElement createPhysicalElement(final String text) throws IncorrectOperationException { + final XmlFile file = (XmlFile)createFile("file.xml", text); + final DomFileElementImpl fileElement = getDomManager().getFileElement(file, MyElement.class, "a"); + final MyElement rootElement = fileElement.getRootElement(); + return rootElement; + } + + public void testRenameFixedTag() throws Throwable { + final XmlFile file = (XmlFile)createFile("file.xml", "\n" + + "\n" + + " \n" + + " \n" + + " \n" + + ""); + final DomFileElementImpl fileElement = getDomManager().getFileElement(file, MyElement.class, "a"); + myCallRegistry.clear(); + final MyElement rootElement = fileElement.getRootElement(); + assertNotNull(rootElement.getAboy().getXmlElement()); + assertNotNull(rootElement.getAgirl().getXmlElement()); + final MyElement oldBoy = rootElement.getAboy(); + final XmlTag tag = oldBoy.getXmlTag(); + assertNotNull(tag); + final int offset = tag.getTextOffset(); + final int endoffset = offset+tag.getTextLength(); + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + rootElement.getAgirl().undefine(); + final Document document = getDocument(file); + PsiDocumentManager.getInstance(getProject()).doPostponedOperationsAndUnblockDocument(document); + document.replaceString(offset+1, offset+1+"aboy".length(), "agirl"); + commitDocument(document); + } + }.execute(); + assertFalse(oldBoy.isValid()); + assertNull(rootElement.getAboy().getXmlElement()); + assertNotNull(rootElement.getAgirl().getXmlElement()); + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + final Document document = getDocument(file); + document.replaceString(endoffset - "aboy".length(), endoffset, "agirl"); + commitDocument(document); + } + }.execute(); + assertNull(rootElement.getAboy().getXmlElement()); + assertNotNull(rootElement.getAgirl().getXmlElement()); + } + + public void testDocumentChange() throws Throwable { + final XmlFile file = (XmlFile)createFile("file.xml", "\n" + + "\n" + + " \n" + + " \n" + + " \n" + + ""); + final DomFileElementImpl fileElement = + getDomManager().getFileElement(file, MyElement.class, "a"); + myCallRegistry.clear(); + final MyElement rootElement = fileElement.getRootElement(); + final MyElement oldLeaf = rootElement.getChild().getChild(); + final XmlTag oldLeafTag = oldLeaf.getXmlTag(); + + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + final Document document = getDocument(file); + document.replaceString(0, document.getText().length(), ""); + commitDocument(document); + } + }.execute(); + assertFalse(oldLeafTag.isValid()); + + putExpected(new DomEvent(fileElement, false)); + assertResultsAndClear(); + + assertEquals(fileElement.getRootElement(), rootElement); + assertTrue(rootElement.isValid()); + + assertFalse(oldLeaf.isValid()); + + assertTrue(rootElement.getChild().isValid()); + assertNull(rootElement.getChild().getXmlTag()); + assertNull(rootElement.getChild().getChild().getXmlTag()); + } + + public void testDocumentChange2() throws Throwable { + final XmlFile file = (XmlFile)createFile("file.xml", "\n" + + "\n" + + "\n" + + " \n" + + " \n" + + " \n" + + ""); + final DomFileElementImpl fileElement = + getDomManager().getFileElement(file, MyElement.class, "a"); + myCallRegistry.clear(); + final MyElement rootElement = fileElement.getRootElement(); + final MyElement oldLeaf = rootElement.getChild().getChild(); + final XmlTag oldLeafTag = oldLeaf.getXmlTag(); + + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + file.getDocument().getProlog().delete(); + final XmlTag tag = file.getDocument().getRootTag(); + tag.setAttribute("xmlns", "something"); + tag.setAttribute("xmlns:xsi", "something"); + } + }.execute(); + + assertTrue(oldLeafTag.isValid()); + + putExpected(new DomEvent(fileElement, false)); + putExpected(new DomEvent(rootElement, false)); + putExpected(new DomEvent(rootElement, false)); + assertResultsAndClear(); + + assertEquals(fileElement.getRootElement(), rootElement); + assertTrue(rootElement.isValid()); + + assertTrue(rootElement.getChild().isValid()); + assertTrue(rootElement.getChild().getXmlTag().isValid()); + assertTrue(rootElement.getChild().getChild().getXmlTag().isValid()); + } + + public void testMoveUp() throws Throwable { + final XmlFile file = (XmlFile)createFile("file.xml", "\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""); + final DomFileElementImpl fileElement = getDomManager().getFileElement(file, MyElement.class, "a"); + myCallRegistry.clear(); + final MyElement rootElement = fileElement.getRootElement(); + rootElement.getChild().getAboy(); + rootElement.getChild().getAgirl(); + + final Document document = getDocument(file); + final int len = "".length(); + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + final int agirl = document.getText().indexOf(""); + final int boy = document.getText().indexOf(""); + document.replaceString(agirl, agirl + len, ""); + document.replaceString(boy, boy + len, ""); + commitDocument(document); + } + }.execute(); + + assertTrue(rootElement.isValid()); + final XmlTag tag1 = rootElement.getXmlTag().getSubTags()[0]; + assertEquals(getDomManager().getDomElement(tag1.findFirstSubTag("agirl")), rootElement.getChild().getAgirl()); + assertEquals(getDomManager().getDomElement(tag1.findFirstSubTag("aboy")), rootElement.getChild().getAboy()); + } + + public void testRemoveAttributeParent() throws Throwable { + final XmlFile file = (XmlFile)createFile("file.xml", "\n" + + "\n" + + "\n" + + " \n" + + ""); + final DomFileElementImpl fileElement = + getDomManager().getFileElement(file, MyElement.class, "a"); + myCallRegistry.clear(); + final MyElement rootElement = fileElement.getRootElement(); + final MyElement oldLeaf = rootElement.getChildElements().get(0); + final GenericAttributeValue xxx = oldLeaf.getXxx(); + final XmlTag oldLeafTag = oldLeaf.getXmlTag(); + + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + oldLeafTag.delete(); + } + }.execute(); + + assertFalse(oldLeaf.isValid()); + assertFalse(xxx.isValid()); + } + + public void testTypeBeforeRootTag() throws Throwable { + getDomManager().registerFileDescription(new DomFileDescription(MyElement.class, "a"), getTestRootDisposable()); + + final XmlFile file = (XmlFile)createFile("file.xml", "\n" + + ""); + + assertTrue(getDomManager().isDomFile(file)); + final DomFileElementImpl fileElement = getDomManager().getFileElement(file, MyElement.class); + assertTrue(fileElement.isValid()); + myCallRegistry.clear(); + + putExpected(new DomEvent(fileElement, false)); + + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + final Document document = getDocument(file); + final int i = document.getText().indexOf(""); + final MyElement child = element.getChild(); + final MyElement child2 = element.getChild2(); + final MyElement firstChild = element.getChildElements().get(0); + element.getXmlTag().add(createTag("")); + final XmlTag[] subTags = element.getXmlTag().getSubTags(); + assertEquals(2, element.getChildElements().size()); + assertEquals(firstChild, element.getChildElements().get(0)); + MyElement nextChild = element.getChildElements().get(1); + + putExpected(new DomEvent(element, false)); + assertResultsAndClear(); + } + + public void testAddFixedElement() throws Throwable { + final MyElement element = createPhysicalElement("" + + "" + + "" + + ""); + final MyElement child = element.getChild(); + final MyElement child2 = element.getChild2(); + final XmlTag leafTag = child2.getChild().getXmlTag(); + + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + element.getXmlTag().addAfter(createTag(""), child.getXmlTag()); + } + }.execute(); + + assertNoCache(leafTag); + + final XmlTag[] subTags = element.getXmlTag().getSubTags(); + + assertFalse(child2.isValid()); + assertEquals(child, element.getChild()); + assertFalse(child2.equals(element.getChild2())); + + assertCached(child, subTags[0]); + assertNoCache(subTags[2]); + assertNoCache(subTags[3]); + + putExpected(new DomEvent(element, false)); + putExpected(new DomEvent(element, false)); + assertResultsAndClear(); + } + + public void testAddFixedElementCanDefineIt() throws Throwable { + final MyElement element = createElement(""); + final MyElement child = element.getChild(); + + element.getXmlTag().add(createTag("")); + + final XmlTag[] subTags = element.getXmlTag().getSubTags(); + + assertTrue(child.equals(element.getChild())); + assertTrue(element.getChild().equals(child)); + + assertCached(element.getChild(), subTags[0]); + + putExpected(new DomEvent(element, false)); + assertResultsAndClear(); + } + + public void testActuallyRemoveCollectionElement() throws Throwable { + final MyElement element = createElement(""); + final MyElement child = element.getChild(); + final MyElement child2 = element.getChild2(); + final MyElement firstChild = element.getChildElements().get(0); + final MyElement lastChild = element.getChildElements().get(1); + + final XmlTag tag = element.getXmlTag(); + final XmlTag childTag = tag.getSubTags()[0]; + ApplicationManager.getApplication().runWriteAction(new Runnable() { + public void run() { + childTag.delete(); + } + }); + + putExpected(new DomEvent(element, false)); + assertResultsAndClear(); + + assertEquals(child, element.getChild()); + assertEquals(child2, element.getChild2()); + assertEquals(Arrays.asList(lastChild), element.getChildElements()); + assertCached(lastChild, tag.getSubTags()[0]); + } + + public void testCustomChildrenEvents() throws Throwable { + final Sepulka element = createElement("", Sepulka.class); + final List list = element.getCustomChildren(); + final XmlTag tag = element.getXmlTag(); + ApplicationManager.getApplication().runWriteAction(new Runnable() { + public void run() { + tag.getSubTags()[0].delete(); + tag.getSubTags()[0].delete(); + } + }); + + tag.add(createTag("")); + putExpected(new DomEvent(element, false)); + putExpected(new DomEvent(element, false)); + putExpected(new DomEvent(element, false)); + assertResultsAndClear(); + + assertEquals(1, element.getCustomChildren().size()); + } + + public void testRemoveFixedElement() throws Throwable { + final MyElement element = createElement("" + + "" + + "" + + "" + + ""); + final MyElement child = element.getChild(); + final MyElement child2 = element.getChild2(); + final MyElement oldLeaf = child2.getChild(); + final XmlTag tag = element.getXmlTag(); + XmlTag leafTag = tag.getSubTags()[2].getSubTags()[0]; + assertNoCache(leafTag); + + ApplicationManager.getApplication().runWriteAction(new Runnable() { + public void run() { + tag.getSubTags()[1].delete(); + + assertFalse(oldLeaf.isValid()); + + putExpected(new DomEvent(element, false)); + assertResultsAndClear(); + + assertEquals(child, element.getChild()); + assertFalse(child2.isValid()); + + tag.getSubTags()[1].delete(); + } + }); + + putExpected(new DomEvent(element, false)); + assertResultsAndClear(); + } + + public void testRootTagAppearsLater() throws Throwable { + final XmlFile file = createXmlFile(""); + final DomFileElementImpl fileElement = getDomManager().getFileElement(file, MyElement.class, "root"); + myCallRegistry.clear(); + + assertNull(fileElement.getRootElement().getXmlTag()); + + file.getDocument().replace(createXmlFile("").getDocument()); + final XmlTag rootTag = fileElement.getRootTag(); + assertEquals(rootTag, file.getDocument().getRootTag()); + putExpected(new DomEvent(fileElement.getRootElement(), false)); + assertResultsAndClear(); + } + + public void testAnotherChildren() throws Throwable { + final MyElement element = createElement(""); + element.getXmlTag().add(createTag("")); + assertEquals(1, element.getAnotherChildren().size()); + + putExpected(new DomEvent(element, false)); + assertResultsAndClear(); + } + + public void testInvalidateParent() throws Throwable { + final MyElement root = getDomManager().createMockElement(MyElement.class, null, true); + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + root.getChild().ensureTagExists(); + root.getChild2().ensureTagExists(); + final MyElement element = root.addChildElement().getChild(); + result.setResult(element); + element.ensureTagExists().getValue().setText("abc"); + root.addChildElement(); + root.addChildElement(); + } + }.execute().getResultObject(); + assertTrue(root.isValid()); + final MyElement element = root.getChildElements().get(0).getChild(); + assertTrue(element.isValid()); + final MyElement child = element.getChild(); + final MyElement genericValue = child.getChild(); + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + final Document document = getDocument(DomUtil.getFile(element)); + final TextRange range = element.getXmlTag().getTextRange(); + document.replaceString(range.getStartOffset(), range.getEndOffset(), ""); + commitDocument(document); + } + }.execute(); + assertFalse(genericValue.isValid()); + assertFalse(child.isValid()); + assertFalse(element.isValid()); + } + + public void testCollectionChildValidAfterFormattingReparse() throws Throwable { + final MyElement root = getDomManager().createMockElement(MyElement.class, null, true); + final MyElement element = new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + result.setResult(root.addChildElement()); + } + }.execute().getResultObject(); + assertTrue(root.isValid()); + assertNotNull(element.getXmlElement()); + } + + public void testChangeImplementationClass() throws Throwable { + getTypeChooserManager().registerTypeChooser(MyElement.class, createClassChooser()); + try { + final MyElement element = getDomManager().createMockElement(MyElement.class, getModule(), true); + final DomFileElement root = DomUtil.getFileElement(element); + + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + element.addChildElement().addChildElement(); + } + }.execute(); + + final MyElement child = element.getChildElements().get(0); + MyElement grandChild = child.getChildElements().get(0); + assertTrue(child instanceof BarInterface); + assertTrue(grandChild instanceof BarInterface); + + grandChild = element.getChildElements().get(0).getChildElements().get(0); + final XmlTag tag = grandChild.getXmlTag(); + assertTrue(grandChild.isValid()); + assertEquals(grandChild, root.getRootElement().getChildElements().get(0).getChildElements().get(0)); + assertNotNull(element.getXmlTag()); + assertNotNull(child.getXmlTag()); + assertNotNull(tag); + assertTrue(tag.isValid()); + + myCallRegistry.clear(); + + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + tag.add(XmlElementFactory.getInstance(getProject()).createTagFromText("")); + } + }.execute(); + + assertTrue(root.isValid()); + assertTrue(element.isValid()); + assertTrue(grandChild.isValid()); + final MyElement newChild = root.getRootElement().getChildElements().get(0); + assertTrue(newChild instanceof BarInterface); + + final MyElement newGrandChild = newChild.getChildElements().get(0); + assertTrue(newGrandChild.isValid()); + assertTrue(newGrandChild instanceof FooInterface); + + putExpected(new DomEvent(child, false)); + putExpected(new DomEvent(grandChild, false)); + assertResultsAndClear(); + } finally { + getTypeChooserManager().unregisterTypeChooser(MyElement.class); + } + } + + public void testChangeImplementationClass_InCollection() throws Throwable { + getTypeChooserManager().registerTypeChooser(MyElement.class, createClassChooser()); + try { + final MyElement element = getDomManager().createMockElement(MyElement.class, getModule(), true); + final DomFileElement root = DomUtil.getFileElement(element); + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + element.addChildElement().addChildElement(); + } + }.execute().getResultObject(); + final MyElement child = element.getChildElements().get(0); + final MyElement grandChild = child.getChildElements().get(0); + assertTrue(child instanceof BarInterface); + assertTrue(grandChild instanceof BarInterface); + + assertTrue(element.isValid()); + assertTrue(child.isValid()); + assertTrue(grandChild.isValid()); + + assertNotNull(element.getXmlTag()); + assertNotNull(child.getXmlTag()); + + final XmlTag tag = grandChild.getXmlTag(); + assertNotNull(tag); + + myCallRegistry.clear(); + + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + tag.add(XmlElementFactory.getInstance(getProject()).createTagFromText("")); + } + }.execute(); + + assertTrue(root.isValid()); + assertTrue(element.isValid()); + + assertTrue(child.isValid()); + final MyElement newChild = element.getChildElements().get(0); + assertTrue(newChild.isValid()); + assertTrue(newChild.getClass().toString(), newChild instanceof BarInterface); + + assertTrue(grandChild.isValid()); + final MyElement newGrandChild = newChild.getChildElements().get(0); + assertTrue(newGrandChild.isValid()); + assertTrue(newGrandChild instanceof FooInterface); + + putExpected(new DomEvent(child, false)); + putExpected(new DomEvent(grandChild, false)); + assertResultsAndClear(); + } finally { + getTypeChooserManager().unregisterTypeChooser(MyElement.class); + } + } + /* + + public void testCommentTag() throws Throwable { + final String prefix = "\n" + + "\n" + + "\n" + " MyAppa\n" + " ss\n" + + " \n" + " a\n" + " MyWebApp2.war\n" + + " "; + final String infix = "MyWebApp2"; + String suffix = "\n" + " \n" + " \n" + + " \n" + " \n" + " MyWebApp2.war\n" + + " MyWebApp2\n" + " \n" + " \n" + + " \n" + " MyEjb32.jar\n" + " \n" + + " \n" + " MyEjb4.jar\n" + " \n" + " \n" + + " asdf\n" + " nameasdf\n" + + " \n" + "\n" + ""; + + final XmlFile file = (XmlFile)createFile("web.xml", prefix + infix + suffix); + final JavaeeApplication javaeeApplication = getDomManager().getFileElement(file, JavaeeApplication.class).getRootElement(); + assertEquals("MyWebApp2", javaeeApplication.getModules().get(0).getWeb().getContextRoot().getValue()); + + new WriteCommandAction(getProject()) { + @Override + protected void run(Result result) throws Throwable { + final Document document = getDocument(file); + document.insertString(prefix.length() + infix.length(), "-->"); + document.insertString(prefix.length(), "