001package org.apache.maven.doxia.module.xdoc; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.io.File; 023import java.io.FileFilter; 024import java.io.FileReader; 025import java.io.Reader; 026import java.io.Writer; 027 028import java.util.Iterator; 029import java.util.regex.Pattern; 030 031import org.apache.maven.doxia.parser.AbstractParserTest; 032import org.apache.maven.doxia.parser.ParseException; 033import org.apache.maven.doxia.parser.Parser; 034import org.apache.maven.doxia.sink.Sink; 035import org.apache.maven.doxia.sink.SinkEventAttributeSet; 036import org.apache.maven.doxia.sink.SinkEventElement; 037import org.apache.maven.doxia.sink.SinkEventTestingSink; 038 039import org.codehaus.plexus.util.IOUtil; 040 041/** 042 * @author <a href="mailto:jason@maven.org">Jason van Zyl</a> 043 * @author <a href="mailto:evenisse@codehaus.org">Emmanuel Venisse</a> 044 * @version $Id$ 045 * @since 1.0 046 */ 047public class XdocParserTest 048 extends AbstractParserTest 049{ 050 private XdocParser parser; 051 052 @Override 053 protected void setUp() 054 throws Exception 055 { 056 super.setUp(); 057 058 parser = (XdocParser) lookup( Parser.ROLE, "xdoc" ); 059 060 // AbstractXmlParser.CachedFileEntityResolver downloads DTD/XSD files in ${java.io.tmpdir} 061 // Be sure to delete them 062 String tmpDir = System.getProperty( "java.io.tmpdir" ); 063 064 final Pattern xsdFilePattern = Pattern.compile( "(xdoc\\-.*|xml)\\.xsd" ); 065 066 File[] xsdFiles = new File( tmpDir ).listFiles( new FileFilter() 067 { 068 069 public boolean accept( File pathname ) 070 { 071 return pathname.isFile() && xsdFilePattern.matcher( pathname.getName() ).matches(); 072 } 073 } ); 074 075 for ( File xsdFile : xsdFiles ) 076 { 077 xsdFile.delete(); 078 } 079 080 /* FileUtils 3.0.10 is about 5-8 times slower than File.listFiles() + regexp */ 081// String includes = "xdoc-*.xsd, xml.xsd"; 082// List<File> tmpFiles = FileUtils.getFiles( new File( tmpDir ), includes, null, true ); 083// for ( File tmpFile : tmpFiles ) 084// { 085// tmpFile.delete(); 086// } 087 } 088 089 /** {@inheritDoc} */ 090 protected String outputExtension() 091 { 092 return "xml"; 093 } 094 095 /** {@inheritDoc} */ 096 protected Parser createParser() 097 { 098 return parser; 099 } 100 101 /** @throws Exception */ 102 public void testSnippetMacro() 103 throws Exception 104 { 105 Writer output = null; 106 Reader reader = null; 107 108 try 109 { 110 output = getTestWriter( "macro" ); 111 reader = getTestReader( "macro" ); 112 113 Sink sink = new XdocSink( output ); 114 createParser().parse( reader, sink ); 115 sink.close(); 116 } 117 finally 118 { 119 IOUtil.close( output ); 120 IOUtil.close( reader ); 121 } 122 123 File f = getTestFile( getBasedir(), outputBaseDir() + getOutputDir() + "macro.xml" ); 124 assertTrue( "The file " + f.getAbsolutePath() + " was not created", f.exists() ); 125 126 String content; 127 try 128 { 129 reader = new FileReader( f ); 130 content = IOUtil.toString( reader ); 131 } 132 finally 133 { 134 IOUtil.close( reader ); 135 } 136 137 assertTrue( content.indexOf( "<modelVersion>4.0.0</modelVersion>" ) != -1 ); 138 } 139 140 /** @throws Exception */ 141 public void testTocMacro() 142 throws Exception 143 { 144 Writer output = null; 145 Reader reader = null; 146 147 try 148 { 149 output = getTestWriter( "toc" ); 150 reader = getTestReader( "toc" ); 151 152 Sink sink = new XdocSink( output ); 153 createParser().parse( reader, sink ); 154 sink.close(); 155 } 156 finally 157 { 158 IOUtil.close( output ); 159 IOUtil.close( reader ); 160 } 161 162 File f = getTestFile( getBasedir(), outputBaseDir() + getOutputDir() + "toc.xml" ); 163 assertTrue( "The file " + f.getAbsolutePath() + " was not created", f.exists() ); 164 165 String content; 166 try 167 { 168 reader = new FileReader( f ); 169 content = IOUtil.toString( reader ); 170 } 171 finally 172 { 173 IOUtil.close( reader ); 174 } 175 176 // No section, only subsection 1 and 2 177 assertTrue( content.indexOf( "<a href=\"#Section_11\">Section 11</a>" ) != -1 ); 178 assertTrue( content.indexOf( "<a href=\"#Section_1211\">Section 1211</a>" ) == -1 ); 179 } 180 181 /** @throws Exception */ 182 public void testHeadEventsList() 183 throws Exception 184 { 185 String text = "<document>" 186 + "<properties><title>title</title>" 187 + "<!-- Test comment: DOXIA-312 -->" 188 + "<author email=\"a@b.c\">John Doe</author></properties>" 189 + "<head>" 190 + "<meta name=\"security\" content=\"low\"/>" 191 + "<base href=\"http://maven.apache.org/\"/>" 192 + "</head>" 193 + "<body></body></document>"; 194 195 SinkEventTestingSink sink = new SinkEventTestingSink(); 196 197 parser.parse( text, sink ); 198 199 Iterator<SinkEventElement> it = sink.getEventList().iterator(); 200 201 assertEquals( it, "head", "title", "text", "title_", "comment", "author", "text", "author_" ); 202 203 SinkEventElement unknown = it.next(); 204 assertEquals( "unknown", unknown.getName() ); 205 assertEquals( "meta", unknown.getArgs()[0] ); 206 207 unknown = it.next(); 208 assertEquals( "unknown", unknown.getName() ); 209 assertEquals( "base", unknown.getArgs()[0] ); 210 211 assertEquals( it, "head_", "body", "body_" ); 212 assertFalse( it.hasNext() ); 213 214 // DOXIA-359 215 text = "<document>" 216 + "<properties><title>properties title</title></properties>" 217 + "<head><title>head title</title></head>" 218 + "<body></body></document>"; 219 220 sink.reset(); 221 parser.parse( text, sink ); 222 223 it = sink.getEventList().iterator(); 224 225 assertEquals( it, "head", "title" ); 226 227 SinkEventElement title = it.next(); 228 assertEquals( "text", title.getName() ); 229 assertEquals( "properties title", title.getArgs()[0] ); 230 231 assertEquals( it, "title_", "head_", "body", "body_" ); 232 assertFalse( it.hasNext() ); 233 } 234 235 /** @throws Exception */ 236 public void testDocumentBodyEventsList() 237 throws Exception 238 { 239 String text = "<document><body></body></document>"; 240 241 SinkEventTestingSink sink = new SinkEventTestingSink(); 242 243 parser.parse( text, sink ); 244 245 Iterator<SinkEventElement> it = sink.getEventList().iterator(); 246 247 assertEquals( it, "body", "body_" ); 248 assertFalse( it.hasNext() ); 249 } 250 251 /** @throws Exception */ 252 public void testSectionEventsList() 253 throws Exception 254 { 255 String text = "<section name=\"sec 1\"><subsection name=\"sub 1\"></subsection></section>"; 256 257 SinkEventTestingSink sink = new SinkEventTestingSink(); 258 259 parser.parse( text, sink ); 260 261 Iterator<SinkEventElement> it = sink.getEventList().iterator(); 262 263 assertEquals( it, "section1", "sectionTitle1", "text", "sectionTitle1_", "section2", "sectionTitle2", "text", 264 "sectionTitle2_", "section2_", "section1_" ); 265 assertFalse( it.hasNext() ); 266 } 267 268 /** @throws Exception */ 269 public void testSectionAttributes() 270 throws Exception 271 { 272 // DOXIA-448 273 String text = "<section name=\"section name\" class=\"foo\" id=\"bar\"></section>"; 274 275 SinkEventTestingSink sink = new SinkEventTestingSink(); 276 277 parser.parse( text, sink ); 278 279 Iterator<SinkEventElement> it = sink.getEventList().iterator(); 280 281 assertEquals( it, "anchor", "anchor_" ); 282 283 SinkEventElement next = it.next(); 284 assertEquals( "section1", next.getName() ); 285 SinkEventAttributeSet set = (SinkEventAttributeSet) next.getArgs()[0]; 286 assertEquals( 3, set.getAttributeCount() ); 287 assertTrue( set.containsAttribute( "name", "section name" ) ); 288 assertTrue( set.containsAttribute( "class", "foo" ) ); 289 assertTrue( set.containsAttribute( "id", "bar" ) ); 290 291 next = it.next(); 292 assertEquals( "sectionTitle1", next.getName() ); 293 assertNull( (SinkEventAttributeSet) next.getArgs()[0] ); 294 295 assertEquals( it, "text", "sectionTitle1_", "section1_" ); 296 assertFalse( it.hasNext() ); 297 } 298 299 /** @throws Exception */ 300 public void testNestedSectionsEventsList() 301 throws Exception 302 { 303 // DOXIA-241 304 String text = "<section name=\"section\"><h6>h6</h6><subsection name=\"subsection\"></subsection></section>"; 305 306 SinkEventTestingSink sink = new SinkEventTestingSink(); 307 308 parser.parse( text, sink ); 309 310 Iterator<SinkEventElement> it = sink.getEventList().iterator(); 311 312 assertEquals( it, "section1", "sectionTitle1", "text", "sectionTitle1_", "section2", "section3", "section4", 313 "section5", "sectionTitle5", "text", "sectionTitle5_", "section5_", "section4_", "section3_", 314 "section2_", "section2", "sectionTitle2", "text", "sectionTitle2_", "section2_", "section1_" ); 315 assertFalse( it.hasNext() ); 316 } 317 318 /** @throws Exception */ 319 public void testSourceEventsList() 320 throws Exception 321 { 322 String text = "<source><a href=\"what.html\">what</a></source>"; 323 324 SinkEventTestingSink sink = new SinkEventTestingSink(); 325 326 parser.parse( text, sink ); 327 328 Iterator<SinkEventElement> it = sink.getEventList().iterator(); 329 assertEquals( it, "verbatim", "link", "text", "link_", "verbatim_" ); 330 assertFalse( it.hasNext() ); 331 332 text = "<source><![CDATA[<a href=\"what.html\">what</a>]]></source>"; 333 sink.reset(); 334 parser.parse( text, sink ); 335 336 it = sink.getEventList().iterator(); 337 assertEquals( it, "verbatim", "text", "verbatim_" ); 338 assertFalse( it.hasNext() ); 339 340 text = "<source><![CDATA[<source>what</source>]]></source>"; 341 sink.reset(); 342 parser.parse( text, sink ); 343 344 it = sink.getEventList().iterator(); 345 assertEquals( it, "verbatim", "text", "verbatim_" ); 346 assertFalse( it.hasNext() ); 347 } 348 349 /** @throws Exception */ 350 public void testSourceContainingDTD() 351 throws Exception 352 { 353 String text = "<source><![CDATA[" + 354 "<!DOCTYPE web-app PUBLIC " + 355 "\"-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN\"" + 356 " \"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd\">" + 357 "]]></source>"; 358 359 SinkEventTestingSink sink = new SinkEventTestingSink(); 360 361 parser.parse( text, sink ); 362 363 Iterator<SinkEventElement> it = sink.getEventList().iterator(); 364 assertEquals( it, "verbatim", "text", "verbatim_" ); 365 assertFalse( it.hasNext() ); 366 367 } 368 369 /** @throws Exception */ 370 public void testPreEOL() 371 throws Exception 372 { 373 // test EOLs within <source>: the sink MUST receive a text event for the EOL 374 String text = "<source><a href=\"what.html\">what</a>" + EOL 375 + "<a href=\"what.html\">what</a></source>"; 376 377 SinkEventTestingSink sink = new SinkEventTestingSink(); 378 379 parser.parse( text, sink ); 380 381 Iterator<SinkEventElement> it = sink.getEventList().iterator(); 382 383 assertEquals( it, "verbatim", "link", "text", "link_", "text", "link", "text", "link_", "verbatim_" ); 384 } 385 386 /** 387 * Test section with ids. 388 * 389 * @throws java.lang.Exception if any. 390 */ 391 public void testSectionIdAnchor() 392 throws Exception 393 { 394 String text = "<section name=\"test\" id=\"test-id\">This is a test." 395 + "<subsection name=\"sub-test\" id=\"sub-id\">Sub-section</subsection></section>"; 396 397 SinkEventTestingSink sink = new SinkEventTestingSink(); 398 399 parser.parse( text, sink ); 400 401 Iterator<SinkEventElement> it = sink.getEventList().iterator(); 402 403 assertEquals( it.next(), "anchor", "test-id" ); 404 405 assertEquals( it, "anchor_", "section1", "sectionTitle1", "text", "sectionTitle1_", "text" ); 406 407 assertEquals( it.next(), "anchor", "sub-id" ); 408 409 assertEquals( it, "anchor_", "section2", "sectionTitle2", "text", "sectionTitle2_", "text", "section2_", 410 "section1_" ); 411 assertFalse( it.hasNext() ); 412 } 413 414 /** 415 * Test script block. 416 * 417 * @throws java.lang.Exception if any. 418 */ 419 public void testJavaScript() 420 throws Exception 421 { 422 String text = "<script type=\"text/javascript\"><![CDATA[alert(\"Hello!\");]]></script>"; 423 424 SinkEventTestingSink sink = new SinkEventTestingSink(); 425 426 parser.parse( text, sink ); 427 428 Iterator<SinkEventElement> it = sink.getEventList().iterator(); 429 assertEquals( it, "unknown", "unknown", "unknown" ); 430 assertFalse( it.hasNext() ); 431 } 432 433 /** 434 * Test unknown tags. 435 * 436 * @throws java.lang.Exception if any. 437 */ 438 public void testUnknown() 439 throws Exception 440 { 441 String text = "<applet><param name=\"name\" value=\"value\"/><unknown/></applet>"; 442 443 SinkEventTestingSink sink = new SinkEventTestingSink(); 444 445 parser.parse( text, sink ); 446 447 Iterator<SinkEventElement> it = sink.getEventList().iterator(); 448 assertEquals( it, "unknown", "unknown", "unknown", "unknown", "unknown" ); 449 assertFalse( it.hasNext() ); 450 } 451 452 /** 453 * Test invalid macro tags. 454 */ 455 public void testMacroExceptions() 456 { 457 SinkEventTestingSink sink = new SinkEventTestingSink(); 458 assertParseException( sink, "<macro/>" ); 459 assertParseException( sink, "<macro name=\"\"/>" ); 460 assertParseException( sink, "<macro name=\"name\"><param name=\"\" value=\"value\"/></macro>" ); 461 assertParseException( sink, "<macro name=\"name\"><param name=\"name\" value=\"\"/></macro>" ); 462 assertParseException( sink, "<macro name=\"name\"><param value=\"value\"/></macro>" ); 463 assertParseException( sink, "<macro name=\"name\"><param name=\"name\"/></macro>" ); 464 assertParseException( sink, "<macro name=\"unknown\"></macro>" ); 465 } 466 467 private void assertParseException( Sink sink, String text ) 468 { 469 try 470 { 471 parser.parse( text, sink ); 472 473 fail( "Should not be parseable: '" + text + "'" ); 474 } 475 catch ( ParseException ex ) 476 { 477 assertNotNull( ex ); 478 } 479 } 480 481 /** @throws Exception */ 482 public void testEntities() 483 throws Exception 484 { 485 final String text = "<!DOCTYPE test [<!ENTITY foo \"ř\"><!ENTITY tritPos \"𝟭\">]>" 486 + "<section name=\"&&foo;&tritPos;\"><p>&&foo;&tritPos;</p></section>"; 487 488 SinkEventTestingSink sink = new SinkEventTestingSink(); 489 490 parser.setValidate( false ); 491 parser.parse( text, sink ); 492 493 Iterator<SinkEventElement> it = sink.getEventList().iterator(); 494 495 assertEquals( it, "section1", "sectionTitle1" ); 496 497 assertEquals( it.next(), "text", "&\u0159\uD835\uDFED" ); 498 499 assertEquals( it, "sectionTitle1_", "paragraph" ); 500 501 assertEquals( it.next(), "text", "&" ); 502 503 assertEquals( it.next(), "text", "\u0159" ); 504 505 assertEquals( it.next(), "text", "\uD835\uDFED" ); 506 507 assertEquals( it, "paragraph_", "section1_" ); 508 assertFalse( it.hasNext() ); 509 } 510 511 public void testStyleWithCData() throws Exception 512 { 513 // DOXIA-449 514 final String text = "<style type=\"text/css\">\n" + 515 "<![CDATA[\n" + 516 "h2 {\n" + 517 "font-size: 50px;\n" + 518 "}\n" + 519 "]]>\n" + 520 "</style>"; 521 522 SinkEventTestingSink sink = new SinkEventTestingSink(); 523 524 parser.setValidate( false ); 525 parser.parse( text, sink ); 526 527 Iterator<SinkEventElement> it = sink.getEventList().iterator(); 528 SinkEventElement styleElm = it.next(); 529 assertEquals( "unknown", styleElm.getName() ); 530 assertEquals( "style", styleElm.getArgs()[0] ); 531 SinkEventElement cdataElm = it.next(); 532 assertEquals( "unknown", cdataElm.getName() ); 533 assertEquals( "CDATA", cdataElm.getArgs()[0] ); 534 SinkEventElement styleElm_ = it.next(); 535 assertEquals( "unknown", styleElm_.getName() ); 536 assertEquals( "style", styleElm_.getArgs()[0] ); 537 assertFalse( it.hasNext() ); 538 } 539}