/** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd.lang.jsp.ast; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Set; import org.junit.Ignore; import org.junit.Test; import net.sourceforge.pmd.lang.ast.Node; /** * Test parsing of a JSP in document style, by checking the generated AST. * * @author pieter_van_raemdonck - Application Engineers NV/SA - www.ae.be * */ public class JspDocStyleTest extends AbstractJspNodesTst { /** * Smoke test for JSP parser. */ @Test public void testSimplestJsp() { assertNumberOfNodes(ASTElement.class, TEST_SIMPLEST_HTML, 1); } /** * Test the information on a Element and Attribute. */ @Test public void testElementAttributeAndNamespace() { Set<JspNode> nodes = getNodes(null, TEST_ELEMENT_AND_NAMESPACE); Set<ASTElement> elementNodes = getNodesOfType(ASTElement.class, nodes); assertEquals("One element node expected!", 1, elementNodes.size()); ASTElement element = elementNodes.iterator().next(); assertEquals("Correct name expected!", "h:html", element.getName()); assertEquals("Has namespace prefix!", true, element.isHasNamespacePrefix()); assertEquals("Element is empty!", true, element.isEmpty()); assertEquals("Correct namespace prefix of element expected!", "h", element.getNamespacePrefix()); assertEquals("Correct local name of element expected!", "html", element.getLocalName()); Set<ASTAttribute> attributeNodes = getNodesOfType(ASTAttribute.class, nodes); assertEquals("One attribute node expected!", 1, attributeNodes.size()); ASTAttribute attribute = attributeNodes.iterator().next(); assertEquals("Correct name expected!", "MyNsPrefix:MyAttr", attribute.getName()); assertEquals("Has namespace prefix!", true, attribute.isHasNamespacePrefix()); assertEquals("Correct namespace prefix of element expected!", "MyNsPrefix", attribute.getNamespacePrefix()); assertEquals("Correct local name of element expected!", "MyAttr", attribute.getLocalName()); } /** * Test exposing a bug of parsing error when having a hash as last character * in an attribute value. * */ @Test public void testAttributeValueContainingHash() { Set<JspNode> nodes = getNodes(null, TEST_ATTRIBUTE_VALUE_CONTAINING_HASH); Set<ASTAttribute> attributes = getNodesOfType(ASTAttribute.class, nodes); assertEquals("Three attributes expected!", 3, attributes.size()); List<ASTAttribute> attrsList = new ArrayList<>(attributes); Collections.sort(attrsList, new Comparator<ASTAttribute>() { public int compare(ASTAttribute arg0, ASTAttribute arg1) { return arg0.getName().compareTo(arg1.getName()); } }); ASTAttribute attr = attrsList.get(0); assertEquals("Correct attribute name expected!", "foo", attr.getName()); assertEquals("Correct attribute value expected!", "CREATE", attr.getFirstDescendantOfType(ASTAttributeValue.class).getImage()); attr = attrsList.get(1); assertEquals("Correct attribute name expected!", "href", attr.getName()); assertEquals("Correct attribute value expected!", "#", attr.getFirstDescendantOfType(ASTAttributeValue.class).getImage()); attr = attrsList.get(2); assertEquals("Correct attribute name expected!", "something", attr.getName()); assertEquals("Correct attribute value expected!", "#yes#", attr.getFirstDescendantOfType(ASTAttributeValue.class).getImage()); } /** * Test correct parsing of CDATA. */ @Test public void testCData() { Set<ASTCData> cdataNodes = getNodes(ASTCData.class, TEST_CDATA); assertEquals("One CDATA node expected!", 1, cdataNodes.size()); ASTCData cdata = cdataNodes.iterator().next(); assertEquals("Content incorrectly parsed!", " some <cdata> ]] ]> ", cdata.getImage()); } /** * Test parsing of Doctype declaration. */ @Test public void testDoctype() { Set<JspNode> nodes = getNodes(null, TEST_DOCTYPE); Set<ASTDoctypeDeclaration> docTypeDeclarations = getNodesOfType(ASTDoctypeDeclaration.class, nodes); assertEquals("One doctype declaration expected!", 1, docTypeDeclarations.size()); ASTDoctypeDeclaration docTypeDecl = docTypeDeclarations.iterator().next(); assertEquals("Correct doctype-name expected!", "html", docTypeDecl.getName()); Set<ASTDoctypeExternalId> externalIds = getNodesOfType(ASTDoctypeExternalId.class, nodes); assertEquals("One doctype external id expected!", 1, externalIds.size()); ASTDoctypeExternalId externalId = externalIds.iterator().next(); assertEquals("Correct external public id expected!", "-//W3C//DTD XHTML 1.1//EN", externalId.getPublicId()); assertEquals("Correct external uri expected!", "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd", externalId.getUri()); } /** * Test parsing of a XML comment. * */ @Test public void testComment() { Set<ASTCommentTag> comments = getNodes(ASTCommentTag.class, TEST_COMMENT); assertEquals("One comment expected!", 1, comments.size()); ASTCommentTag comment = comments.iterator().next(); assertEquals("Correct comment content expected!", "comment", comment.getImage()); } /** * Test parsing of HTML <script> element. */ @Test public void testHtmlScript() { Set<ASTHtmlScript> scripts = getNodes(ASTHtmlScript.class, TEST_HTML_SCRIPT); assertEquals("One script expected!", 1, scripts.size()); ASTHtmlScript script = scripts.iterator().next(); assertEquals("Correct script content expected!", "Script!", script.getImage()); } /** * Test parsing of HTML <script src="x"/> element. It might not be valid * html but it is likely to appear in .JSP files. */ @Test public void testImportHtmlScript() { Set<ASTHtmlScript> scripts = getNodes(ASTHtmlScript.class, TEST_IMPORT_JAVASCRIPT); assertEquals("One script expected!", 1, scripts.size()); ASTHtmlScript script = scripts.iterator().next(); List<ASTAttributeValue> value = script.findDescendantsOfType(ASTAttributeValue.class); assertEquals("filename.js", value.get(0).getImage()); } /** * Test parsing of HTML <script> element. */ @Test public void testHtmlScriptWithAttribute() { Set<ASTHtmlScript> scripts = getNodes(ASTHtmlScript.class, TEST_HTML_SCRIPT_WITH_ATTRIBUTE); assertEquals("One script expected!", 1, scripts.size()); ASTHtmlScript script = scripts.iterator().next(); assertEquals("Correct script content expected!", "Script!", script.getImage()); List<ASTAttributeValue> attrs = script.findDescendantsOfType(ASTAttributeValue.class); assertTrue("text/javascript".equals(attrs.get(0).getImage())); } /** * A complex script containing HTML comments, escapes, quotes, etc. */ @Test public void testComplexHtmlScript() { Set<ASTHtmlScript> script = getNodes(ASTHtmlScript.class, TEST_COMPLEX_SCRIPT); assertEquals("One script expected!", 1, script.size()); ASTHtmlScript next = script.iterator().next(); assertTrue(next.getImage().contains("<!--")); Set<ASTCommentTag> comments = getNodes(ASTCommentTag.class, TEST_COMPLEX_SCRIPT); assertEquals("One comment expected!", 1, comments.size()); } /** * Test parsing of HTML <script> element. */ @Test public void testInlineCss() { Set<ASTElement> scripts = getNodes(ASTElement.class, TEST_INLINE_STYLE); assertEquals("Three elements expected!", 3, scripts.size()); } /** * Test parsing of HTML text within element. */ @Test public void testTextInTag() { Set<ASTText> scripts = getNodes(ASTText.class, TEST_TEXT_IN_TAG); assertEquals("One text chunk expected!", 1, scripts.size()); ASTText script = scripts.iterator().next(); assertEquals("Correct content expected!", " some text ", script.getImage()); } /** * Test parsing of HTML with no spaces between tags. Parser is likely in * this scenario. */ @Test public void noSpacesBetweenTags() { Set<ASTElement> scripts = getNodes(ASTElement.class, TEST_TAGS_NO_SPACE); assertEquals("Two tags expected!", 2, scripts.size()); List<ASTElement> elmts = sortNodesByName(scripts); Iterator<ASTElement> iterator = elmts.iterator(); ASTElement script = iterator.next(); assertEquals("Correct content expected!", "a", script.getName()); script = iterator.next(); assertEquals("Correct content expected!", "b", script.getName()); } /** * the $ sign might trick the parser into thinking an EL is next. He should * be able to treat it as plain text */ @Test public void unclosedTagsWithDollar() { Set<ASTText> scripts = getNodes(ASTText.class, TEST_TAGS_WITH_DOLLAR); assertEquals("Two text chunks expected!", 2, scripts.size()); ASTText script = scripts.iterator().next(); assertEquals("Correct content expected!", " $ ", script.getImage()); } /** * Make sure EL expressions aren't treated as plain text when they are * around unclosed tags. */ @Test public void unclosedTagsWithELWithin() { Set<ASTElExpression> scripts = getNodes(ASTElExpression.class, TEST_TAGS_WITH_EL_WITHIN); assertEquals("Two EL expressions expected!", 2, scripts.size()); List<ASTElExpression> exprs = sortByImage(scripts); Iterator<ASTElExpression> iterator = exprs.iterator(); ASTElExpression script = iterator.next(); assertEquals("Correct content expected!", "expr1", script.getImage()); script = iterator.next(); assertEquals("Correct content expected!", "expr2", script.getImage()); } /** * Make sure mixed expressions don't confuse the parser */ @Test public void mixedExpressions() { Set<ASTJspExpression> exprs = getNodes(ASTJspExpression.class, TEST_TAGS_WITH_MIXED_EXPRESSIONS); assertEquals("One JSP expression expected!", 1, exprs.size()); assertEquals("Image of expression should be \"expr\"", "expr", exprs.iterator().next().getImage()); Set<ASTElExpression> els = getNodes(ASTElExpression.class, TEST_TAGS_WITH_MIXED_EXPRESSIONS); assertEquals("Two EL expression expected!", 2, els.size()); assertEquals("Image of el should be \"expr\"", "expr", els.iterator().next().getImage()); Set<ASTUnparsedText> unparsedtexts = getNodes(ASTUnparsedText.class, TEST_TAGS_WITH_MIXED_EXPRESSIONS); List<ASTUnparsedText> sortedUnparsedTxts = sortByImage(unparsedtexts); assertEquals("Two unparsed texts expected!", 2, sortedUnparsedTxts.size()); Iterator<ASTUnparsedText> iterator = sortedUnparsedTxts.iterator(); assertEquals("Image of text should be \"\\${expr}\"", " \\${expr} ", iterator.next().getImage()); assertEquals("Image of text should be \" aaa \"", " aaa ", iterator.next().getImage()); // ASTText should contain the text between two tags. Set<ASTText> texts = getNodes(ASTText.class, TEST_TAGS_WITH_MIXED_EXPRESSIONS); List<ASTText> sortedTxts = sortByImage(texts); assertEquals("Two regular texts expected!", 2, sortedTxts.size()); Iterator<ASTText> iterator2 = sortedTxts.iterator(); assertEquals("Image of text should be \"\\${expr}\"", " \\${expr} ", iterator2.next().getImage()); assertEquals("Image of text should be all text between two nodes" + " \" aaa ${expr}#{expr} \"", " aaa ${expr}#{expr}", iterator2.next().getImage()); } /** * Make sure JSP expressions are properly detected when they are next to * unclosed tags. */ @Test public void unclosedTagsWithJspExpressionWithin() { Set<ASTJspExpression> scripts = getNodes(ASTJspExpression.class, TEST_TAGS_WITH_EXPRESSION_WITHIN); assertEquals("Two JSP expressions expected!", 2, scripts.size()); ASTJspExpression script = scripts.iterator().next(); assertEquals("Correct content expected!", "expr", script.getImage()); } /** * A dangling unopened ( just </closed> ) tag should not influence the * parsing. */ @Test @Ignore // sadly the number of // <opening> tags has to be >= then the number of </closing> tags public void textBetweenUnopenedTag() { Set<ASTText> scripts = getNodes(ASTText.class, TEST_TEXT_WITH_UNOPENED_TAG); assertEquals("Two text chunks expected!", 2, scripts.size()); ASTText script = scripts.iterator().next(); assertEquals("Correct content expected!", "$", script.getImage()); } /** * Parser should be able to handle documents which start or end with * unparsed text */ @Test @Ignore // sadly the number of // <opening> tags has to be >= then the number of </closing> tags public void textMultipleClosingTags() { Set<ASTText> scripts = getNodes(ASTText.class, TEST_MULTIPLE_CLOSING_TAGS); assertEquals("Four text chunks expected!", 4, scripts.size()); ASTText script = scripts.iterator().next(); assertEquals("Correct content expected!", " some text ", script.getImage()); } /** * Test parsing of HTML <script> element. */ @Test public void textAfterOpenAndClosedTag() { Set<ASTElement> nodes = getNodes(ASTElement.class, TEST_TEXT_AFTER_OPEN_AND_CLOSED_TAG); assertEquals("Two elements expected!", 2, nodes.size()); List<ASTElement> elmts = sortNodesByName(nodes); assertEquals("First element should be a", "a", elmts.get(0).getName()); assertFalse("first element should be closed", elmts.get(0).isUnclosed()); assertEquals("Second element should be b", "b", elmts.get(1).getName()); assertTrue("Second element should not be closed", elmts.get(1).isUnclosed()); Set<ASTText> text = getNodes(ASTText.class, TEST_TEXT_AFTER_OPEN_AND_CLOSED_TAG); assertEquals("Two text chunks expected!", 2, text.size()); } @Test public void quoteEL() { Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class, TEST_QUOTE_EL); assertEquals("One attribute expected!", 1, attributes.size()); ASTAttributeValue attr = attributes.iterator().next(); assertEquals("Expected to detect proper value for attribute!", "${something}", attr.getImage()); } @Test public void quoteExpression() { Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class, TEST_QUOTE_EXPRESSION); assertEquals("One attribute expected!", 1, attributes.size()); ASTAttributeValue attr = attributes.iterator().next(); assertEquals("Expected to detect proper value for attribute!", "<%=something%>", attr.getImage()); } @Test @Ignore // tags contain quotes and break attribute parsing public void quoteTagInAttribute() { Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class, TEST_QUOTE_TAG_IN_ATTR); assertEquals("One attribute expected!", 1, attributes.size()); ASTAttributeValue attr = attributes.iterator().next(); assertEquals("Expected to detect proper value for attribute!", "<bean:write name=\"x\" property=\"z\">", attr.getImage()); } /** * smoke test for a non-quoted attribute value */ @Test public void noQuoteAttrValue() { Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class, TEST_NO_QUOTE_ATTR); assertEquals("One attribute expected!", 1, attributes.size()); ASTAttributeValue attr = attributes.iterator().next(); assertEquals("Expected to detect proper value for attribute!", "yes|", attr.getImage()); } /** * tests whether JSP el is properly detected as attribute value */ @Test public void noQuoteAttrWithJspEL() { Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class, TEST_NO_QUOTE_ATTR_WITH_EL); assertEquals("two attributes expected!", 2, attributes.size()); Iterator<ASTAttributeValue> iterator = attributes.iterator(); ASTAttributeValue attr2 = iterator.next(); if ("url".equals(attr2.getImage())) { // we have to employ this nasty work-around // in order to ensure that we check the proper attribute attr2 = iterator.next(); } assertEquals("Expected to detect proper value for EL in attribute!", "${something}", attr2.getImage()); } /** * tests whether parse correctly detects presence of JSP expression <%= %> * within an non-quoted attribute value */ @Test public void noQuoteAttrWithJspExpression() { Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class, TEST_NO_QUOTE_ATTR_WITH_EXPRESSION); assertEquals("One attribute expected!", 1, attributes.size()); ASTAttributeValue attr = attributes.iterator().next(); assertEquals("Expected to detect proper value for attribute!", "<%=something%>", attr.getImage()); } /** * tests whether parse correctly interprets empty non quote attribute */ @Test public void noQuoteAttrEmpty() { Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class, TEST_NO_QUOTE_EMPTY_ATTR); assertEquals("two attributes expected!", 2, attributes.size()); Iterator<ASTAttributeValue> iterator = attributes.iterator(); ASTAttributeValue attr = iterator.next(); if ("http://someHost:/some_URL".equals(attr.getImage())) { // we have to employ this nasty work-around // in order to ensure that we check the proper attribute attr = iterator.next(); } assertEquals("Expected to detect proper value for attribute!", "", attr.getImage()); } /** * tests whether parse correctly interprets an cr lf instead of an attribute */ @Test public void noQuoteAttrCrLf() { Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class, TEST_NO_QUOTE_CR_LF_ATTR); assertEquals("One attribute expected!", 2, attributes.size()); Iterator<ASTAttributeValue> iterator = attributes.iterator(); ASTAttributeValue attr = iterator.next(); if ("http://someHost:/some_URL".equals(attr.getImage())) { // we have to employ this nasty work-around // in order to ensure that we check the proper attribute attr = iterator.next(); } assertEquals("Expected to detect proper value for attribute!", "\r\n", attr.getImage()); } /** * tests whether parse correctly interprets an tab instead of an attribute */ @Test public void noQuoteAttrTab() { Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class, TEST_NO_QUOTE_TAB_ATTR); assertEquals("One attribute expected!", 1, attributes.size()); Iterator<ASTAttributeValue> iterator = attributes.iterator(); ASTAttributeValue attr = iterator.next(); assertEquals("Expected to detect proper value for attribute!", "\t", attr.getImage()); } /** * tests whether parse does not fail in the presence of unclosed JSP * expression <%= within an non-quoted attribute value */ @Test public void noQuoteAttrWithMalformedJspExpression() { Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class, TEST_NO_QUOTE_ATTR_WITH_MALFORMED_EXPR); assertEquals("One attribute expected!", 1, attributes.size()); ASTAttributeValue attr = attributes.iterator().next(); assertEquals("Expected to detect proper value for attribute!", "<%=something", attr.getImage()); } /** * test a no quote attribute value which contains a scriptlet <% %> within * its value */ @Test @Ignore // nice test for future development public void noQuoteAttrWithScriptletInValue() { Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class, TEST_NO_QUOTE_ATTR_WITH_SCRIPTLET); assertEquals("One attribute expected!", 1, attributes.size()); ASTAttributeValue attr = attributes.iterator().next(); assertEquals("Expected to detect proper value for attribute!", "<% String a = \"1\";%>", attr.getImage()); } /** * test a no quote attribute value can contain a tag (e.g. * attr=<bean:write property="value" />) * */ @Test @Ignore // nice test for future development public void noQuoteAttrWithBeanWriteTagAsValue() { Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class, TEST_NO_QUOTE_TAG_IN_ATTR); assertEquals("One attribute expected!", 1, attributes.size()); ASTAttributeValue attr = attributes.iterator().next(); assertEquals("Expected to detect proper value for attribute!", "<% String a = \"1\";%>", attr.getImage()); } /** * test a quote attribute value can contain a tag (e.g. * attr="<bean:write property="value" />" ) Not sure if it's legal JSP code * but most JSP engine accept and properly treat this value at runtime */ @Test @Ignore // nice test for future development public void quoteAttrWithBeanWriteTagAsValue() { Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class, TEST_NO_QUOTE_TAG_IN_ATTR); assertEquals("One attribute expected!", 1, attributes.size()); ASTAttributeValue attr = attributes.iterator().next(); assertEquals("Expected to detect proper value for attribute!", "<% String a = \"1\";%>", attr.getImage()); } /** * test a no quote attribute value which contains the EL dollar sign $ * within its value */ @Test @Ignore // nice test for future development public void noQuoteAttrWithDollarSignInValue() { Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class, TEST_NO_QUOTE_ATTR_WITH_DOLLAR); assertEquals("One attribute expected!", 2, attributes.size()); ASTAttributeValue attr = attributes.iterator().next(); assertEquals("Expected to detect proper value for attribute!", "${something", attr.getImage()); } /** * test a no quote attribute value which contains the EL sharp sign # within * its value */ @Test @Ignore // nice test for future development public void noQuoteAttrWithSharpSymbolInValue() { Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class, TEST_NO_QUOTE_ATTR_WITH_HASH); assertEquals("One attribute expected!", 1, attributes.size()); ASTAttributeValue attr = attributes.iterator().next(); assertEquals("Expected to detect proper value for attribute!", "#{something", attr.getImage()); } @Test public void unclosedTag() { Set<ASTElement> elements = getNodes(ASTElement.class, TEST_UNCLOSED_SIMPLE); List<ASTElement> sortedElmnts = sortNodesByName(elements); assertEquals("2 tags expected", 2, elements.size()); assertEquals("First element should be sorted tag:if", "tag:if", sortedElmnts.get(0).getName()); assertEquals("Second element should be tag:someTag", "tag:someTag", sortedElmnts.get(1).getName()); assertTrue(sortedElmnts.get(0).isEmpty()); assertTrue(sortedElmnts.get(0).isUnclosed()); assertFalse(sortedElmnts.get(1).isEmpty()); assertFalse(sortedElmnts.get(1).isUnclosed()); } @Test public void unclosedTagAndNoQuotesForAttribute() { Set<ASTElement> elements = getNodes(ASTElement.class, TEST_UNCLOSED_NO_QUOTE_ATTR); List<ASTElement> sortedElmnts = sortNodesByName(elements); assertEquals("2 tags expected", 2, elements.size()); assertEquals("First element should be sorted tag:if", "tag:if", sortedElmnts.get(0).getName()); assertEquals("Second element should be tag:someTag", "tag:someTag", sortedElmnts.get(1).getName()); assertTrue(sortedElmnts.get(0).isEmpty()); assertTrue(sortedElmnts.get(0).isUnclosed()); assertFalse(sortedElmnts.get(1).isEmpty()); assertFalse(sortedElmnts.get(1).isUnclosed()); } @Test public void unclosedTagMultipleLevels() { Set<ASTElement> elements = getNodes(ASTElement.class, TEST_UNCLOSED_MULTIPLE_LEVELS); List<ASTElement> sortedElmnts = sortNodesByName(elements); assertEquals("3 tags expected", 3, elements.size()); assertEquals("First element should be sorted tag:someTag", "tag:someTag", sortedElmnts.get(0).getName()); assertEquals("Second element should be tag:someTag", "tag:someTag", sortedElmnts.get(1).getName()); assertEquals("Third element should be tag:x", "tag:x", sortedElmnts.get(2).getName()); assertFalse(sortedElmnts.get(0).isEmpty()); assertFalse(sortedElmnts.get(0).isUnclosed()); assertTrue(sortedElmnts.get(1).isEmpty()); assertTrue(sortedElmnts.get(1).isUnclosed()); assertFalse(sortedElmnts.get(2).isEmpty()); assertFalse(sortedElmnts.get(2).isUnclosed()); } /** * <html> <a1> <a2/> <b/> </a1> </html> */ @Test public void nestedEmptyTags() { Set<ASTElement> elements = getNodes(ASTElement.class, TEST_MULTIPLE_EMPTY_TAGS); List<ASTElement> sortedElmnts = sortNodesByName(elements); assertEquals("4 tags expected", 4, elements.size()); assertEquals("First element should a1", "a1", sortedElmnts.get(0).getName()); assertEquals("Second element should be a2", "a2", sortedElmnts.get(1).getName()); assertEquals("Third element should be b", "b", sortedElmnts.get(2).getName()); assertEquals("Third element should be html", "html", sortedElmnts.get(3).getName()); // a1 assertFalse(sortedElmnts.get(0).isEmpty()); assertFalse(sortedElmnts.get(0).isUnclosed()); // a2 assertTrue(sortedElmnts.get(1).isEmpty()); assertFalse(sortedElmnts.get(1).isUnclosed()); // b assertTrue(sortedElmnts.get(2).isEmpty()); assertFalse(sortedElmnts.get(2).isUnclosed()); // html assertFalse(sortedElmnts.get(3).isEmpty()); assertFalse(sortedElmnts.get(3).isUnclosed()); } /** * <html> <a1> <a2> <a3> </a2> </a1> <b/> <a4/> </html> */ @Test public void nestedMultipleTags() { Set<ASTElement> elements = getNodes(ASTElement.class, TEST_MULTIPLE_NESTED_TAGS); List<ASTElement> sortedElmnts = sortNodesByName(elements); assertEquals("4 tags expected", 6, elements.size()); assertEquals("First element should a1", "a1", sortedElmnts.get(0).getName()); assertEquals("Second element should be a2", "a2", sortedElmnts.get(1).getName()); assertEquals("Third element should be a3", "a3", sortedElmnts.get(2).getName()); assertEquals("Forth element should be a4", "a4", sortedElmnts.get(3).getName()); assertEquals("Fifth element should be b", "b", sortedElmnts.get(4).getName()); assertEquals("Sixth element should be html", "html", sortedElmnts.get(5).getName()); // a1 not empty and closed assertFalse(sortedElmnts.get(0).isEmpty()); assertFalse(sortedElmnts.get(0).isUnclosed()); // a2 not empty and closed assertFalse(sortedElmnts.get(1).isEmpty()); assertFalse(sortedElmnts.get(1).isUnclosed()); // a3 empty and not closed assertTrue(sortedElmnts.get(2).isEmpty()); assertTrue(sortedElmnts.get(2).isUnclosed()); // a4 empty but closed assertTrue(sortedElmnts.get(3).isEmpty()); assertFalse(sortedElmnts.get(3).isUnclosed()); // b empty but closed assertTrue(sortedElmnts.get(4).isEmpty()); assertFalse(sortedElmnts.get(4).isUnclosed()); // html not empty and closed assertFalse(sortedElmnts.get(5).isEmpty()); assertFalse(sortedElmnts.get(5).isUnclosed()); } /** * will test <x> <a> <b> <b> </x> </a> </x> . * Here x is the first tag to be closed thus rendering the next close of a (</a>) * to be disregarded. */ @Test public void unclosedParentTagClosedBeforeChild() { Set<ASTElement> elements = getNodes(ASTElement.class, TEST_UNCLOSED_END_AFTER_PARENT_CLOSE); List<ASTElement> sortedElmnts = sortNodesByName(elements); assertEquals("4 tags expected", 4, elements.size()); assertEquals("First element should be 'a'", "a", sortedElmnts.get(0).getName()); assertEquals("Second element should be b", "b", sortedElmnts.get(1).getName()); assertEquals("Third element should be b", "b", sortedElmnts.get(2).getName()); assertEquals("Forth element should be x", "x", sortedElmnts.get(3).getName()); // a assertTrue(sortedElmnts.get(0).isEmpty()); assertTrue(sortedElmnts.get(0).isUnclosed()); // b assertTrue(sortedElmnts.get(1).isEmpty()); assertTrue(sortedElmnts.get(1).isUnclosed()); // b assertTrue(sortedElmnts.get(2).isEmpty()); assertTrue(sortedElmnts.get(2).isUnclosed()); // x assertFalse(sortedElmnts.get(3).isEmpty()); assertFalse(sortedElmnts.get(3).isUnclosed()); } /** * <x> <a> <b> <b> </z> </a> </x> An unmatched closing of 'z' appears * randomly in the document. This should be disregarded and structure of * children and parents should not be influenced. in other words </a> should * close the first <a> tag , </x> should close the first <x>, etc. */ @Test public void unmatchedTagDoesNotInfluenceStructure() { Set<ASTElement> elements = getNodes(ASTElement.class, TEST_UNCLOSED_UNMATCHED_CLOSING_TAG); List<ASTElement> sortedElmnts = sortNodesByName(elements); assertEquals("4 tags expected", 4, elements.size()); assertEquals("First element should be 'a'", "a", sortedElmnts.get(0).getName()); assertEquals("Second element should be b", "b", sortedElmnts.get(1).getName()); assertEquals("Third element should be b", "b", sortedElmnts.get(2).getName()); assertEquals("Forth element should be x", "x", sortedElmnts.get(3).getName()); // a is not empty and closed assertFalse(sortedElmnts.get(0).isEmpty()); assertFalse(sortedElmnts.get(0).isUnclosed()); // b empty and unclosed assertTrue(sortedElmnts.get(1).isEmpty()); assertTrue(sortedElmnts.get(1).isUnclosed()); // b empty and unclosed assertTrue(sortedElmnts.get(2).isEmpty()); assertTrue(sortedElmnts.get(2).isUnclosed()); // x not empty and closed assertFalse(sortedElmnts.get(3).isEmpty()); assertFalse(sortedElmnts.get(3).isUnclosed()); } /** * <a> <x> <a> <b> <b> </z> </a> </x> * An unmatched closing of 'z' appears randomly in the document. This * should be disregarded and structure of children and parents should not be influenced. * Also un unclosed <a> tag appears at the start of the document */ @Test public void unclosedStartTagWithUnmatchedCloseOfDifferentTag() { Set<ASTElement> elements = getNodes(ASTElement.class, TEST_UNCLOSED_START_TAG_WITH_UNMATCHED_CLOSE); List<ASTElement> sortedElmnts = sortNodesByName(elements); assertEquals("5 tags expected", 5, elements.size()); assertEquals("First element should be 'a'", "a", sortedElmnts.get(0).getName()); assertEquals("Second element should be a", "a", sortedElmnts.get(1).getName()); assertEquals("Third element should be b", "b", sortedElmnts.get(2).getName()); assertEquals("Forth element should be b", "b", sortedElmnts.get(3).getName()); assertEquals("Fifth element should be x", "x", sortedElmnts.get(4).getName()); // first a is empty and unclosed assertTrue(sortedElmnts.get(0).isEmpty()); assertTrue(sortedElmnts.get(0).isUnclosed()); // second a not empty and closed assertFalse(sortedElmnts.get(1).isEmpty()); assertFalse(sortedElmnts.get(1).isUnclosed()); // b empty and unclosed assertTrue(sortedElmnts.get(2).isEmpty()); assertTrue(sortedElmnts.get(2).isUnclosed()); // b empty and unclosed assertTrue(sortedElmnts.get(3).isEmpty()); assertTrue(sortedElmnts.get(3).isUnclosed()); // x not empty and closed assertFalse(sortedElmnts.get(4).isEmpty()); assertFalse(sortedElmnts.get(4).isUnclosed()); } /** * {@link #TEST_UNCLOSED_END_OF_DOC} * <tag:x> <tag:y> * Tests whether parser breaks on no closed tags at all */ // This is yet to be improved. If a closing tag does not // exist no tags will be marked as empty :( @Ignore @Test public void unclosedEndOfDoc() { Set<ASTElement> elements = getNodes(ASTElement.class, TEST_UNCLOSED_END_OF_DOC); List<ASTElement> sortedElmnts = sortNodesByName(elements); assertEquals("2 tags expected", 2, elements.size()); assertEquals("First element should be 'tag:x'", "tag:x", sortedElmnts.get(0).getName()); assertEquals("Second element should be tag:y", "tag:y", sortedElmnts.get(1).getName()); // b // assertTrue(sortedElmnts.get(0).isEmpty()); assertTrue(sortedElmnts.get(0).isUnclosed()); // b assertTrue(sortedElmnts.get(1).isEmpty()); assertTrue(sortedElmnts.get(1).isUnclosed()); } /** * will sort the AST element in list in alphabetical order and if tag name * is the same it will sort against o1.getBeginColumn() +""+ * o1.getBeginLine(). so first criteria is the name, then the second is the * column +""+line string. * * @param elements * @return */ private List<ASTElement> sortNodesByName(Set<ASTElement> elements) { List<ASTElement> list = new ArrayList<>(); list.addAll(elements); Collections.sort(list, new Comparator<ASTElement>() { public int compare(ASTElement o1, ASTElement o2) { if (o1.getName() == null) { return Integer.MIN_VALUE; } if (o2.getName() == null) { return Integer.MAX_VALUE; } if (o1.getName().equals(o2.getName())) { String o1Value = o1.getBeginColumn() + "" + o1.getBeginLine(); String o2Value = o2.getBeginColumn() + "" + o2.getBeginLine(); return o1Value.compareTo(o2Value); } return o1.getName().compareTo(o2.getName()); } }); return list; } /** * will sort the AST node by the image name. * * @param elements * @return */ private <T extends Node> List<T> sortByImage(Set<T> elements) { List<T> list = new ArrayList<>(); list.addAll(elements); Collections.sort(list, new Comparator<Node>() { public int compare(Node o1, Node o2) { if (o1.getImage() == null) { return Integer.MIN_VALUE; } if (o2.getImage() == null) { return Integer.MAX_VALUE; } if (o1.getImage().equals(o2.getImage())) { String o1Value = o1.getBeginColumn() + "" + o1.getBeginLine(); String o2Value = o2.getBeginColumn() + "" + o2.getBeginLine(); return o1Value.compareTo(o2Value); } return o1.getImage().compareTo(o2.getImage()); } }); return list; } private static final String TEST_SIMPLEST_HTML = "<html/>"; private static final String TEST_ELEMENT_AND_NAMESPACE = "<h:html MyNsPrefix:MyAttr='MyValue'/>"; private static final String TEST_CDATA = "<html><![CDATA[ some <cdata> ]] ]> ]]></html>"; private static final String TEST_DOCTYPE = "<?xml version=\"1.0\" standalone='yes'?>\n" + "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" " + "\"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n" + "<greeting>Hello, world!</greeting>"; private static final String TEST_COMMENT = "<html><!-- comment --></html>"; private static final String TEST_ATTRIBUTE_VALUE_CONTAINING_HASH = "<tag:if something=\"#yes#\" foo=\"CREATE\"> <a href=\"#\">foo</a> </tag:if>"; private static final String TEST_HTML_SCRIPT = "<html><head><script>Script!</script></head></html>"; private static final String TEST_IMPORT_JAVASCRIPT = "<html><head><script src=\"filename.js\" type=\"text/javascript\"/></head></html>"; private static final String TEST_HTML_SCRIPT_WITH_ATTRIBUTE = "<html><head><script type=\"text/javascript\">Script!</script></head></html>"; private static final String TEST_COMPLEX_SCRIPT = "<HTML><BODY><!--Java Script-->" + "<SCRIPT language='JavaScript' type='text/javascript'>" + "<!--function calcDays(){" + " date1 = date1.split(\"-\"); date2 = date2.split(\"-\");" + " var sDate = new Date(date1[0]+\"/\"+date1[1]+\"/\"+date1[2]);" + " var eDate = new Date(date2[0]+\"/\"+date2[1]+\"/\"+date2[2]);" + " onload=calcDays;//-->" + "</SCRIPT></BODY></HTML>;"; private static final String TEST_INLINE_STYLE = "<html><head><style> div { color:red; } </style></head></html>"; private static final String TEST_TEXT_IN_TAG = "<a> some text </a>"; private static final String TEST_TAGS_NO_SPACE = "<a><b></a>"; private static final String TEST_TAGS_WITH_DOLLAR = "<a> $ <b> $ </a>"; private static final String TEST_TAGS_WITH_EL_WITHIN = "<a>#{expr1}<b>${expr2}</a>"; private static final String TEST_TAGS_WITH_MIXED_EXPRESSIONS = "<a> aaa ${expr} #{expr} <%=expr%> <b> \\${expr} </a>"; private static final String TEST_TAGS_WITH_EXPRESSION_WITHIN = "<a> <%=expr%> <b> <%=expr%> </a>"; private static final String TEST_TEXT_AFTER_OPEN_AND_CLOSED_TAG = "<a> some text <b> some text </a>"; private static final String TEST_TEXT_WITH_UNOPENED_TAG = "<a> some text </b> some text </a>"; private static final String TEST_MULTIPLE_CLOSING_TAGS = "<a> some text </b> </b> </b> some text </a>"; private static final String TEST_QUOTE_EL = "<tag:if something=\"${something}\" > </tag:if>"; private static final String TEST_QUOTE_EXPRESSION = "<tag:if something=\"<%=something%>\" > </tag:if>"; private static final String TEST_QUOTE_TAG_IN_ATTR = "<tag:if something=\"<bean:write name=\"x\" property=\"z\">\" > " + "<a href=http://someHost:/some_URL >foo</a> </tag:if>"; private static final String TEST_NO_QUOTE_ATTR = "<tag:if something=yes| > </tag:if>"; private static final String TEST_NO_QUOTE_EMPTY_ATTR = "<tag:if something= > <a href=http://someHost:/some_URL >foo</a> </tag:if>"; private static final String TEST_NO_QUOTE_TAG_IN_ATTR = "<tag:if something=<bean:write name=\"x\" property=\"z\"> > <a href=http://someHost:/some_URL >foo</a> </tag:if>"; private static final String TEST_NO_QUOTE_CR_LF_ATTR = "<tag:if something=\r\n > <a href=http://someHost:/some_URL >foo</a> </tag:if>"; private static final String TEST_NO_QUOTE_TAB_ATTR = "<tag:if something=\t > </tag:if>"; private static final String TEST_NO_QUOTE_ATTR_WITH_EL = "<tag:if something=${something} > <a href=url >foo</a> </tag:if>"; private static final String TEST_NO_QUOTE_ATTR_WITH_EXPRESSION = "<tag:if something=<%=something%> > </tag:if>"; /** * same as {@link #TEST_NO_QUOTE_ATTR_WITH_EXPRESSION} only expression is * not properly closed */ private static final String TEST_NO_QUOTE_ATTR_WITH_MALFORMED_EXPR = "<tag:if something=<%=something > </tag:if>"; private static final String TEST_NO_QUOTE_ATTR_WITH_SCRIPTLET = "<tag:if something=<% String a = \"1\";%>x > </tag:if>"; private static final String TEST_NO_QUOTE_ATTR_WITH_DOLLAR = "<tag:if something=${something > <a href=${ >foo</a> </tag:if>"; private static final String TEST_NO_QUOTE_ATTR_WITH_HASH = "<tag:if something=#{something > <a href=#{url} >foo</a> </tag:if>"; private static final String TEST_UNCLOSED_SIMPLE = "<tag:someTag> <tag:if someting=\"x\" > </tag:someTag>"; /** * someTag is closed just once */ private static final String TEST_UNCLOSED_MULTIPLE_LEVELS = "<tag:x> <tag:someTag> <tag:someTag someting=\"x\" > </tag:someTag> </tag:x>"; /** * nested empty tags */ private static final String TEST_MULTIPLE_EMPTY_TAGS = "<html> <a1> <a2/> <b/> </a1> </html>"; /** * multiple nested tags with some tags unclosed */ private static final String TEST_MULTIPLE_NESTED_TAGS = "<html> <a1> <a2> <a3> </a2> </a1> <b/> <a4/> </html>"; /** * </x> will close before </a>, thus leaving <a> to remain unclosed */ private static final String TEST_UNCLOSED_END_AFTER_PARENT_CLOSE = "<x> <a> <b> <b> </x> </a> aa </x> bb </x>"; /** * </z> is just a dangling closing tag not matching any parent. The parser * should disregard it */ private static final String TEST_UNCLOSED_UNMATCHED_CLOSING_TAG = "<x> <a> <b> <b> </z> </a> </x>"; /** * First <a> tag does not close. The first closing of </a> will match the * second opening of a. Another rogue </z> is there for testing compliance */ private static final String TEST_UNCLOSED_START_TAG_WITH_UNMATCHED_CLOSE = "<a> <x> <a> <b> <b> </z> </a> </x>"; private static final String TEST_UNCLOSED_END_OF_DOC = "<tag:x> <tag:y>"; private static final String TEST_UNCLOSED_NO_QUOTE_ATTR = "<tag:someTag> <tag:if someting=x > </tag:someTag>"; }