/* * 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. */ /* * @author max */ package com.intellij.codeInsight; import com.intellij.psi.impl.source.parsing.xml.XmlBuilder; import com.intellij.psi.impl.source.parsing.xml.XmlBuilderDriver; import com.intellij.testFramework.LightCodeInsightTestCase; import org.jetbrains.annotations.Nullable; public class XmlBuilderTest extends LightCodeInsightTestCase { private static class TestXmlBuilder implements XmlBuilder { private final StringBuilder builder = new StringBuilder(); private final StringBuilder currentPhysicalText = new StringBuilder(); private final StringBuilder currentDisplayText = new StringBuilder(); private final ProcessingOrder myTagProcessingOrder; public TestXmlBuilder(final ProcessingOrder tagsAndAttributes) { myTagProcessingOrder = tagsAndAttributes; } @Override public void attribute(final CharSequence name, final CharSequence value, final int startoffset, final int endoffset) { flushText(); builder.append("ATT: name='").append(name).append("' value='").append(value).append("'\n"); } @Override public void endTag(final CharSequence localName, final String namespace, final int startoffset, final int endoffset) { flushText(); builder.append("ENDTAG: name='").append(localName).append("' namespace='").append(namespace).append("'\n"); } @Override public void doctype(@Nullable final CharSequence publicId, @Nullable final CharSequence systemId, final int startOffset, final int endOffset) { } @Override public ProcessingOrder startTag(final CharSequence localName, final String namespace, final int startoffset, final int endoffset, final int headerEndOffset) { flushText(); builder.append("TAG: name='").append(localName).append("' namespace='").append(namespace).append("'\n"); return myTagProcessingOrder; } @Override public void textElement(final CharSequence display, final CharSequence physical, final int startoffset, final int endoffset) { currentPhysicalText.append(physical); currentDisplayText.append(display); } @Override public void entityRef(final CharSequence ref, final int startOffset, final int endOffset) { flushText(); builder.append("REF: '").append(ref).append("'\n"); } @Override public void error(String message, int startOffset, int endOffset) { flushText(); builder.append("ERROR: '").append(message).append("'\n"); } private void flushText() { if (currentPhysicalText.length() > 0) { builder.append("TEXT: '").append(currentPhysicalText).append("' DISPLAY: '").append(currentDisplayText).append("'\n"); currentPhysicalText.setLength(0); currentDisplayText.setLength(0); } } public String getResult() { flushText(); return builder.toString(); } } public void testEmptyXml() throws Exception { doTest("<root/>", "TAG: name='root' namespace=''\n" + "ENDTAG: name='root' namespace=''\n", XmlBuilder.ProcessingOrder.TAGS_AND_ATTRIBUTES); } public void testRealJspx() throws Exception { doTest( /* Test: */ "<jsp:root xmlns:jsp=\"http://java.sun.com/JSP/Page\" xmlns=\"http://www.w3.org/1999/xhtml\" version=\"2.0\"\n" + " xmlns:spring=\"http://www.springframework.org/tags\" xmlns:c=\"http://java.sun.com/jsp/jstl/core\">\n" + "<html>\n" + " <c:set var=\"foo\" value=\"${1}\"/>\n" + " <c:set var=\"foobar\" value=\"${2}\"/>\n" + " <spring:bind path=\"test.fieldName\">\n" + " <jsp:scriptlet></jsp:scriptlet>\n" + " </spring:bind>\n" + "</html>\n" + "</jsp:root>", /* Expected result: */ "TAG: name='root' namespace='http://java.sun.com/JSP/Page'\n" + "ATT: name='xmlns:jsp' value='http://java.sun.com/JSP/Page'\n" + "ATT: name='xmlns' value='http://www.w3.org/1999/xhtml'\n" + "ATT: name='version' value='2.0'\n" + "ATT: name='xmlns:spring' value='http://www.springframework.org/tags'\n" + "ATT: name='xmlns:c' value='http://java.sun.com/jsp/jstl/core'\n" + "TAG: name='html' namespace='http://www.w3.org/1999/xhtml'\n" + "TAG: name='set' namespace='http://java.sun.com/jsp/jstl/core'\n" + "ATT: name='var' value='foo'\n" + "ATT: name='value' value='${1}'\n" + "ENDTAG: name='set' namespace='http://java.sun.com/jsp/jstl/core'\n" + "TAG: name='set' namespace='http://java.sun.com/jsp/jstl/core'\n" + "ATT: name='var' value='foobar'\n" + "ATT: name='value' value='${2}'\n" + "ENDTAG: name='set' namespace='http://java.sun.com/jsp/jstl/core'\n" + "TAG: name='bind' namespace='http://www.springframework.org/tags'\n" + "ATT: name='path' value='test.fieldName'\n" + "TAG: name='scriptlet' namespace='http://java.sun.com/JSP/Page'\n" + "ENDTAG: name='scriptlet' namespace='http://java.sun.com/JSP/Page'\n" + "ENDTAG: name='bind' namespace='http://www.springframework.org/tags'\n" + "ENDTAG: name='html' namespace='http://www.w3.org/1999/xhtml'\n" + "ENDTAG: name='root' namespace='http://java.sun.com/JSP/Page'\n", XmlBuilder.ProcessingOrder.TAGS_AND_ATTRIBUTES); } public void testRealJspxNoAttributes() throws Exception { doTest( /* Test: */ "<jsp:root xmlns:jsp=\"http://java.sun.com/JSP/Page\" xmlns=\"http://www.w3.org/1999/xhtml\" version=\"2.0\"\n" + " xmlns:spring=\"http://www.springframework.org/tags\" xmlns:c=\"http://java.sun.com/jsp/jstl/core\">\n" + "<html>\n" + " <c:set var=\"foo\" value=\"${1}\"/>\n" + " <c:set var=\"foobar\" value=\"${2}\"/>\n" + " <spring:bind path=\"test.fieldName\">\n" + " <jsp:scriptlet></jsp:scriptlet>\n" + " </spring:bind>\n" + "</html>\n" + "</jsp:root>", /* Expected result: */ "TAG: name='root' namespace='http://java.sun.com/JSP/Page'\n" + "TAG: name='html' namespace='http://www.w3.org/1999/xhtml'\n" + "TAG: name='set' namespace='http://java.sun.com/jsp/jstl/core'\n" + "ENDTAG: name='set' namespace='http://java.sun.com/jsp/jstl/core'\n" + "TAG: name='set' namespace='http://java.sun.com/jsp/jstl/core'\n" + "ENDTAG: name='set' namespace='http://java.sun.com/jsp/jstl/core'\n" + "TAG: name='bind' namespace='http://www.springframework.org/tags'\n" + "TAG: name='scriptlet' namespace='http://java.sun.com/JSP/Page'\n" + "ENDTAG: name='scriptlet' namespace='http://java.sun.com/JSP/Page'\n" + "ENDTAG: name='bind' namespace='http://www.springframework.org/tags'\n" + "ENDTAG: name='html' namespace='http://www.w3.org/1999/xhtml'\n" + "ENDTAG: name='root' namespace='http://java.sun.com/JSP/Page'\n", XmlBuilder.ProcessingOrder.TAGS); } public void testNamespaceOverride() throws Exception { doTest( "<c:x xmlns:c=\"ns1\">\n" + " <c:y/>\n" + " <c:x xmlns:c=\"ns2\">\n" + " <c:y/>\n" + " </c:x>\n" + " <c:y/>\n" + "</c:x>\n", "TAG: name='x' namespace='ns1'\n" + "ATT: name='xmlns:c' value='ns1'\n" + "TAG: name='y' namespace='ns1'\n" + "ENDTAG: name='y' namespace='ns1'\n" + "TAG: name='x' namespace='ns2'\n" + "ATT: name='xmlns:c' value='ns2'\n" + "TAG: name='y' namespace='ns2'\n" + "ENDTAG: name='y' namespace='ns2'\n" + "ENDTAG: name='x' namespace='ns2'\n" + "TAG: name='y' namespace='ns1'\n" + "ENDTAG: name='y' namespace='ns1'\n" + "ENDTAG: name='x' namespace='ns1'\n", XmlBuilder.ProcessingOrder.TAGS_AND_ATTRIBUTES); } public void testSimpleEntityResolution() throws Exception { doTest( "<root><</root>", "TAG: name='root' namespace=''\n" + "TEXT: '<' DISPLAY: '<'\n" + "ENDTAG: name='root' namespace=''\n", XmlBuilder.ProcessingOrder.TAGS_AND_TEXTS); } public void testCDATA() throws Exception { doTest( "<root><![CDATA[<asis/>]]></root>", "TAG: name='root' namespace=''\n" + "TEXT: '<![CDATA[<asis/>]]>' DISPLAY: '<asis/>'\n" + "ENDTAG: name='root' namespace=''\n", XmlBuilder.ProcessingOrder.TAGS_AND_TEXTS ); } public void testErrors() throws Exception { doTest( "<root>" + "<foo>" + "<bar" + "<" + "</root>", "TAG: name='root' namespace=''\n" + "TAG: name='foo' namespace=''\n" + "ERROR: 'Element foo is not closed'\n" + "ENDTAG: name='foo' namespace=''\n" + "TAG: name='bar' namespace=''\n" + "ERROR: 'Tag start is not closed'\n" + "ENDTAG: name='bar' namespace=''\n" + "TAG: name='' namespace=''\n" + "ERROR: 'Tag name expected'\n" + "ENDTAG: name='' namespace=''\n" + "ENDTAG: name='root' namespace=''\n", XmlBuilder.ProcessingOrder.TAGS_AND_TEXTS ); } public void testComments() throws Exception { doTest( "<root>" + "<foo>" + "<!--aa-->" + "</foo>" + "<foo>" + "<!---->" + "</foo>" + "<foo>" + "aaa<!--aa-->aaa" + "</foo>" + "<foo>" + "\naaa\n<!--aa-->\naaa\n" + "</foo>" + "</root>", "TAG: name='root' namespace=''\n" + "TAG: name='foo' namespace=''\n" + "ENDTAG: name='foo' namespace=''\n" + "TAG: name='foo' namespace=''\n" + "ENDTAG: name='foo' namespace=''\n" + "TAG: name='foo' namespace=''\n" + "TEXT: 'aaaaaa' DISPLAY: 'aaaaaa'\n" + "ENDTAG: name='foo' namespace=''\n" + "TAG: name='foo' namespace=''\n" + "TEXT: '\naaa\n\naaa\n' DISPLAY: '\naaa\n\naaa\n'\n" + "ENDTAG: name='foo' namespace=''\n" + "ENDTAG: name='root' namespace=''\n", XmlBuilder.ProcessingOrder.TAGS_AND_TEXTS ); } private static void doTest(String xml, String expectedEventSequence, final XmlBuilder.ProcessingOrder tagsAndAttributes) { final TestXmlBuilder builder = new TestXmlBuilder(tagsAndAttributes); new XmlBuilderDriver(xml).build(builder); assertEquals(expectedEventSequence, builder.getResult()); } }