View Javadoc

    1   package net.sourceforge.pmd.lang.jsp.ast;
    2
    3   import static org.junit.Assert.assertEquals;
    4   import static org.junit.Assert.assertFalse;
    5   import static org.junit.Assert.assertTrue;
    6
    7   import java.util.ArrayList;
    8   import java.util.Collections;
    9   import java.util.Comparator;
    10  import java.util.Iterator;
    11  import java.util.List;
    12  import java.util.Set;
    13
    14  import net.sourceforge.pmd.lang.ast.Node;
    15
    16  import org.junit.Ignore;
    17  import org.junit.Test;
    18  /**
    19   * Test parsing of a JSP in document style, by checking the generated AST.
    20   *
    21   * @author pieter_van_raemdonck - Application Engineers NV/SA - www.ae.be
    22   *
    23   */
    24  public class JspDocStyleTest extends AbstractJspNodesTst {
    25
    26  	/**
    27  	 * Smoke test for JSP parser.
    28  	 *
    29  	 * @throws Throwable
    30  	 */
    31      @Test
    32      public void testSimplestJsp() throws Throwable {
    33  		assertNumberOfNodes(ASTElement.class, TEST_SIMPLEST_HTML, 1);
    34  	}
    35
    36  	/**
    37  	 * Test the information on a Element and Attribute.
    38  	 *
    39  	 * @throws Throwable
    40  	 */
    41      @Test
    42  	public void testElementAttributeAndNamespace() throws Throwable {
    43  		Set<Object> nodes = getNodes(null, TEST_ELEMENT_AND_NAMESPACE);
    44
    45  		Set<ASTElement> elementNodes = getNodesOfType(ASTElement.class, nodes);
    46  		assertEquals("One element node expected!", 1, elementNodes.size());
    47  		ASTElement element = elementNodes.iterator().next();
    48  		assertEquals("Correct name expected!", "h:html", element.getName());
    49  		assertEquals("Has namespace prefix!", true, element.isHasNamespacePrefix());
    50  		assertEquals("Element is empty!", true, element.isEmpty());
    51  		assertEquals("Correct namespace prefix of element expected!", "h", element
    52  				.getNamespacePrefix());
    53  		assertEquals("Correct local name of element expected!", "html", element
    54  				.getLocalName());
    55
    56  		Set<ASTAttribute> attributeNodes = getNodesOfType(ASTAttribute.class, nodes);
    57  		assertEquals("One attribute node expected!", 1, attributeNodes.size());
    58  		ASTAttribute attribute = (ASTAttribute) attributeNodes.iterator().next();
    59  		assertEquals("Correct name expected!", "MyNsPrefix:MyAttr", attribute
    60  				.getName());
    61  		assertEquals("Has namespace prefix!", true, attribute.isHasNamespacePrefix());
    62  		assertEquals("Correct namespace prefix of element expected!", "MyNsPrefix",
    63  				attribute.getNamespacePrefix());
    64  		assertEquals("Correct local name of element expected!", "MyAttr", attribute
    65  				.getLocalName());
    66
    67  	}
    68
    69  	/**
    70  	 * Test exposing a bug of parsing error when having a hash as last character
    71  	 * in an attribute value.
    72  	 *
    73  	 */
    74      @Test
    75      public void testAttributeValueContainingHash()
    76  	{
    77  		Set<Object> nodes = getNodes(null, TEST_ATTRIBUTE_VALUE_CONTAINING_HASH);
    78
    79  		Set<ASTAttribute> attributes = getNodesOfType(ASTAttribute.class, nodes);
    80  		assertEquals("Three attributes expected!", 3, attributes.size());
    81
    82  		List<ASTAttribute> attrsList = new ArrayList<ASTAttribute>(attributes);
    83  		Collections.sort(attrsList, new Comparator<ASTAttribute>() {
    84  			public int compare(ASTAttribute arg0, ASTAttribute arg1) {
    85  				return arg0.getName().compareTo(arg1.getName());
    86  			}
    87  		});
    88
    89  		ASTAttribute attr = attrsList.get(0);
    90  		assertEquals("Correct attribute name expected!",
    91  				"foo", attr.getName());
    92  		assertEquals("Correct attribute value expected!",
    93  				"CREATE", attr.getFirstDescendantOfType(ASTAttributeValue.class).getImage());
    94
    95  		attr = attrsList.get(1);
    96  		assertEquals("Correct attribute name expected!",
    97  				"href", attr.getName());
    98  		assertEquals("Correct attribute value expected!",
    99  				"#", attr.getFirstDescendantOfType(ASTAttributeValue.class).getImage());
    100
    101 		attr = attrsList.get(2);
    102 		assertEquals("Correct attribute name expected!",
    103 				"something", attr.getName());
    104 		assertEquals("Correct attribute value expected!",
    105 				"#yes#", attr.getFirstDescendantOfType(ASTAttributeValue.class).getImage());
    106 	}
    107
    108 	/**
    109 	 * Test correct parsing of CDATA.
    110 	 */
    111     @Test
    112     public void testCData() {
    113 		Set<ASTCData> cdataNodes = getNodes(ASTCData.class, TEST_CDATA);
    114
    115 		assertEquals("One CDATA node expected!", 1, cdataNodes.size());
    116 		ASTCData cdata = (ASTCData) cdataNodes.iterator().next();
    117 		assertEquals("Content incorrectly parsed!", " some <cdata> ]] ]> ", cdata
    118 				.getImage());
    119 	}
    120
    121 	/**
    122 	 * Test parsing of Doctype declaration.
    123 	 */
    124     @Test
    125     public void testDoctype() {
    126 		Set<Object> nodes = getNodes(null, TEST_DOCTYPE);
    127
    128 		Set<ASTDoctypeDeclaration> docTypeDeclarations = getNodesOfType(ASTDoctypeDeclaration.class, nodes);
    129 		assertEquals("One doctype declaration expected!", 1, docTypeDeclarations
    130 				.size());
    131 		ASTDoctypeDeclaration docTypeDecl = docTypeDeclarations
    132 				.iterator().next();
    133 		assertEquals("Correct doctype-name expected!", "html", docTypeDecl.getName());
    134
    135 		Set<ASTDoctypeExternalId> externalIds = getNodesOfType(ASTDoctypeExternalId.class, nodes);
    136 		assertEquals("One doctype external id expected!", 1, externalIds
    137 				.size());
    138 		ASTDoctypeExternalId externalId = (ASTDoctypeExternalId) externalIds
    139 				.iterator().next();
    140 		assertEquals("Correct external public id expected!", "-//W3C//DTD XHTML 1.1//EN",
    141 				externalId.getPublicId());
    142 		assertEquals("Correct external uri expected!", "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd",
    143 				externalId.getUri());
    144
    145 	}
    146
    147 	/**
    148 	 * Test parsing of a XML comment.
    149 	 *
    150 	 */
    151     @Test
    152     public void testComment() {
    153 		Set<ASTCommentTag> comments = getNodes(ASTCommentTag.class, TEST_COMMENT);
    154 		assertEquals("One comment expected!", 1, comments.size());
    155 		ASTCommentTag comment = (ASTCommentTag) comments.iterator().next();
    156 		assertEquals("Correct comment content expected!", "comment", comment.getImage());
    157 	}
    158
    159     /**
    160      * Test parsing of HTML <script> element.
    161      */
    162     @Test
    163     public void testHtmlScript() {
    164 		Set<ASTHtmlScript> scripts = getNodes(ASTHtmlScript.class, TEST_HTML_SCRIPT);
    165 		assertEquals("One script expected!", 1, scripts.size());
    166 		ASTHtmlScript script = (ASTHtmlScript) scripts.iterator().next();
    167 		assertEquals("Correct script content expected!", "Script!", script.getImage());
    168     }
    169
    170     /**
    171      * Test parsing of HTML <script src="x"/> element. It might not be valid html
    172      * but it is likely to appear in .JSP files.
    173      */
    174     @Test
    175     public void testImportHtmlScript() {
    176 		Set<ASTHtmlScript> scripts = getNodes(ASTHtmlScript.class, TEST_IMPORT_JAVASCRIPT);
    177 		assertEquals("One script expected!", 1, scripts.size());
    178 		ASTHtmlScript script = (ASTHtmlScript) scripts.iterator().next();
    179 		List<ASTAttributeValue> value = script.findDescendantsOfType(ASTAttributeValue.class);
    180 		assertEquals("filename.js",value.get(0).getImage());
    181     }
    182
    183     /**
    184      * Test parsing of HTML <script> element.
    185      */
    186     @Test
    187     public void testHtmlScriptWithAttribute() {
    188 		Set<ASTHtmlScript> scripts = getNodes(ASTHtmlScript.class, TEST_HTML_SCRIPT_WITH_ATTRIBUTE);
    189 		assertEquals("One script expected!", 1, scripts.size());
    190 		ASTHtmlScript script = (ASTHtmlScript) scripts.iterator().next();
    191 		assertEquals("Correct script content expected!", "Script!", script.getImage());
    192 		List<ASTAttributeValue> attrs = script.findDescendantsOfType(ASTAttributeValue.class);
    193 		assertTrue("text/javascript".equals(attrs.get(0).getImage()));
    194     }
    195
    196     /**
    197      * A complex script containing HTML comments, escapes, quotes, etc.
    198      */
    199     @Test
    200     public void testComplexHtmlScript(){
    201     	Set<ASTHtmlScript> script = getNodes(ASTHtmlScript.class, TEST_COMPLEX_SCRIPT);
    202     	assertEquals("One script expected!", 1, script.size());
    203     	ASTHtmlScript next = script.iterator().next();
    204 		assertTrue(next.getImage().contains("<!--"));
    205     	Set<ASTCommentTag> comments = getNodes(ASTCommentTag.class, TEST_COMPLEX_SCRIPT);
    206     	assertEquals("One comment expected!", 1, comments.size());
    207     }
    208
    209     /**
    210      * Test parsing of HTML <script> element.
    211      */
    212     @Test
    213     public void testInlineCss() {
    214 		Set<ASTElement> scripts = getNodes(ASTElement.class, TEST_INLINE_STYLE);
    215 		assertEquals("Three elements expected!", 3, scripts.size());
    216     }
    217
    218     /**
    219      * Test parsing of HTML text within element.
    220      */
    221     @Test
    222     public void testTextInTag() {
    223 		Set<ASTText> scripts = getNodes(ASTText.class, TEST_TEXT_IN_TAG);
    224 		assertEquals("One text chunk expected!", 1, scripts.size());
    225 		ASTText script = (ASTText) scripts.iterator().next();
    226 		assertEquals("Correct content expected!", " some text ", script.getImage());
    227     }
    228
    229     /**
    230      * Test parsing of HTML with no spaces between tags. Parser is likely
    231      * in this scenario.
    232      */
    233     @Test
    234     public void noSpacesBetweenTags() {
    235 		Set<ASTElement> scripts = getNodes(ASTElement.class, TEST_TAGS_NO_SPACE);
    236 		assertEquals("Two tags expected!", 2, scripts.size());
    237 		List<ASTElement> elmts = sortNodesByName(scripts);
    238 		Iterator<ASTElement> iterator = elmts.iterator();
    239 		ASTElement script = (ASTElement) iterator.next();
    240 		assertEquals("Correct content expected!", "a", script.getName());
    241 		script = (ASTElement) iterator.next();
    242 		assertEquals("Correct content expected!", "b", script.getName());
    243     }
    244
    245     /**
    246      * the $ sign might trick the parser into thinking an EL is next.
    247      * He should be able to treat it as plain text
    248      */
    249     @Test
    250     public void unclosedTagsWithDollar(){
    251     	Set<ASTText> scripts = getNodes(ASTText.class, TEST_TAGS_WITH_DOLLAR);
    252 		assertEquals("Two text chunks expected!", 2, scripts.size());
    253 		ASTText script = (ASTText) scripts.iterator().next();
    254 		assertEquals("Correct content expected!", " $ ", script.getImage());
    255     }
    256
    257     /**
    258      * Make sure EL expressions aren't treated as plain text when they
    259      * are around unclosed tags.
    260      */
    261     @Test
    262     public void unclosedTagsWithELWithin(){
    263     	Set<ASTElExpression> scripts = getNodes(ASTElExpression.class, TEST_TAGS_WITH_EL_WITHIN);
    264 		assertEquals("Two EL expressions expected!", 2, scripts.size());
    265 		List<ASTElExpression> exprs = sortByImage(scripts);
    266 		Iterator<ASTElExpression> iterator = exprs.iterator();
    267 		ASTElExpression script = (ASTElExpression) iterator.next();
    268 		assertEquals("Correct content expected!", "expr1", script.getImage());
    269 		script = (ASTElExpression) iterator.next();
    270 		assertEquals("Correct content expected!", "expr2", script.getImage());
    271     }
    272
    273     /**
    274      * Make sure mixed expressions don't confuse the parser
    275      */
    276     @Test
    277     public void mixedExpressions(){
    278     	Set<ASTJspExpression> exprs = getNodes(ASTJspExpression.class, TEST_TAGS_WITH_MIXED_EXPRESSIONS);
    279     	assertEquals("One JSP expression expected!", 1, exprs.size());
    280     	assertEquals("Image of expression should be \"expr\"",
    281     			"expr",exprs.iterator().next().getImage());
    282     	Set<ASTElExpression> els = getNodes(ASTElExpression.class, TEST_TAGS_WITH_MIXED_EXPRESSIONS);
    283 		assertEquals("Two EL expression expected!", 2, els.size());
    284 		assertEquals("Image of el should be \"expr\"",
    285 				"expr",els.iterator().next().getImage());
    286
    287 		Set<ASTUnparsedText> unparsedtexts = getNodes(ASTUnparsedText.class, TEST_TAGS_WITH_MIXED_EXPRESSIONS);
    288 		List<ASTUnparsedText> sortedUnparsedTxts = sortByImage(unparsedtexts);
    289 		assertEquals("Two unparsed texts expected!", 2, sortedUnparsedTxts.size());
    290 		Iterator<ASTUnparsedText> iterator = sortedUnparsedTxts.iterator();
    291 		assertEquals("Image of text should be \"\\${expr}\"",
    292 				" \\${expr} ",iterator.next().getImage());
    293 		assertEquals("Image of text should be \" aaa \"",
    294 				" aaa ",iterator.next().getImage());
    295
    296 		// ASTText should contain the text between two tags.
    297 		Set<ASTText> texts = getNodes(ASTText.class, TEST_TAGS_WITH_MIXED_EXPRESSIONS);
    298 		List<ASTText> sortedTxts = sortByImage(texts);
    299 		assertEquals("Two regular texts expected!", 2, sortedTxts.size());
    300 		Iterator<ASTText> iterator2 = sortedTxts.iterator();
    301 		assertEquals("Image of text should be \"\\${expr}\"",
    302 				" \\${expr} ",iterator2.next().getImage());
    303 		assertEquals("Image of text should be all text between two nodes" +
    304 				" \"  aaa ${expr}#{expr} \"",
    305 				" aaa ${expr}#{expr}",iterator2.next().getImage());
    306     }
    307
    308
    309     /**
    310      * Make sure JSP expressions are properly detected when they are next
    311      * to unclosed tags.
    312      */
    313     @Test
    314     public void unclosedTagsWithJspExpressionWithin(){
    315     	Set<ASTJspExpression> scripts = getNodes(ASTJspExpression.class, TEST_TAGS_WITH_EXPRESSION_WITHIN);
    316 		assertEquals("Two JSP expressions expected!", 2, scripts.size());
    317 		ASTJspExpression script = (ASTJspExpression) scripts.iterator().next();
    318 		assertEquals("Correct content expected!", "expr", script.getImage());
    319     }
    320
    321
    322     /**
    323      * A dangling unopened ( just </closed> )  tag should not influence the parsing.
    324      */
    325     @Test
    326     @Ignore // sadly the number of
    327     // <opening> tags has to be >= then the number of </closing> tags
    328     public void textBetweenUnopenedTag(){
    329     	Set<ASTText> scripts = getNodes(ASTText.class, TEST_TEXT_WITH_UNOPENED_TAG);
    330     	assertEquals("Two text chunks expected!", 2, scripts.size());
    331 		ASTText script = (ASTText) scripts.iterator().next();
    332 		assertEquals("Correct content expected!", "$", script.getImage());
    333     }
    334
    335     /**
    336      * Parser should be able to handle documents which start or end with unparsed text
    337      */
    338     @Test
    339     @Ignore// sadly the number of
    340     // <opening> tags has to be >= then the number of </closing> tags
    341     public void textMultipleClosingTags(){
    342     	Set<ASTText> scripts = getNodes(ASTText.class, TEST_MULTIPLE_CLOSING_TAGS);
    343     	assertEquals("Four text chunks expected!", 4, scripts.size());
    344 		ASTText script = (ASTText) scripts.iterator().next();
    345 		assertEquals("Correct content expected!", " some text ", script.getImage());
    346     }
    347
    348
    349     /**
    350      * Test parsing of HTML <script> element.
    351      */
    352     @Test
    353     public void textAfterOpenAndClosedTag() {
    354 		Set<ASTElement> nodes = getNodes(ASTElement.class, TEST_TEXT_AFTER_OPEN_AND_CLOSED_TAG);
    355 		assertEquals("Two elements expected!", 2, nodes.size());
    356 		List<ASTElement> elmts = sortNodesByName(nodes);
    357 		assertEquals("First element should be a","a",elmts.get(0).getName());
    358 		assertFalse("first element should be closed",elmts.get(0).isUnclosed());
    359 		assertEquals("Second element should be b","b",elmts.get(1).getName());
    360 		assertTrue("Second element should not be closed",elmts.get(1).isUnclosed());
    361
    362 		Set<ASTText> text = getNodes(ASTText.class, TEST_TEXT_AFTER_OPEN_AND_CLOSED_TAG);
    363 		assertEquals("Two text chunks expected!", 2, text.size());
    364     }
    365
    366     @Test
    367     public void  quoteEL(){
    368     	Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class, TEST_QUOTE_EL);
    369 		assertEquals("One attribute expected!", 1, attributes.size());
    370 		ASTAttributeValue attr =  attributes.iterator().next();
    371 		assertEquals("Expected to detect proper value for attribute!", "${something}", attr.getImage());
    372     }
    373
    374     @Test
    375     public void quoteExpression(){
    376     	Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class, TEST_QUOTE_EXPRESSION);
    377 		assertEquals("One attribute expected!", 1, attributes.size());
    378 		ASTAttributeValue attr =  attributes.iterator().next();
    379 		assertEquals("Expected to detect proper value for attribute!", "<%=something%>", attr.getImage());
    380     }
    381
    382
    383     @Test
    384     @Ignore // tags contain quotes and break attribute parsing
    385     public void quoteTagInAttribute(){
    386     	Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class, TEST_QUOTE_TAG_IN_ATTR);
    387 		assertEquals("One attribute expected!", 1, attributes.size());
    388 		ASTAttributeValue attr =  attributes.iterator().next();
    389 		assertEquals("Expected to detect proper value for attribute!",
    390 				"<bean:write name=\"x\" property=\"z\">", attr.getImage());
    391     }
    392     /**
    393      * smoke test for a non-quoted attribute value
    394      */
    395     @Test
    396     public void noQuoteAttrValue() {
    397     	Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class, TEST_NO_QUOTE_ATTR);
    398 		assertEquals("One attribute expected!", 1, attributes.size());
    399 		ASTAttributeValue attr =  attributes.iterator().next();
    400 		assertEquals("Expected to detect proper value for attribute!", "yes|", attr.getImage());
    401     }
    402
    403
    404     /**
    405      * tests whether JSP el is properly detected as attribute value
    406      */
    407     @Test
    408     public void noQuoteAttrWithJspEL(){
    409     	Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class, TEST_NO_QUOTE_ATTR_WITH_EL);
    410 		assertEquals("two attributes expected!", 2, attributes.size());
    411 		Iterator<ASTAttributeValue> iterator = attributes.iterator();
    412 		ASTAttributeValue attr2 =  iterator.next();
    413 		if ("url".equals(attr2.getImage())){
    414 			// we have to employ this nasty work-around
    415 			// in order to ensure that we check the proper attribute
    416 			attr2 = iterator.next();
    417 		}
    418 		assertEquals("Expected to detect proper value for EL in attribute!", "${something}", attr2.getImage());
    419     }
    420
    421     /**
    422      * tests whether parse correctly detects presence of JSP expression <%= %>
    423      * within an non-quoted attribute value
    424      */
    425     @Test
    426     public void noQuoteAttrWithJspExpression(){
    427     	Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class, TEST_NO_QUOTE_ATTR_WITH_EXPRESSION);
    428 		assertEquals("One attribute expected!", 1, attributes.size());
    429 		ASTAttributeValue attr =  attributes.iterator().next();
    430 		assertEquals("Expected to detect proper value for attribute!", "<%=something%>", attr.getImage());
    431     }
    432
    433     /**
    434      * tests whether parse correctly interprets empty non quote attribute
    435      */
    436     @Test
    437     public void noQuoteAttrEmpty(){
    438     	Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class, TEST_NO_QUOTE_EMPTY_ATTR);
    439 		assertEquals("two attributes expected!", 2, attributes.size());
    440 		Iterator<ASTAttributeValue> iterator = attributes.iterator();
    441 		ASTAttributeValue attr =  iterator.next();
    442 		if ("http://someHost:/some_URL".equals(attr.getImage())){
    443 			// we have to employ this nasty work-around
    444 			// in order to ensure that we check the proper attribute
    445 			attr = iterator.next();
    446 		}
    447 		assertEquals("Expected to detect proper value for attribute!", "", attr.getImage());
    448     }
    449
    450     /**
    451      * tests whether parse correctly interprets an cr lf instead of an attribute
    452      */
    453     @Test
    454     public void noQuoteAttrCrLf(){
    455     	Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class, TEST_NO_QUOTE_CR_LF_ATTR);
    456 		assertEquals("One attribute expected!", 2, attributes.size());
    457 		Iterator<ASTAttributeValue> iterator = attributes.iterator();
    458 		ASTAttributeValue attr =  iterator.next();
    459 		if ("http://someHost:/some_URL".equals(attr.getImage())){
    460 			// we have to employ this nasty work-around
    461 			// in order to ensure that we check the proper attribute
    462 			attr = iterator.next();
    463 		}
    464 		assertEquals("Expected to detect proper value for attribute!", "\r\n", attr.getImage());
    465
    466     }
    467
    468     /**
    469      * tests whether parse correctly interprets an tab instead of an attribute
    470      */
    471     @Test
    472     public void noQuoteAttrTab(){
    473     	Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class, TEST_NO_QUOTE_TAB_ATTR);
    474 		assertEquals("One attribute expected!", 1, attributes.size());
    475 		Iterator<ASTAttributeValue> iterator = attributes.iterator();
    476 		ASTAttributeValue attr =  iterator.next();
    477 		assertEquals("Expected to detect proper value for attribute!", "\t", attr.getImage());
    478
    479     }
    480
    481     /**
    482      * tests whether parse does not fail in the presence of unclosed JSP expression <%=
    483      * within an non-quoted attribute value
    484      */
    485     @Test
    486     public void noQuoteAttrWithMalformedJspExpression(){
    487     	Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class, TEST_NO_QUOTE_ATTR_WITH_MALFORMED_EXPR);
    488 		assertEquals("One attribute expected!",1, attributes.size());
    489 		ASTAttributeValue attr =  attributes.iterator().next();
    490 		assertEquals("Expected to detect proper value for attribute!", "<%=something", attr.getImage());
    491     }
    492
    493     /**
    494      * test a no quote attribute value which contains a scriptlet <% %>
    495      * within its value
    496      */
    497     @Test
    498     @Ignore// nice test for future development
    499     public void noQuoteAttrWithScriptletInValue(){
    500     	Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class, TEST_NO_QUOTE_ATTR_WITH_SCRIPTLET);
    501 		assertEquals("One attribute expected!", 1, attributes.size());
    502
    503 		ASTAttributeValue attr =  attributes.iterator().next();
    504 		assertEquals("Expected to detect proper value for attribute!", "<% String a = \"1\";%>", attr.getImage());
    505     }
    506
    507     /**
    508      * test a no quote attribute value can contain a tag (e.g. attr=<bean:write property="value" />)
    509      *
    510      */
    511     @Test
    512     @Ignore// nice test for future development
    513     public void noQuoteAttrWithBeanWriteTagAsValue(){
    514     	Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class, TEST_NO_QUOTE_TAG_IN_ATTR);
    515 		assertEquals("One attribute expected!", 1, attributes.size());
    516
    517 		ASTAttributeValue attr =  attributes.iterator().next();
    518 		assertEquals("Expected to detect proper value for attribute!", "<% String a = \"1\";%>", attr.getImage());
    519     }
    520
    521     /**
    522      * test a quote attribute value can contain a tag (e.g. attr="<bean:write property="value" />" )
    523      * Not sure if it's legal JSP code but most JSP engine accept and properly treat
    524      * this value at runtime
    525      */
    526     @Test
    527     @Ignore// nice test for future development
    528     public void quoteAttrWithBeanWriteTagAsValue(){
    529     	Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class, TEST_NO_QUOTE_TAG_IN_ATTR);
    530 		assertEquals("One attribute expected!", 1, attributes.size());
    531
    532 		ASTAttributeValue attr =  attributes.iterator().next();
    533 		assertEquals("Expected to detect proper value for attribute!", "<% String a = \"1\";%>", attr.getImage());
    534     }
    535
    536     /**
    537      * test a no quote attribute value which contains the EL dollar sign $
    538      * within its value
    539      */
    540     @Test
    541     @Ignore// nice test for future development
    542     public void noQuoteAttrWithDollarSignInValue(){
    543     	Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class,
    544     			TEST_NO_QUOTE_ATTR_WITH_DOLLAR);
    545 		assertEquals("One attribute expected!", 2, attributes.size());
    546 		ASTAttributeValue attr =  attributes.iterator().next();
    547 		assertEquals("Expected to detect proper value for attribute!", "${something", attr.getImage());
    548     }
    549
    550     /**
    551      * test a no quote attribute value which contains the EL sharp sign #
    552      * within its value
    553      */
    554     @Test
    555     @Ignore// nice test for future development
    556     public void noQuoteAttrWithSharpSymbolInValue(){
    557     	Set<ASTAttributeValue> attributes = getNodes(ASTAttributeValue.class,
    558     			TEST_NO_QUOTE_ATTR_WITH_HASH);
    559 		assertEquals("One attribute expected!", 1, attributes.size());
    560 		ASTAttributeValue attr =  attributes.iterator().next();
    561 		assertEquals("Expected to detect proper value for attribute!", "#{something", attr.getImage());
    562     }
    563
    564     @Test
    565     public void unclosedTag(){
    566     	Set<ASTElement> elements = getNodes(ASTElement.class, TEST_UNCLOSED_SIMPLE);
    567     	List<ASTElement> sortedElmnts = sortNodesByName(elements);
    568 		assertEquals("2 tags expected",2,elements.size());
    569 		assertEquals("First element should be sorted tag:if",
    570 				"tag:if",sortedElmnts.get(0).getName());
    571 		assertEquals("Second element should be tag:someTag",
    572 				"tag:someTag",sortedElmnts.get(1).getName());
    573
    574 		assertTrue(sortedElmnts.get(0).isEmpty());
    575 		assertTrue(sortedElmnts.get(0).isUnclosed());
    576 		assertFalse(sortedElmnts.get(1).isEmpty());
    577 		assertFalse(sortedElmnts.get(1).isUnclosed());
    578     }
    579
    580     @Test
    581     public void unclosedTagAndNoQuotesForAttribute(){
    582     	Set<ASTElement> elements = getNodes(ASTElement.class, TEST_UNCLOSED_NO_QUOTE_ATTR);
    583     	List<ASTElement> sortedElmnts = sortNodesByName(elements);
    584 		assertEquals("2 tags expected",2,elements.size());
    585 		assertEquals("First element should be sorted tag:if",
    586 				"tag:if",sortedElmnts.get(0).getName());
    587 		assertEquals("Second element should be tag:someTag",
    588 				"tag:someTag",sortedElmnts.get(1).getName());
    589
    590 		assertTrue(sortedElmnts.get(0).isEmpty());
    591 		assertTrue(sortedElmnts.get(0).isUnclosed());
    592 		assertFalse(sortedElmnts.get(1).isEmpty());
    593 		assertFalse(sortedElmnts.get(1).isUnclosed());
    594     }
    595
    596     @Test
    597     public void unclosedTagMultipleLevels(){
    598     	Set<ASTElement> elements = getNodes(ASTElement.class, TEST_UNCLOSED_MULTIPLE_LEVELS);
    599     	List<ASTElement> sortedElmnts = sortNodesByName(elements);
    600 		assertEquals("3 tags expected",3,elements.size());
    601 		assertEquals("First element should be sorted tag:someTag",
    602 				"tag:someTag",sortedElmnts.get(0).getName());
    603 		assertEquals("Second element should be tag:someTag",
    604 				"tag:someTag",sortedElmnts.get(1).getName());
    605 		assertEquals("Third element should be tag:x",
    606 				"tag:x",sortedElmnts.get(2).getName());
    607
    608 		assertFalse(sortedElmnts.get(0).isEmpty());
    609 		assertFalse(sortedElmnts.get(0).isUnclosed());
    610
    611 		assertTrue(sortedElmnts.get(1).isEmpty());
    612 		assertTrue(sortedElmnts.get(1).isUnclosed());
    613
    614 		assertFalse(sortedElmnts.get(2).isEmpty());
    615 		assertFalse(sortedElmnts.get(2).isUnclosed());
    616     }
    617
    618     /**
    619      * <html> <a1> <a2/> <b/> </a1> </html>
    620      */
    621     @Test
    622     public void nestedEmptyTags(){
    623     	Set<ASTElement> elements = getNodes(ASTElement.class, TEST_MULTIPLE_EMPTY_TAGS);
    624     	List<ASTElement> sortedElmnts = sortNodesByName(elements);
    625 		assertEquals("4 tags expected",4,elements.size());
    626 		assertEquals("First element should a1",
    627 				"a1",sortedElmnts.get(0).getName());
    628 		assertEquals("Second element should be a2",
    629 				"a2",sortedElmnts.get(1).getName());
    630 		assertEquals("Third element should be b",
    631 				"b",sortedElmnts.get(2).getName());
    632 		assertEquals("Third element should be html",
    633 				"html",sortedElmnts.get(3).getName());
    634
    635
    636 		//  a1
    637 		assertFalse(sortedElmnts.get(0).isEmpty());
    638 		assertFalse(sortedElmnts.get(0).isUnclosed());
    639
    640 		//  a2
    641 		assertTrue(sortedElmnts.get(1).isEmpty());
    642 		assertFalse(sortedElmnts.get(1).isUnclosed());
    643
    644 		// b
    645 		assertTrue(sortedElmnts.get(2).isEmpty());
    646 		assertFalse(sortedElmnts.get(2).isUnclosed());
    647
    648 		//html
    649 		assertFalse(sortedElmnts.get(3).isEmpty());
    650 		assertFalse(sortedElmnts.get(3).isUnclosed());
    651
    652     }
    653
    654     /**
    655      * <html> <a1> <a2> <a3> </a2> </a1> <b/> <a4/> </html>
    656      */
    657     @Test
    658     public void nestedMultipleTags(){
    659     	Set<ASTElement> elements = getNodes(ASTElement.class, TEST_MULTIPLE_NESTED_TAGS);
    660     	List<ASTElement> sortedElmnts = sortNodesByName(elements);
    661 		assertEquals("4 tags expected",6,elements.size());
    662 		assertEquals("First element should a1",
    663 				"a1",sortedElmnts.get(0).getName());
    664 		assertEquals("Second element should be a2",
    665 				"a2",sortedElmnts.get(1).getName());
    666 		assertEquals("Third element should be a3",
    667 				"a3",sortedElmnts.get(2).getName());
    668 		assertEquals("Forth element should be a4",
    669 				"a4",sortedElmnts.get(3).getName());
    670 		assertEquals("Fifth element should be b",
    671 				"b",sortedElmnts.get(4).getName());
    672 		assertEquals("Sixth element should be html",
    673 				"html",sortedElmnts.get(5).getName());
    674
    675
    676 		//  a1 not empty and closed
    677 		assertFalse(sortedElmnts.get(0).isEmpty());
    678 		assertFalse(sortedElmnts.get(0).isUnclosed());
    679
    680 		//  a2  not empty and closed
    681 		assertFalse(sortedElmnts.get(1).isEmpty());
    682 		assertFalse(sortedElmnts.get(1).isUnclosed());
    683
    684 		//  a3 empty and not closed
    685 		assertTrue(sortedElmnts.get(2).isEmpty());
    686 		assertTrue(sortedElmnts.get(2).isUnclosed());
    687
    688 		//  a4 empty but closed
    689 		assertTrue(sortedElmnts.get(3).isEmpty());
    690 		assertFalse(sortedElmnts.get(3).isUnclosed());
    691
    692 		//  b empty but closed
    693 		assertTrue(sortedElmnts.get(4).isEmpty());
    694 		assertFalse(sortedElmnts.get(4).isUnclosed());
    695
    696 		//html not empty and closed
    697 		assertFalse(sortedElmnts.get(5).isEmpty());
    698 		assertFalse(sortedElmnts.get(5).isUnclosed());
    699
    700     }
    701
    702     /**
    703      * will test <x> <a> <b> <b> </x> </a> </x> .
    704      * Here x is the first tag to be closed thus rendering the next close of a (</a>)
    705      * to be disregarded.
    706      */
    707     @Test
    708     public void unclosedParentTagClosedBeforeChild(){
    709     	Set<ASTElement> elements = getNodes(ASTElement.class,
    710     			TEST_UNCLOSED_END_AFTER_PARENT_CLOSE);
    711     	List<ASTElement> sortedElmnts = sortNodesByName(elements);
    712 		assertEquals("4 tags expected",4,elements.size());
    713 		assertEquals("First element should be 'a'",
    714 				"a",sortedElmnts.get(0).getName());
    715 		assertEquals("Second element should be b",
    716 				"b",sortedElmnts.get(1).getName());
    717 		assertEquals("Third element should be b",
    718 				"b",sortedElmnts.get(2).getName());
    719 		assertEquals("Forth element should be x",
    720 				"x",sortedElmnts.get(3).getName());
    721
    722 		//a
    723 		assertTrue(sortedElmnts.get(0).isEmpty());
    724 		assertTrue(sortedElmnts.get(0).isUnclosed());
    725
    726 		//b
    727 		assertTrue(sortedElmnts.get(1).isEmpty());
    728 		assertTrue(sortedElmnts.get(1).isUnclosed());
    729
    730 		//b
    731 		assertTrue(sortedElmnts.get(2).isEmpty());
    732 		assertTrue(sortedElmnts.get(2).isUnclosed());
    733
    734 		//x
    735 		assertFalse(sortedElmnts.get(3).isEmpty());
    736 		assertFalse(sortedElmnts.get(3).isUnclosed());
    737     }
    738
    739
    740     /**
    741      * <x> <a> <b> <b> </z> </a> </x>
    742      * An unmatched closing of 'z' appears randomly in the document. This
    743      * should be disregarded and structure of children and parents should not be influenced.
    744      * in other words </a> should close the first <a> tag , </x> should close the first
    745      *  <x>, etc.
    746      */
    747     @Test
    748     public void unmatchedTagDoesNotInfluenceStructure(){
    749     	Set<ASTElement> elements = getNodes(ASTElement.class,
    750     			TEST_UNCLOSED_UNMATCHED_CLOSING_TAG);
    751     	List<ASTElement> sortedElmnts = sortNodesByName(elements);
    752 		assertEquals("4 tags expected",4,elements.size());
    753 		assertEquals("First element should be 'a'",
    754 				"a",sortedElmnts.get(0).getName());
    755 		assertEquals("Second element should be b",
    756 				"b",sortedElmnts.get(1).getName());
    757 		assertEquals("Third element should be b",
    758 				"b",sortedElmnts.get(2).getName());
    759 		assertEquals("Forth element should be x",
    760 				"x",sortedElmnts.get(3).getName());
    761
    762 		//a is not empty and closed
    763 		assertFalse(sortedElmnts.get(0).isEmpty());
    764 		assertFalse(sortedElmnts.get(0).isUnclosed());
    765
    766 		//b empty and unclosed
    767 		assertTrue(sortedElmnts.get(1).isEmpty());
    768 		assertTrue(sortedElmnts.get(1).isUnclosed());
    769
    770 		//b empty and unclosed
    771 		assertTrue(sortedElmnts.get(2).isEmpty());
    772 		assertTrue(sortedElmnts.get(2).isUnclosed());
    773
    774 		//x not empty and closed
    775 		assertFalse(sortedElmnts.get(3).isEmpty());
    776 		assertFalse(sortedElmnts.get(3).isUnclosed());
    777     }
    778
    779     /**
    780      * <a> <x> <a> <b> <b> </z> </a> </x>
    781      * An unmatched closing of 'z' appears randomly in the document. This
    782      * should be disregarded and structure of children and parents should not be influenced.
    783      * Also un unclosed <a> tag appears at the start of the document
    784      */
    785     @Test
    786     public void unclosedStartTagWithUnmatchedCloseOfDifferentTag(){
    787     	Set<ASTElement> elements = getNodes(ASTElement.class,
    788     			TEST_UNCLOSED_START_TAG_WITH_UNMATCHED_CLOSE);
    789     	List<ASTElement> sortedElmnts = sortNodesByName(elements);
    790 		assertEquals("5 tags expected",5,elements.size());
    791 		assertEquals("First element should be 'a'",
    792 				"a",sortedElmnts.get(0).getName());
    793 		assertEquals("Second element should be a",
    794 				"a",sortedElmnts.get(1).getName());
    795 		assertEquals("Third element should be b",
    796 				"b",sortedElmnts.get(2).getName());
    797 		assertEquals("Forth element should be b",
    798 				"b",sortedElmnts.get(3).getName());
    799 		assertEquals("Fifth element should be x",
    800 				"x",sortedElmnts.get(4).getName());
    801
    802 		//first a is empty and unclosed
    803 		assertTrue(sortedElmnts.get(0).isEmpty());
    804 		assertTrue(sortedElmnts.get(0).isUnclosed());
    805
    806 		//second a not empty and closed
    807 		assertFalse(sortedElmnts.get(1).isEmpty());
    808 		assertFalse(sortedElmnts.get(1).isUnclosed());
    809
    810 		//b empty and unclosed
    811 		assertTrue(sortedElmnts.get(2).isEmpty());
    812 		assertTrue(sortedElmnts.get(2).isUnclosed());
    813
    814 		//b empty and unclosed
    815 		assertTrue(sortedElmnts.get(3).isEmpty());
    816 		assertTrue(sortedElmnts.get(3).isUnclosed());
    817
    818 		//x not empty and closed
    819 		assertFalse(sortedElmnts.get(4).isEmpty());
    820 		assertFalse(sortedElmnts.get(4).isUnclosed());
    821     }
    822
    823     /**
    824      * {@link #TEST_UNCLOSED_END_OF_DOC}
    825      * <tag:x> <tag:y>
    826      * Tests whether parser breaks on no closed tags at all
    827      */
    828     //This is yet to be improved. If a closing tag does not
    829     // exist no tags will be marked as empty :(
    830     @Ignore
    831     @Test
    832     public void unclosedEndOfDoc(){
    833     	Set<ASTElement> elements = getNodes(ASTElement.class,
    834     			TEST_UNCLOSED_END_OF_DOC);
    835     	List<ASTElement> sortedElmnts = sortNodesByName(elements);
    836 		assertEquals("2 tags expected",2,elements.size());
    837 		assertEquals("First element should be 'tag:x'",
    838 				"tag:x",sortedElmnts.get(0).getName());
    839 		assertEquals("Second element should be tag:y",
    840 				"tag:y",sortedElmnts.get(1).getName());
    841
    842 		//b
    843 		//assertTrue(sortedElmnts.get(0).isEmpty());
    844 		assertTrue(sortedElmnts.get(0).isUnclosed());
    845
    846 		//b
    847 		assertTrue(sortedElmnts.get(1).isEmpty());
    848 		assertTrue(sortedElmnts.get(1).isUnclosed());
    849     }
    850
    851     /**
    852      * will sort the AST element in list in alphabetical order and if tag name
    853      * is the same it will sort against o1.getBeginColumn() +""+ o1.getBeginLine().
    854      * so first criteria is the name, then the second is the column +""+line string.
    855      * @param elements
    856      * @return
    857      */
    858     private List<ASTElement> sortNodesByName(Set<ASTElement> elements){
    859     	List<ASTElement> list = new ArrayList<ASTElement>();
    860     	list.addAll(elements);
    861     	Collections.sort(list, new Comparator<ASTElement>() {
    862     		public int compare(ASTElement o1, ASTElement o2) {
    863     			if (o1.getName() == null)
    864     				return Integer.MIN_VALUE;
    865     			if (o2.getName() == null)
    866     				return Integer.MAX_VALUE;
    867     			if (o1.getName().equals(o2.getName())){
    868     				String o1Value = o1.getBeginColumn() +""+ o1.getBeginLine();
    869     				String o2Value = o2.getBeginColumn() +""+ o2.getBeginLine();
    870     				return o1Value.compareTo(o2Value);
    871     			}
    872     			return o1.getName().compareTo(o2.getName());
    873     		}
    874 		});
    875     	return list;
    876     }
    877
    878     /**
    879      * will sort the AST node by the image name.
    880      * @param elements
    881      * @return
    882      */
    883     private <T extends Node> List<T> sortByImage(Set<T> elements){
    884     	List<T> list = new ArrayList<T>();
    885     	list.addAll(elements);
    886     	Collections.sort(list, new Comparator<Node>() {
    887     		public int compare(Node o1, Node o2) {
    888     			if (o1.getImage() == null)
    889     				return Integer.MIN_VALUE;
    890     			if (o2.getImage() == null)
    891     				return Integer.MAX_VALUE;
    892     			if (o1.getImage().equals(o2.getImage())){
    893     				String o1Value = o1.getBeginColumn() +""+ o1.getBeginLine();
    894     				String o2Value = o2.getBeginColumn() +""+ o2.getBeginLine();
    895     				return o1Value.compareTo(o2Value);
    896     			}
    897     			return o1.getImage().compareTo(o2.getImage());
    898     		}
    899 		});
    900     	return list;
    901     }
    902
    903
    904 	private static final String TEST_SIMPLEST_HTML = "<html/>";
    905
    906 	private static final String TEST_ELEMENT_AND_NAMESPACE = "<h:html MyNsPrefix:MyAttr='MyValue'/>";
    907
    908 	private static final String TEST_CDATA = "<html><![CDATA[ some <cdata> ]] ]> ]]></html>";
    909
    910 	private static final String TEST_DOCTYPE = "<?xml version=\"1.0\" standalone='yes'?>\n"
    911 			+ "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" "
    912 			+ "\"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
    913 			+ "<greeting>Hello, world!</greeting>";
    914
    915 	private static final String TEST_COMMENT = "<html><!-- comment --></html>";
    916
    917 	private static final String TEST_ATTRIBUTE_VALUE_CONTAINING_HASH =
    918 		"<tag:if something=\"#yes#\" foo=\"CREATE\">  <a href=\"#\">foo</a> </tag:if>";
    919
    920 	private static final String TEST_HTML_SCRIPT =
    921 			"<html><head><script>Script!</script></head></html>";
    922
    923 	private static final String TEST_IMPORT_JAVASCRIPT =
    924 			"<html><head><script src=\"filename.js\" type=\"text/javascript\"/></head></html>";
    925
    926 	private static final String TEST_HTML_SCRIPT_WITH_ATTRIBUTE =
    927 			"<html><head><script type=\"text/javascript\">Script!</script></head></html>";
    928
    929 	private static final String TEST_COMPLEX_SCRIPT =
    930 			"<HTML><BODY><!--Java Script-->" +
    931 			"<SCRIPT language='JavaScript' type='text/javascript'>" +
    932 			"<!--function calcDays(){" +
    933 			" date1 = date1.split(\"-\");  date2 = date2.split(\"-\");" +
    934 			" var sDate = new Date(date1[0]+\"/\"+date1[1]+\"/\"+date1[2]);" +
    935 			" var eDate = new Date(date2[0]+\"/\"+date2[1]+\"/\"+date2[2]);" +
    936 			" onload=calcDays;//-->" +
    937 			"</SCRIPT></BODY></HTML>;";
    938
    939 	private static final String TEST_INLINE_STYLE =
    940 			"<html><head><style> div { color:red; } </style></head></html>";
    941
    942 	private static final String TEST_TEXT_IN_TAG=
    943 			"<a> some text </a>";
    944
    945 	private static final String TEST_TAGS_NO_SPACE =
    946 			"<a><b></a>";
    947
    948 	private static final String TEST_TAGS_WITH_DOLLAR =
    949 			"<a> $ <b> $ </a>";
    950
    951 	private static final String TEST_TAGS_WITH_EL_WITHIN =
    952 			"<a>#{expr1}<b>${expr2}</a>";
    953
    954 	private static final String TEST_TAGS_WITH_MIXED_EXPRESSIONS =
    955 			"<a> aaa ${expr} #{expr} <%=expr%> <b> \\${expr} </a>";
    956
    957 	private static final String TEST_TAGS_WITH_EXPRESSION_WITHIN =
    958 			"<a> <%=expr%> <b> <%=expr%> </a>";
    959
    960 	private static final String TEST_TEXT_AFTER_OPEN_AND_CLOSED_TAG =
    961 			"<a> some text <b> some text </a>";
    962
    963 	private static final String TEST_TEXT_WITH_UNOPENED_TAG =
    964 			"<a> some text </b> some text </a>";
    965
    966 	private static final String TEST_MULTIPLE_CLOSING_TAGS =
    967 			"<a> some text </b> </b> </b> some text </a>";
    968
    969 	private static final String TEST_QUOTE_EL =
    970 			"<tag:if something=\"${something}\" > </tag:if>";
    971
    972 	private static final String TEST_QUOTE_EXPRESSION =
    973 			"<tag:if something=\"<%=something%>\" >  </tag:if>";
    974
    975 	private static final String TEST_QUOTE_TAG_IN_ATTR =
    976 			"<tag:if something=\"<bean:write name=\"x\" property=\"z\">\" >  " +
    977 			"<a href=http://someHost:/some_URL >foo</a> </tag:if>";
    978
    979 	private static final String TEST_NO_QUOTE_ATTR =
    980 "<tag:if something=yes| > </tag:if>";
    981
    982 	private static final String TEST_NO_QUOTE_EMPTY_ATTR =
    983 			"<tag:if something= >  <a href=http://someHost:/some_URL >foo</a> </tag:if>";
    984
    985 	private static final String TEST_NO_QUOTE_TAG_IN_ATTR =
    986 			"<tag:if something=<bean:write name=\"x\" property=\"z\"> >  <a href=http://someHost:/some_URL >foo</a> </tag:if>";
    987
    988 	private static final String TEST_NO_QUOTE_CR_LF_ATTR =
    989 			"<tag:if something=\r\n >  <a href=http://someHost:/some_URL >foo</a> </tag:if>";
    990
    991 	private static final String TEST_NO_QUOTE_TAB_ATTR =
    992 			"<tag:if something=\t >   </tag:if>";
    993
    994 	private static final String TEST_NO_QUOTE_ATTR_WITH_EL =
    995 			"<tag:if something=${something} >  <a href=url >foo</a> </tag:if>";
    996
    997 	private static final String TEST_NO_QUOTE_ATTR_WITH_EXPRESSION =
    998 			"<tag:if something=<%=something%> >  </tag:if>";
    999
    1000 	/**
    1001 	 * same as {@link #TEST_NO_QUOTE_ATTR_WITH_EXPRESSION} only expression is not
    1002 	 * properly closed
    1003 	 */
    1004 	private static final String TEST_NO_QUOTE_ATTR_WITH_MALFORMED_EXPR =
    1005 			"<tag:if something=<%=something >  </tag:if>";
    1006
    1007 	private static final String TEST_NO_QUOTE_ATTR_WITH_SCRIPTLET =
    1008 			"<tag:if something=<% String a = \"1\";%>x >  </tag:if>";
    1009
    1010
    1011 	private static final String TEST_NO_QUOTE_ATTR_WITH_DOLLAR =
    1012 			"<tag:if something=${something >  <a href=${ >foo</a> </tag:if>";
    1013
    1014 	private static final String TEST_NO_QUOTE_ATTR_WITH_HASH =
    1015 			"<tag:if something=#{something >  <a href=#{url} >foo</a> </tag:if>";
    1016
    1017 	private static final String TEST_UNCLOSED_SIMPLE =
    1018 			"<tag:someTag> <tag:if someting=\"x\" > </tag:someTag>";
    1019
    1020 	/**
    1021 	 * someTag is closed just once
    1022 	 */
    1023 	private static final String TEST_UNCLOSED_MULTIPLE_LEVELS =
    1024 			"<tag:x> <tag:someTag> <tag:someTag someting=\"x\" > </tag:someTag> </tag:x>";
    1025
    1026 	/**
    1027 	 * nested empty tags
    1028 	 */
    1029 	private static final String TEST_MULTIPLE_EMPTY_TAGS =
    1030 			"<html> <a1> <a2/> <b/> </a1> </html>";
    1031
    1032
    1033 	/**
    1034 	 * multiple nested tags with some tags unclosed
    1035 	 */
    1036 	private static final String TEST_MULTIPLE_NESTED_TAGS =
    1037 			"<html> <a1> <a2> <a3> </a2> </a1> <b/> <a4/> </html>";
    1038
    1039 	/**
    1040 	 * </x> will close before </a>, thus leaving <a> to remain unclosed
    1041 	 */
    1042 	private static final String TEST_UNCLOSED_END_AFTER_PARENT_CLOSE =
    1043 			"<x> <a> <b> <b> </x> </a> aa </x> bb </x>";
    1044
    1045 	/**
    1046 	 * </z> is just a dangling closing tag not matching any parent. The parser should
    1047 	 * disregard it
    1048 	 */
    1049 	private static final String TEST_UNCLOSED_UNMATCHED_CLOSING_TAG =
    1050 			"<x> <a> <b> <b> </z> </a> </x>";
    1051
    1052 	/**
    1053 	 * First <a> tag does not close. The first closing of </a> will match the
    1054 	 * second opening of a. Another rogue </z> is there for testing compliance
    1055 	 */
    1056 	private static final String TEST_UNCLOSED_START_TAG_WITH_UNMATCHED_CLOSE =
    1057 			"<a> <x> <a> <b> <b> </z> </a> </x>";
    1058
    1059 	private static final String TEST_UNCLOSED_END_OF_DOC =
    1060 			"<tag:x> <tag:y>";
    1061
    1062 	private static final String TEST_UNCLOSED_NO_QUOTE_ATTR =
    1063 			"<tag:someTag> <tag:if someting=x > </tag:someTag>";
    1064
    1065
    1066     public static junit.framework.Test suite() {
    1067         return new junit.framework.JUnit4TestAdapter(JspDocStyleTest.class);
    1068     }
    1069 }