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 extends DomElement> 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 extends DomAttributeChildDescription> 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 extends MyElement> subBars) {
+ assertEquals(2, subBars.size());
+ assertEquals("foo", subBars.get(0).getValue());
+ assertEquals("bar", subBars.get(1).getValue());
+ }
+
+ public void testClassChoosers() throws Throwable {
+ getTypeChooserManager().registerTypeChooser(MyAbstractElement.class, new MyTypeChooser());
+ try {
+ MyElement element = createElement("" +
+ "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 extends MyElement> 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 extends DomExtender> extender) {
+ registerDomExtender(MyElement.class, extender);
+ }
+
+ public void registerDomExtender(final Class domClass, final Class extends DomExtender> 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 extends String> 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 extends StringBuffer> 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