| /* |
| * 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 { |
| |
| } |
| |
| } |