blob: d141ce0abcbffb01030c427a9f901afa1ee29a4d [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.util.xml;
import com.intellij.codeInsight.navigation.actions.GotoDeclarationAction;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.WriteCommandAction;
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.CodeInsightFixtureTestCase;
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 CodeInsightFixtureTestCase {
public void testGetChildrenHonorsIncludes() throws Throwable {
final MyElement rootElement = createDomFile("a.xml", "<root xmlns:xi=\"http://www.w3.org/2001/XInclude\">" +
"<xi:include href=\"b.xml\" xpointer=\"xpointer(/xxx/*)\"/>" +
"<child xxx=\"a\"/>" +
"</root>");
createFile("b.xml", "<xxx><child xxx=\"b\"/></xxx>");
final List<DomIncludesTest.Child> children = rootElement.getChildren();
Consumer<MyElement> consumer1 = new Consumer<MyElement>() {
@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<MyElement> consumer2 = new Consumer<MyElement>() {
@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, consumer1, consumer2);
}
public void testNamespaces() throws Throwable {
final MyElement rootElement = createDomFile("a.xml", "<root xmlns:xi=\"http://www.w3.org/2001/XInclude\" xmlns:foo=\"foo\">" +
"<xi:include href=\"b.xml\" xpointer=\"xpointer(/xxx/*)\"/>" +
"<foo:boy xxx=\"a\"/>" +
"</root>");
DomUtil.getFileElement(rootElement).getFileDescription().registerNamespacePolicy("foo", "foo");
createFile("b.xml", "<xxx xmlns:foo=\"foo\"><foo:boy xxx=\"b\"/></xxx>");
final List<Boy> children = rootElement.getBoys();
assertOrderedCollection(children, new Consumer<Boy>() {
@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<Boy>() {
@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", "<root xmlns:xi=\"http://www.w3.org/2001/XInclude\">" +
"<xi:include href=\"b.xml\" xpointer=\"xpointer(/xxx/*)\"/>" +
"</root>");
final String textB =
"<xxx><boy/><xi:include href=\"c.xml\" xpointer=\"xpointer(/xxx/*)\"/><child/><xi:include href=\"c.xml\" xpointer=\"xpointer(/xxx/*)\"/></xxx>";
final PsiFile fileB = createFile("b.xml", textB);
final String textC =
"<xxx><child/><xi:include href=\"d.xml\" xpointer=\"xpointer(/xxx/*)\"/><boy/><xi:include href=\"d.xml\" xpointer=\"xpointer(/xxx/*)\"/></xxx>";
final PsiFile fileC = createFile("c.xml", textC);
final String textD =
"<xxx><boy/><xi:include href=\"e.xml\" xpointer=\"xpointer(/xxx/*)\"/><child/><xi:include href=\"e.xml\" xpointer=\"xpointer(/xxx/*)\"/></xxx>";
final PsiFile fileD = createFile("d.xml", textD);
final String textE = "<xxx><boy/><child/><boy/><child/><boy/><child/><boy/><child/><boy/><child/><boy/><child/></xxx>";
final PsiFile fileE = createFile("e.xml", textE);
final int threadCount = 100;
final int iterationCount = Timings.adjustAccordingToMySpeed(100, true);
System.out.println("iterationCount = " + iterationCount);
final CountDownLatch finished = new CountDownLatch(threadCount);
final AtomicReference<Exception> ex = new AtomicReference<Exception>();
for (int j = 0; j < threadCount; j++) {
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
@Override
public void run() {
try {
for (int k = 0; k < iterationCount; k++) {
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
final List<Boy> boys = rootElement.getBoys();
Thread.yield();
final List<Child> children = rootElement.getChildren();
Thread.yield();
assertEquals(boys, rootElement.getBoys());
assertEquals(children, rootElement.getChildren());
}
});
Thread.yield();
}
}
catch (Exception e) {
ex.set(e);
}
finally {
finished.countDown();
}
}
});
}
for (int i = 0; i < iterationCount; i++) {
WriteCommandAction.runWriteCommandAction(null, new Runnable() {
@Override
public void run() {
fileB.getViewProvider().getDocument().insertString(0, " ");
fileD.getViewProvider().getDocument().insertString(0, " ");
PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); //clear xinclude caches
}
});
Thread.sleep(10);
WriteCommandAction.runWriteCommandAction(null, new Runnable(){
@Override
public void run() {
fileC.getViewProvider().getDocument().insertString(0, " ");
fileE.getViewProvider().getDocument().insertString(0, " ");
PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); //clear xinclude caches
}
});
Thread.sleep(10);
WriteCommandAction.runWriteCommandAction(null, new Runnable(){
@Override
public void run() {
fileB.getViewProvider().getDocument().setText(textB);
fileC.getViewProvider().getDocument().setText(textC);
fileD.getViewProvider().getDocument().setText(textD);
fileE.getViewProvider().getDocument().setText(textE);
PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); //clear xinclude caches
}
});
}
finished.await();
final Exception exception = ex.get();
if (exception != null) {
throw exception;
}
}
public void testNavigationToIncluded() throws Throwable {
final MyElement rootElement = createDomFile("a.xml", "<root xmlns:xi=\"http://www.w3.org/2001/XInclude\">" +
"<xi:include href=\"b.xml\" xpointer=\"xpointer(/xxx/*)\"/>" +
"<child ref=\"b\"/>" +
"</root>");
final XmlFile includedFile = (XmlFile) createFile("b.xml", "<xxx><child xxx=\"b\"/></xxx>");
final List<Child> children = rootElement.getChildren();
final MyElement domTarget = children.get(0);
final GenericAttributeValue<Child> 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<MyElement> 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 myFixture.getPsiManager().findFile(file);
}
private DomManagerImpl getDomManager() {
return DomManagerImpl.getDomManager(getProject());
}
public interface MyElement extends DomElement {
List<Child> getChildren();
List<Boy> getBoys();
@NameValue
GenericAttributeValue<String> getXxx();
GenericAttributeValue<Child> getRef();
}
@Namespace("foo")
public interface Boy extends MyElement {
}
public interface Child extends MyElement {
}
}